diff --git a/invesalius/data/styles.py b/invesalius/data/styles.py index f8c33d3..569e268 100644 --- a/invesalius/data/styles.py +++ b/invesalius/data/styles.py @@ -18,182 +18,601 @@ #-------------------------------------------------------------------------- import vtk +import wx + +from wx.lib.pubsub import pub as Publisher import constants as const -class ViewerStyle: - def __init__(self): - self.interactor = None - self.style = None - self.render = None - - def SetInteractor(self, interactor): - - # Setting style already defined in VTK - if (self.style is None): - self.style = vtk.vtkInteractorStyle() - #self.style.SetInteractor(interactor) - self.SetStyleConfig() - interactor.SetInteractorStyle(style) - - # Saving data into attributes - self.render = interactor.GetRenderWindow().GetRenderer() - self.interactor = interactor - - # Call events - self.__init_evt() - - def SetStyleConfig(self): - print "calling parent" - pass - - def __init_evt(self): - style = self.style - style.AddObserver("LeftButtonPressEvent", self.OnLeftButtonDown) - style.AddObserver("LeftButtonReleaseEvent", self.OnLeftButtonUp) - style.AddObserver("MiddleButtonPressEvent", self.OnMiddleButtonDown) - style.AddObserver("MiddleButtonReleaseEvent", self.OnMiddleButtonUp) - style.AddObserver("RightButtonPressEvent", self.OnRightButtonDown) - style.AddObserver("RightButtonReleaseEvent", self.OnRightButtonUp) - style.AddObserver("MouseWheelForwardEvent", self.OnScroll) - style.AddObserver("MouseWheelBackwardEvent", self.OnScroll) - - style.AddObserver("MouseMoveEvent",self.OnMouseMove) - - def OnScroll(self, evt, str_evt): - pass - - def OnLeftButtonDown(self, evt, str_evt): - pass - - def OnLeftButtonUp(self, evt, str_evt): - pass - - def OnMiddleButtonDown(self, evt, str_evt): - pass - - def OnMiddleButtonUp(self, evt, str_evt): - pass - - def OnRightButtonDown(self, evt, str_evt): - pass - - def OnRightButtonUp(self, evt, str_evt): - pass - - def OnMouseMove(self, evt, str_evt): - pass - -class ViewerStyleSlice(ViewerStyle): - def __init__(self): - ViewerStyle.__init__(self) - self.orientation = 'AXIAL' - - self.style = vtk.vtkInteractorStyleImage() - self.style.AutoAdjustCameraClippingRangeOn() - - def SetOrientation(self, orientation='AXIAL'): - self.orientation = orientation - - def OnScroll(self, evt, evt_string): - if evt_string == "MouseWheelForwardEvent": - value = 1 - else: # elif evt_string =="MouseWheelBackwardEvent": - value = -1 - ps.Publisher().sendMessage(('Set scroll position', self.orientation), value) - -class ViewerStyleSliceEditor(ViewerStyleSlice): +ORIENTATIONS = { + "AXIAL": const.AXIAL, + "CORONAL": const.CORONAL, + "SAGITAL": const.SAGITAL, + } - def __init__(self): - # FIXME: the idea is not using Slice from here...! - #self.slice_ = slc.Slice() - - self.picker = vtk.vtkCellPicker() # define position where user clicked - - self.mouse_pressed = 0 # define if operation will executed run or not - - self.style = const.OP_ADD # define brush operation .OP_DEL, .OP_THRESH +class BaseImageInteractorStyle(vtk.vtkInteractorStyleImage): + def __init__(self, viewer): + self.right_pressed = False + self.left_pressed = False - - def SetData(self, actor_bounds, imagedata_dimensions): - self.pos_converter = ViewerToImagedataPosConverter(actor_bounds, - imagedata_dimensions, - self.renderer) + self.AddObserver("LeftButtonPressEvent", self.OnPressLeftButton) + self.AddObserver("LeftButtonReleaseEvent", self.OnReleaseLeftButton) - def SetStyleConfig(self): - print "calling son" - - def SetOrientation(self, orient): - pass + self.AddObserver("RightButtonPressEvent",self.OnPressRightButton) + self.AddObserver("RightButtonReleaseEvent", self.OnReleaseRightButton) + + def OnPressLeftButton(self, evt, obj): + self.left_pressed = True + + def OnReleaseLeftButton(self, evt, obj): + self.left_pressed = False + + def OnPressRightButton(self, evt, obj): + self.right_pressed = True + self.viewer.last_position_mouse_move = \ + self.viewer.interactor.GetLastEventPosition() + + def OnReleaseRightButton(self, evt, obj): + self.right_pressed = False + + +class DefaultInteractorStyle(BaseImageInteractorStyle): + """ + Interactor style responsible for Default functionalities: + * Zoom moving mouse with right button pressed; + * Change the slices with the scroll. + """ + def __init__(self, viewer): + BaseImageInteractorStyle.__init__(self, viewer) + + self.viewer = viewer + + # Zoom using right button + self.AddObserver("RightButtonPressEvent",self.OnZoomRightClick) + self.AddObserver("MouseMoveEvent", self.OnZoomRightMove) + + self.AddObserver("MouseWheelForwardEvent",self.OnScrollForward) + self.AddObserver("MouseWheelBackwardEvent", self.OnScrollBackward) + + def OnZoomRightMove(self, evt, obj): + if (self.right_pressed): + evt.Dolly() + evt.OnRightButtonDown() + + def OnZoomRightClick(self, evt, obj): + evt.StartDolly() + + def OnScrollForward(self, evt, obj): + self.viewer.OnScrollForward() + + def OnScrollBackward(self, evt, obj): + self.viewer.OnScrollBackward() + + +class CrossInteractorStyle(DefaultInteractorStyle): + """ + Interactor style responsible for the Cross. + """ + def __init__(self, viewer): + DefaultInteractorStyle.__init__(self, viewer) + + self.viewer = viewer + self.orientation = viewer.orientation + self.slice_actor = viewer.slice_data.actor + self.slice_data = viewer.slice_data + + self.picker = vtk.vtkWorldPointPicker() + + self.AddObserver("MouseMoveEvent", self.OnCrossMove) + self.AddObserver("LeftButtonPressEvent", self.OnCrossMouseClick) + self.AddObserver("LeftButtonReleaseEvent", self.OnReleaseLeftButton) + + def SetUp(self): + self.viewer._set_cross_visibility(1) + + def CleanUp(self): + self.viewer._set_cross_visibility(0) + + def OnCrossMouseClick(self, obj, evt): + iren = obj.GetInteractor() + self.ChangeCrossPosition(iren) + + def OnCrossMove(self, obj, evt): + # The user moved the mouse with left button pressed + if self.left_pressed: + print "OnCrossMove interactor style" + iren = obj.GetInteractor() + self.ChangeCrossPosition(iren) + + def ChangeCrossPosition(self, iren): + mouse_x, mouse_y = iren.GetEventPosition() + ren = iren.GetRenderWindow().GetRenderers().GetFirstRenderer() + self.picker.Pick(mouse_x, mouse_y, 0, ren) + + # Get in what slice data the click occurred + # pick to get click position in the 3d world + coord_cross = self.get_coordinate_cursor() + position = self.slice_actor.GetInput().FindPoint(coord_cross) + # Forcing focal point to be setted in the center of the pixel. + coord_cross = self.slice_actor.GetInput().GetPoint(position) + + coord = self.calcultate_scroll_position(position) + self.ScrollSlice(coord) + + Publisher.sendMessage('Update cross position', coord_cross) + Publisher.sendMessage('Set ball reference position based on bound', + coord_cross) + Publisher.sendMessage('Set camera in volume', coord_cross) + Publisher.sendMessage('Render volume viewer') - def OnLeftButtonDown(self, evt, str_evt): - self.mouse_pressed = 1 - - def OnLeftButtonUp(self, evt, str_evt): - self.mouse_pressed = 0 - - def OnMouseMove(self, evt, str_evt): - pos = self.interactor.GetEventPosition() - wx = pos[0] - wy = pos[1] + iren.Render() + + + def calcultate_scroll_position(self, position): + # Based in the given coord (x, y, z), returns a list with the scroll positions for each + # orientation, being the first position the sagital, second the coronal + # and the last, axial. - self.pick.Pick(wx, wy, 0, self.render) + if self.orientation == 'AXIAL': + image_width = self.slice_actor.GetInput().GetDimensions()[0] + axial = self.slice_data.number + coronal = position / image_width + sagital = position % image_width + + elif self.orientation == 'CORONAL': + image_width = self.slice_actor.GetInput().GetDimensions()[0] + axial = position / image_width + coronal = self.slice_data.number + sagital = position % image_width + + elif self.orientation == 'SAGITAL': + image_width = self.slice_actor.GetInput().GetDimensions()[1] + axial = position / image_width + coronal = position % image_width + sagital = self.slice_data.number + + return sagital, coronal, axial + + def ScrollSlice(self, coord): + if self.orientation == "AXIAL": + Publisher.sendMessage(('Set scroll position', 'SAGITAL'), + coord[0]) + Publisher.sendMessage(('Set scroll position', 'CORONAL'), + coord[1]) + elif self.orientation == "SAGITAL": + Publisher.sendMessage(('Set scroll position', 'AXIAL'), + coord[2]) + Publisher.sendMessage(('Set scroll position', 'CORONAL'), + coord[1]) + elif self.orientation == "CORONAL": + Publisher.sendMessage(('Set scroll position', 'AXIAL'), + coord[2]) + Publisher.sendMessage(('Set scroll position', 'SAGITAL'), + coord[0]) + + def get_coordinate_cursor(self): + # Find position x, y, z = self.picker.GetPickPosition() + bounds = self.viewer.slice_data.actor.GetBounds() + if bounds[0] == bounds[1]: + x = bounds[0] + elif bounds[2] == bounds[3]: + y = bounds[2] + elif bounds[4] == bounds[5]: + z = bounds[4] + return x, y, z + + +class WWWLInteractorStyle(DefaultInteractorStyle): + """ + Interactor style responsible for Window Level & Width functionality. + """ + def __init__(self, viewer): + DefaultInteractorStyle.__init__(self, viewer) + + self.viewer = viewer + + self.last_x = 0 + self.last_y = 0 + + self.acum_achange_window = viewer.slice_.window_width + self.acum_achange_level = viewer.slice_.window_level + + self.AddObserver("MouseMoveEvent", self.OnWindowLevelMove) + self.AddObserver("LeftButtonPressEvent", self.OnWindowLevelClick) + + def SetUp(self): + self.viewer.on_wl = True + self.viewer.wl_text.Show() + + def CleanUp(self): + self.viewer.on_wl = False + self.viewer.wl_text.Hide() + + def OnWindowLevelMove(self, obj, evt): + if (self.left_pressed): + iren = obj.GetInteractor() + mouse_x, mouse_y = iren.GetEventPosition() + self.acum_achange_window += mouse_x - self.last_x + self.acum_achange_level += mouse_y - self.last_y + self.last_x, self.last_y = mouse_x, mouse_y + + Publisher.sendMessage('Bright and contrast adjustment image', + (self.acum_achange_window, self.acum_achange_level)) + + #self.SetWLText(self.acum_achange_level, + # self.acum_achange_window) + + const.WINDOW_LEVEL['Manual'] = (self.acum_achange_window,\ + self.acum_achange_level) + Publisher.sendMessage('Check window and level other') + Publisher.sendMessage('Update window level value',(self.acum_achange_window, + self.acum_achange_level)) + #Necessary update the slice plane in the volume case exists + Publisher.sendMessage('Update slice viewer') + Publisher.sendMessage('Render volume viewer') + + def OnWindowLevelClick(self, obj, evt): + iren = obj.GetInteractor() + self.last_x, self.last_y = iren.GetLastEventPosition() + + self.acum_achange_window = viewer.slice_.window_width + self.acum_achange_level = viewer.slice_.window_level + + +class LinearMeasureInteractorStyle(DefaultInteractorStyle): + """ + Interactor style responsible for insert linear measurements. + """ + def __init__(self, viewer): + DefaultInteractorStyle.__init__(self, viewer) + + self.viewer = viewer + self.orientation = viewer.orientation + self.slice_data = viewer.slice_data + + self.picker = vtk.vtkCellPicker() - if self.mouse_pressed: - #wx, wy = self.From3dToImagePixel(pos, (x, y, z)) - wx, wy = self.pos_converter.GetImagedataCoordinates(x, y, z) - #self.CircleOperation(wx, wy, self.slice_) # TODO! - ps.Publisher().sendMessage('Update edited imagedata', self.image) - ps.Publisher().sendMessage('Update slice viewer', None) - - # update cursor - self.cursor.SetPosition(x, y, z) - self.cursor.Update() - - obj.OnMouseMove() - - self.interactor.Render() - -class ViewerToImagedataPosConverter(): - def __init__(self, actor_bounds, imagedata_dimensions, renderer): - self.actor_bounds = actor_bounds - self.imagedata_dimensions = imagedata_dimensions - self.renderer = renderer + self.AddObserver("LeftButtonPressEvent", self.OnInsertLinearMeasurePoint) + + def OnInsertLinearMeasurePoint(self, obj, evt): + iren = obj.GetInteractor() + x,y = iren.GetEventPosition() + render = iren.FindPokedRenderer(x, y) + slice_number = self.slice_data.number + self.picker.Pick(x, y, 0, render) + x, y, z = self.picker.GetPickPosition() + if self.picker.GetViewProp(): + Publisher.sendMessage("Add measurement point", + ((x, y,z), const.LINEAR, + ORIENTATIONS[self.orientation], + slice_number)) + self.viewer.interactor.Render() + + +class AngularMeasureInteractorStyle(DefaultInteractorStyle): + """ + Interactor style responsible for insert angular measurements. + """ + def __init__(self, viewer): + DefaultInteractorStyle.__init__(self, viewer) + + self.viewer = viewer + self.orientation = viewer.orientation + self.slice_data = viewer.slice_data + + self.picker = vtk.vtkCellPicker() + + self.AddObserver("LeftButtonPressEvent", self.OnInsertAngularMeasurePoint) + + def OnInsertAngularMeasurePoint(self, obj, evt): + iren = obj.GetInteractor() + x,y = iren.GetEventPosition() + render = iren.FindPokedRenderer(x, y) + slice_number = self.slice_data.number + self.picker.Pick(x, y, 0, render) + x, y, z = self.picker.GetPickPosition() + if self.picker.GetViewProp(): + Publisher.sendMessage("Add measurement point", + ((x, y,z), const.ANGULAR, + ORIENTATIONS[self.orientation], + slice_number)) + self.viewer.interactor.Render() + + +class PanMoveInteractorStyle(DefaultInteractorStyle): + """ + Interactor style responsible for translate the camera. + """ + def __init__(self, viewer): + DefaultInteractorStyle.__init__(self, viewer) + + self.viewer = viewer + + self.AddObserver("MouseMoveEvent", self.OnPanMove) + self.viewer.interactor.Bind(wx.EVT_LEFT_DCLICK, self.OnUnspan) + + def OnPanMove(self, obj, evt): + if self.left_pressed: + obj.Pan() + obj.OnRightButtonDown() + + def OnUnspan(self, evt): + iren = self.viewer.interactor + mouse_x, mouse_y = iren.GetLastEventPosition() + ren = iren.FindPokedRenderer(mouse_x, mouse_y) + ren.ResetCamera() + iren.Render() + + +class SpinInteractorStyle(DefaultInteractorStyle): + """ + Interactor style responsible for spin the camera. + """ + def __init__(self, viewer): + DefaultInteractorStyle.__init__(self, viewer) + + self.viewer = viewer + + self.AddObserver("MouseMoveEvent", self.OnSpinMove) + self.viewer.interactor.Bind(wx.EVT_LEFT_DCLICK, self.OnUnspin) + + def OnSpinMove(self, obj, evt): + iren = obj.GetInteractor() + mouse_x, mouse_y = iren.GetLastEventPosition() + ren = iren.FindPokedRenderer(mouse_x, mouse_y) + cam = ren.GetActiveCamera() + if (self.left_pressed): + self.viewer.UpdateTextDirection(cam) + self.spined_image = True + obj.Spin() + obj.OnRightButtonDown() + + def OnUnspin(self, evt): + orig_orien = 1 + iren = self.viewer.interactor + mouse_x, mouse_y = iren.GetLastEventPosition() + ren = iren.FindPokedRenderer(mouse_x, mouse_y) + cam = ren.GetActiveCamera() + cam.SetViewUp(const.SLICE_POSITION[orig_orien][0][self.viewer.orientation]) + self.viewer.ResetTextDirection(cam) + iren.Render() + + +class ZoomInteractorStyle(DefaultInteractorStyle): + """ + Interactor style responsible for zoom with movement of the mouse and the + left mouse button clicked. + """ + def __init__(self, viewer): + DefaultInteractorStyle.__init__(self, viewer) + + self.viewer = viewer + + self.AddObserver("MouseMoveEvent", self.OnZoomMoveLeft) + self.viewer.interactor.Bind(wx.EVT_LEFT_DCLICK, self.OnUnZoom) + + def OnZoomMoveLeft(self, obj, evt): + if self.left_pressed: + obj.Dolly() + obj.OnRightButtonDown() + + def OnUnZoom(self, evt): + mouse_x, mouse_y = self.viewer.interactor.GetLastEventPosition() + ren = self.viewer.interactor.FindPokedRenderer(mouse_x, mouse_y) + #slice_data = self.get_slice_data(ren) + ren.ResetCamera() + ren.ResetCameraClippingRange() + #self.Reposition(slice_data) + self.viewer.interactor.Render() + + +class ZoomSLInteractorStyle(vtk.vtkInteractorStyleRubberBandZoom): + """ + Interactor style responsible for zoom by selecting a region. + """ + def __init__(self, viewer): + self.viewer = viewer + self.viewer.interactor.Bind(wx.EVT_LEFT_DCLICK, self.OnUnZoom) + + def OnUnZoom(self, evt): + mouse_x, mouse_y = self.viewer.interactor.GetLastEventPosition() + ren = self.viewer.interactor.FindPokedRenderer(mouse_x, mouse_y) + #slice_data = self.get_slice_data(ren) + ren.ResetCamera() + ren.ResetCameraClippingRange() + #self.Reposition(slice_data) + self.viewer.interactor.Render() + + +class ChangeSliceInteractorStyle(DefaultInteractorStyle): + """ + Interactor style responsible for change slice moving the mouse. + """ + def __init__(self, viewer): + DefaultInteractorStyle.__init__(self, viewer) + + self.viewer = viewer + + self.AddObserver("MouseMoveEvent", self.OnChangeSliceMove) + self.AddObserver("LeftButtonPressEvent", self.OnChangeSliceClick) + + def OnChangeSliceMove(self, evt, obj): + if self.left_pressed: + min = 0 + max = self.viewer.slice_.GetMaxSliceNumber(self.viewer.orientation) + + position = self.viewer.interactor.GetLastEventPosition() + scroll_position = self.viewer.scroll.GetThumbPosition() + + if (position[1] > self.last_position) and\ + (self.acum_achange_slice > min): + self.acum_achange_slice -= 1 + elif(position[1] < self.last_position) and\ + (self.acum_achange_slice < max): + self.acum_achange_slice += 1 + self.last_position = position[1] + + self.viewer.scroll.SetThumbPosition(self.acum_achange_slice) + self.viewer.OnScrollBar() + + def OnChangeSliceClick(self, evt, obj): + position = self.viewer.interactor.GetLastEventPosition() + self.acum_achange_slice = self.viewer.scroll.GetThumbPosition() + self.last_position = position[1] + + +class EditorInteractorStyle(DefaultInteractorStyle): + def __init__(self, viewer): + DefaultInteractorStyle.__init__(self, viewer) + + self.viewer = viewer + self.orientation = self.viewer.orientation + + self.picker = vtk.vtkWorldPointPicker() + + self.AddObserver("EnterEvent", self.OnEnterInteractor) + self.AddObserver("LeaveEvent", self.OnLeaveInteractor) + + self.AddObserver("LeftButtonPressEvent", self.OnBrushClick) + self.AddObserver("LeftButtonReleaseEvent", self.OnBrushRelease) + self.AddObserver("MouseMoveEvent", self.OnBrushMove) + + def OnEnterInteractor(self, obj, evt): + if (self.viewer.slice_.buffer_slices[self.orientation].mask is None): + return + self.viewer.slice_data.cursor.Show() + self.viewer.interactor.SetCursor(wx.StockCursor(wx.CURSOR_BLANK)) + self.viewer.interactor.Render() - def GetImagedataCoordinates(self, picker_position): - x, y, z = picker_position + def OnLeaveInteractor(self, obj, evt): + self.viewer.slice_data.cursor.Show(0) + self.viewer.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) + self.viewer.interactor.Render() + + def OnBrushClick(self, obj, evt): + if (self.viewer.slice_.buffer_slices[self.orientation].mask is None): + return - c = vtk.vtkCoordinate() - c.SetCoordinateSystemToWorld() - c.SetValue(bounds[::2]) - xi, yi = c.GetComputedViewportValue(self.render) + viewer = self.viewer + iren = viewer.interactor - c.SetValue(bounds[1::2]) - xf, yf = c.GetComputedViewportValue(self.render) + viewer._set_editor_cursor_visibility(1) + + mouse_x, mouse_y = iren.GetEventPosition() + render = iren.FindPokedRenderer(mouse_x, mouse_y) + slice_data = viewer.get_slice_data(render) + + # TODO: Improve! + #for i in self.slice_data_list: + #i.cursor.Show(0) + slice_data.cursor.Show() + + self.picker.Pick(mouse_x, mouse_y, 0, render) - c.SetValue(x, y, z) - wx, wy = c.GetComputedViewportValue(self.render) + coord = self.get_coordinate_cursor() + position = slice_data.actor.GetInput().FindPoint(coord) - wx = wx - xi - wy = wy - yi + if position != -1: + coord = slice_data.actor.GetInput().GetPoint(position) + + slice_data.cursor.SetPosition(coord) + cursor = slice_data.cursor + radius = cursor.radius + + if position < 0: + position = viewer.calculate_matrix_position(coord) + + viewer.slice_.edit_mask_pixel(viewer._brush_cursor_op, cursor.GetPixels(), + position, radius, viewer.orientation) + viewer._flush_buffer = True + + # TODO: To create a new function to reload images to viewer. + viewer.OnScrollBar() + + def OnBrushMove(self, obj, evt): + if (self.viewer.slice_.buffer_slices[self.orientation].mask is None): + return + + viewer = self.viewer + iren = viewer.interactor + + viewer._set_editor_cursor_visibility(1) + + mouse_x, mouse_y = iren.GetEventPosition() + render = iren.FindPokedRenderer(mouse_x, mouse_y) + slice_data = viewer.get_slice_data(render) + + # TODO: Improve! + #for i in self.slice_data_list: + #i.cursor.Show(0) + + self.picker.Pick(mouse_x, mouse_y, 0, render) - xf = xf - xi - yf = yf - yi + #if (self.pick.GetViewProp()): + #self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_BLANK)) + #else: + #self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) + + coord = self.get_coordinate_cursor() + position = viewer.slice_data.actor.GetInput().FindPoint(coord) - wx = (wx * self.imagedata_dimensions[0]) / xf - wy = (wy * self.imagedata_dimensions[1]) / yf + # when position == -1 the cursos is not over the image, so is not + # necessary to set the cursor position to world coordinate center of + # pixel from slice image. + if position != -1: + coord = slice_data.actor.GetInput().GetPoint(position) + slice_data.cursor.SetPosition(coord) + #self.__update_cursor_position(slice_data, coord) - return wx, wy + if (self.left_pressed): + cursor = slice_data.cursor + position = slice_data.actor.GetInput().FindPoint(coord) + radius = cursor.radius -################################################################################ + if position < 0: + position = viewer.calculate_matrix_position(coord) + + viewer.slice_.edit_mask_pixel(viewer._brush_cursor_op, cursor.GetPixels(), + position, radius, self.orientation) + # TODO: To create a new function to reload images to viewer. + viewer.OnScrollBar(update3D=False) -# style = vtk.vtkInteractorStyleImage() -# style.SetInteractor(interactor) + else: + viewer.interactor.Render() -# interactor.SetInteractorStyle(style) -# self.style = style + def OnBrushRelease(self, evt, obj): + if (self.viewer.slice_.buffer_slices[self.orientation].mask is None): + return + + self.viewer.slice_.apply_slice_buffer_to_mask(self.orientation) + self.viewer._flush_buffer = False + + def get_coordinate_cursor(self): + # Find position + x, y, z = self.picker.GetPickPosition() + bounds = self.viewer.slice_data.actor.GetBounds() + if bounds[0] == bounds[1]: + x = bounds[0] + elif bounds[2] == bounds[3]: + y = bounds[2] + elif bounds[4] == bounds[5]: + z = bounds[4] + return x, y, z -#self.interactor.SetCursor(cursors.ZOOM_IN_CURSOR) -################################################################################ +def get_style(style): + STYLES = { + const.STATE_DEFAULT: DefaultInteractorStyle, + const.SLICE_STATE_CROSS: CrossInteractorStyle, + const.STATE_WL: WWWLInteractorStyle, + const.STATE_MEASURE_DISTANCE: LinearMeasureInteractorStyle, + const.STATE_MEASURE_ANGLE: AngularMeasureInteractorStyle, + const.STATE_PAN: PanMoveInteractorStyle, + const.STATE_SPIN: SpinInteractorStyle, + const.STATE_ZOOM: ZoomInteractorStyle, + const.STATE_ZOOM_SL: ZoomSLInteractorStyle, + const.SLICE_STATE_SCROLL: ChangeSliceInteractorStyle, + const.SLICE_STATE_EDITOR: EditorInteractorStyle, + } + return STYLES[style] diff --git a/invesalius/data/viewer_slice.py b/invesalius/data/viewer_slice.py index b7ba8ef..86174c2 100755 --- a/invesalius/data/viewer_slice.py +++ b/invesalius/data/viewer_slice.py @@ -27,6 +27,8 @@ import numpy import vtk from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor +import styles + import wx from wx.lib.pubsub import pub as Publisher @@ -65,6 +67,7 @@ class Viewer(wx.Panel): self.spined_image = False #Use to control to spin self.paned_image = False + self.style = None self.last_position_mouse_move = () self.state = const.STATE_DEFAULT @@ -173,194 +176,21 @@ class Viewer(wx.Panel): interactor.SetInteractorStyle(style) def SetInteractorStyle(self, state): - self.state = state - action = {const.SLICE_STATE_CROSS: - { - "MouseMoveEvent": self.OnCrossMove, - "LeftButtonPressEvent": self.OnCrossMouseClick, - }, - const.SLICE_STATE_EDITOR: - { - "MouseMoveEvent": self.OnBrushMove, - "LeftButtonPressEvent": self.OnBrushClick, - "LeftButtonReleaseEvent": self.OnBrushRelease, - "EnterEvent": self.OnEnterInteractor, - "LeaveEvent": self.OnLeaveInteractor - }, - const.STATE_PAN: - { - "MouseMoveEvent": self.OnPanMove, - "LeftButtonPressEvent": self.OnPanClick, - "LeftButtonReleaseEvent": self.OnVtkRightRelease - }, - const.STATE_SPIN: - { - "MouseMoveEvent": self.OnSpinMove, - "LeftButtonPressEvent": self.OnSpinClick, - "LeftButtonReleaseEvent": self.OnVtkRightRelease - }, - const.STATE_ZOOM: - { - "MouseMoveEvent": self.OnZoomMoveLeft, - "LeftButtonPressEvent": self.OnZoomLeftClick, - "LeftButtonReleaseEvent": self.OnVtkRightRelease - }, - const.SLICE_STATE_SCROLL: - { - "MouseMoveEvent": self.OnChangeSliceMove, - "LeftButtonPressEvent": self.OnChangeSliceClick, - }, - const.STATE_WL: - { - "MouseMoveEvent": self.OnWindowLevelMove, - "LeftButtonPressEvent": self.OnWindowLevelClick, - }, - const.STATE_DEFAULT: - { - }, - const.STATE_MEASURE_DISTANCE: - { - "LeftButtonPressEvent": self.OnInsertLinearMeasurePoint - }, - const.STATE_MEASURE_ANGLE: - { - "LeftButtonPressEvent": self.OnInsertAngularMeasurePoint - }, - } - - - if state == const.SLICE_STATE_CROSS: - self.__set_cross_visibility(1) - Publisher.sendMessage('Activate ball reference') - else: - self.__set_cross_visibility(0) - Publisher.sendMessage('Deactivate ball reference') - - if state == const.STATE_WL: - self.on_wl = True - self.wl_text.Show() - else: - self.on_wl = False - self.wl_text.Hide() - - self.__set_editor_cursor_visibility(0) - - # Bind method according to current mode - if(state == const.STATE_ZOOM_SL): - style = vtk.vtkInteractorStyleRubberBandZoom() + cleanup = getattr(self.style, 'CleanUp', None) + if cleanup: + self.style.CleanUp() - style.AddObserver("RightButtonPressEvent", self.QuitRubberBandZoom) - #style.AddObserver("RightButtonPressEvent", self.EnterRubberBandZoom) - - else: - style = vtk.vtkInteractorStyleImage() - - # Check each event available for each state - for event in action[state]: - # Bind event - style.AddObserver(event, - action[state][event]) - - # Common to all styles - # Mouse Buttons' presses / releases - style.AddObserver("LeftButtonPressEvent", self.OnLeftClick) - style.AddObserver("LeftButtonReleaseEvent", self.OnReleaseLeftButton) - style.AddObserver("RightButtonPressEvent", self.OnRightClick) - style.AddObserver("RightButtonReleaseEvent", self.OnReleaseRightButton) - - # Zoom using right button - style.AddObserver("RightButtonPressEvent",self.OnZoomRightClick) - style.AddObserver("MouseMoveEvent", self.OnZoomMoveRight) - style.AddObserver("RightButtonReleaseEvent", self.OnVtkRightRelease) - - #Scroll change slice - style.AddObserver("MouseWheelForwardEvent",self.OnScrollForward) - style.AddObserver("MouseWheelBackwardEvent", self.OnScrollBackward) - - if ((state == const.STATE_ZOOM) or (state == const.STATE_ZOOM_SL)): - self.interactor.Bind(wx.EVT_LEFT_DCLICK, self.OnUnZoom) - else: - self.interactor.Bind(wx.EVT_LEFT_DCLICK, self.OnUnSpinPan) + style = styles.get_style(state)(self) - # Measures are using vtkPropPicker because they need to get which actor - # was picked. - if state in (const.STATE_MEASURE_DISTANCE, const.STATE_MEASURE_ANGLE): - self.pick = vtk.vtkPropPicker() - self.interactor.SetPicker(self.pick) - else: - self.pick = vtk.vtkWorldPointPicker() - self.interactor.SetPicker(self.pick) + setup = getattr(style, 'SetUp', None) + if setup: + style.SetUp() self.style = style self.interactor.SetInteractorStyle(style) self.interactor.Render() - - def QuitRubberBandZoom(self, evt, obj): - style = vtk.vtkInteractorStyleImage() - self.interactor.SetInteractorStyle(style) - self.style = style - - style.AddObserver("LeftButtonPressEvent", self.EnterRubberBandZoom) - # Zoom using right button - style.AddObserver("RightButtonPressEvent", self.OnRightClick) - style.AddObserver("RightButtonReleaseEvent", self.OnReleaseRightButton) - style.AddObserver("RightButtonPressEvent",self.OnZoomRightClick) - style.AddObserver("MouseMoveEvent", self.OnZoomMoveRight) - style.AddObserver("RightButtonReleaseEvent", self.OnReleaseRightButton) - - def EnterRubberBandZoom(self, evt, obj): - style = vtk.vtkInteractorStyleRubberBandZoom() - self.interactor.SetInteractorStyle(style) - self.style = style - - style.AddObserver("RightButtonPressEvent", self.QuitRubberBandZoom) - - def OnRightClick(self, evt, obj): - self.last_position_mouse_move = \ - self.interactor.GetLastEventPosition() - - self.right_pressed = 1 - - def OnReleaseRightButton(self, evt, obj): - self.right_pressed = 0 - Publisher.sendMessage('Update slice viewer') - - def OnLeftClick(self, evt, obj): - self.left_pressed = 1 - - def OnZoomLeftClick(self, evt, obj): - evt.StartDolly() - - def OnReleaseLeftButton(self, evt, obj): - self.left_pressed = 0 - Publisher.sendMessage('Update slice viewer') - - def OnWindowLevelMove(self, evt, obj): - if (self.left_pressed): - position = self.interactor.GetLastEventPosition() - mouse_x, mouse_y = self.interactor.GetEventPosition() - self.acum_achange_window += mouse_x - self.last_x - self.acum_achange_level += mouse_y - self.last_y - self.last_x, self.last_y = mouse_x, mouse_y - - Publisher.sendMessage('Bright and contrast adjustment image', - (self.acum_achange_window, self.acum_achange_level)) - - #self.SetWLText(self.acum_achange_level, - # self.acum_achange_window) - - const.WINDOW_LEVEL['Manual'] = (self.acum_achange_window,\ - self.acum_achange_level) - Publisher.sendMessage('Check window and level other') - Publisher.sendMessage('Update window level value',(self.acum_achange_window, - self.acum_achange_level)) - #Necessary update the slice plane in the volume case exists - Publisher.sendMessage('Update slice viewer') - Publisher.sendMessage('Render volume viewer') - - def OnWindowLevelClick(self, evt, obj): - self.last_x, self.last_y = self.interactor.GetLastEventPosition() + self.state = state def UpdateWindowLevelValue(self, pubsub_evt): window, level = pubsub_evt.data @@ -368,112 +198,6 @@ class Viewer(wx.Panel): self.SetWLText(window, level) Publisher.sendMessage('Update all slice') - - def OnChangeSliceMove(self, evt, obj): - if (self.left_pressed): - min = 0 - max = self.slice_.GetMaxSliceNumber(self.orientation) - - if (self.left_pressed): - position = self.interactor.GetLastEventPosition() - scroll_position = self.scroll.GetThumbPosition() - - if (position[1] > self.last_position) and\ - (self.acum_achange_slice > min): - self.acum_achange_slice -= 1 - elif(position[1] < self.last_position) and\ - (self.acum_achange_slice < max): - self.acum_achange_slice += 1 - self.last_position = position[1] - - self.scroll.SetThumbPosition(self.acum_achange_slice) - self.OnScrollBar() - - def OnChangeSliceClick(self, evt, obj): - position = list(self.interactor.GetLastEventPosition()) - self.acum_achange_slice = self.scroll.GetThumbPosition() - self.last_position = position[1] - - def OnPanMove(self, evt, obj): - mouse_x, mouse_y = self.interactor.GetLastEventPosition() - ren = self.interactor.FindPokedRenderer(mouse_x, mouse_y) - cam = ren.GetActiveCamera() - - if (self.left_pressed): - evt.Pan() - evt.OnRightButtonDown() - self.paned_image = True - - def OnPanClick(self, evt, obj): - evt.StartPan() - - def OnZoomMoveLeft(self, evt, obj): - if self.left_pressed: - evt.Dolly() - evt.OnRightButtonDown() - - def OnVtkRightRelease(self, evt, obj): - evt.OnRightButtonUp() - - def OnUnZoom(self, evt, obj = None): - mouse_x, mouse_y = self.interactor.GetLastEventPosition() - ren = self.interactor.FindPokedRenderer(mouse_x, mouse_y) - slice_data = self.get_slice_data(ren) - ren.ResetCamera() - ren.ResetCameraClippingRange() - #self.Reposition(slice_data) - self.interactor.Render() - - def OnUnSpinPan(self, evt): - orientation = self.orientation - proj = project.Project() - orig_orien = 1 - mouse_x, mouse_y = self.interactor.GetLastEventPosition() - ren = self.interactor.FindPokedRenderer(mouse_x, mouse_y) - - if((self.state == const.STATE_SPIN) and (self.spined_image)): - self.cam.SetViewUp(const.SLICE_POSITION[orig_orien][0][self.orientation]) - # Values are on ccw order, starting from the top: - if self.orientation == 'AXIAL': - values = [_("A"), _("R"), _("P"), _("L")] - elif self.orientation == 'CORONAL': - values = [_("T"), _("R"), _("B"), _("L")] - else: # 'SAGITAL': - values = [_("T"), _("P"), _("B"), _("A")] - - self.RenderTextDirection(values) - self.interactor.Render() - self.spined_image = False - elif((self.state == const.STATE_PAN) and (self.paned_image)): - ren.ResetCamera() - self.interactor.Render() - self.paned_image = False - - def OnSpinMove(self, evt, obj): - mouse_x, mouse_y = self.interactor.GetLastEventPosition() - ren = self.interactor.FindPokedRenderer(mouse_x, mouse_y) - cam = ren.GetActiveCamera() - if (self.left_pressed): - self.UpdateTextDirection(cam) - self.spined_image = True - evt.Spin() - evt.OnRightButtonDown() - - - def OnSpinClick(self, evt, obj): - evt.StartSpin() - - def OnEnterInteractor(self, evt, obj): - if (self.slice_.buffer_slices[self.orientation].mask is None): - return - self.slice_data.cursor.Show() - self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_BLANK)) - - def OnLeaveInteractor(self, evt, obj): - self.slice_data.cursor.Show(0) - self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) - self.interactor.Render() - def SetWLText(self, window_width, window_level): value = STR_WL%(window_level, window_width) if (self.wl_text): @@ -544,6 +268,17 @@ class Viewer(wx.Panel): self.right_text.SetValue(directions[3]) self.interactor.Render() + def ResetTextDirection(self, cam): + # Values are on ccw order, starting from the top: + if self.orientation == 'AXIAL': + values = [_("A"), _("R"), _("P"), _("L")] + elif self.orientation == 'CORONAL': + values = [_("T"), _("R"), _("B"), _("L")] + else: # 'SAGITAL': + values = [_("T"), _("P"), _("B"), _("A")] + + self.RenderTextDirection(values) + self.interactor.Render() def UpdateTextDirection(self, cam): croll = cam.GetRoll() @@ -716,129 +451,6 @@ class Viewer(wx.Panel): slice_data.SetCursor(cursor) self.interactor.Render() - def OnBrushClick(self, evt, obj): - if (self.slice_.buffer_slices[self.orientation].mask is None): - return - - self.__set_editor_cursor_visibility(1) - - mouse_x, mouse_y = self.interactor.GetEventPosition() - render = self.interactor.FindPokedRenderer(mouse_x, mouse_y) - slice_data = self.get_slice_data(render) - - # TODO: Improve! - #for i in self.slice_data_list: - #i.cursor.Show(0) - self.slice_data.cursor.Show() - - self.pick.Pick(mouse_x, mouse_y, 0, render) - - coord = self.get_coordinate_cursor() - position = self.slice_data.actor.GetInput().FindPoint(coord) - - if position != -1: - coord = self.slice_data.actor.GetInput().GetPoint(position) - - slice_data.cursor.SetPosition(coord) - cursor = self.slice_data.cursor - radius = cursor.radius - - if position < 0: - position = self.calculate_matrix_position(coord) - - self.slice_.edit_mask_pixel(self._brush_cursor_op, cursor.GetPixels(), - position, radius, self.orientation) - self._flush_buffer = True - - # TODO: To create a new function to reload images to viewer. - self.OnScrollBar() - - def OnBrushMove(self, evt, obj): - if (self.slice_.buffer_slices[self.orientation].mask is None): - return - - self.__set_editor_cursor_visibility(1) - - mouse_x, mouse_y = self.interactor.GetEventPosition() - render = self.interactor.FindPokedRenderer(mouse_x, mouse_y) - slice_data = self.get_slice_data(render) - - # TODO: Improve! - #for i in self.slice_data_list: - #i.cursor.Show(0) - - self.pick.Pick(mouse_x, mouse_y, 0, render) - - #if (self.pick.GetViewProp()): - #self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_BLANK)) - #else: - #self.interactor.SetCursor(wx.StockCursor(wx.CURSOR_DEFAULT)) - - coord = self.get_coordinate_cursor() - position = self.slice_data.actor.GetInput().FindPoint(coord) - - # when position == -1 the cursos is not over the image, so is not - # necessary to set the cursor position to world coordinate center of - # pixel from slice image. - if position != -1: - coord = self.slice_data.actor.GetInput().GetPoint(position) - slice_data.cursor.SetPosition(coord) - #self.__update_cursor_position(slice_data, coord) - - if (self.left_pressed): - cursor = self.slice_data.cursor - position = self.slice_data.actor.GetInput().FindPoint(coord) - radius = cursor.radius - - if position < 0: - position = self.calculate_matrix_position(coord) - - self.slice_.edit_mask_pixel(self._brush_cursor_op, cursor.GetPixels(), - position, radius, self.orientation) - # TODO: To create a new function to reload images to viewer. - self.OnScrollBar(update3D=False) - - else: - self.interactor.Render() - - def OnBrushRelease(self, evt, obj): - if (self.slice_.buffer_slices[self.orientation].mask is None): - return - - self.slice_.apply_slice_buffer_to_mask(self.orientation) - self._flush_buffer = False - - def OnCrossMouseClick(self, evt, obj): - self.ChangeCrossPosition() - - def OnCrossMove(self, evt, obj): - # The user moved the mouse with left button pressed - if self.left_pressed: - self.ChangeCrossPosition() - - def ChangeCrossPosition(self): - mouse_x, mouse_y = self.interactor.GetEventPosition() - renderer = self.slice_data.renderer - self.pick.Pick(mouse_x, mouse_y, 0, renderer) - - # Get in what slice data the click occurred - # pick to get click position in the 3d world - coord_cross = self.get_coordinate_cursor() - position = self.slice_data.actor.GetInput().FindPoint(coord_cross) - # Forcing focal point to be setted in the center of the pixel. - coord_cross = self.slice_data.actor.GetInput().GetPoint(position) - - coord = self.calcultate_scroll_position(position) - self.ScrollSlice(coord) - - Publisher.sendMessage('Update cross position', coord_cross) - Publisher.sendMessage('Set ball reference position based on bound', - coord_cross) - Publisher.sendMessage('Set camera in volume', coord_cross) - Publisher.sendMessage('Render volume viewer') - - self.interactor.Render() - def Navigation(self, pubsub_evt): # Get point from base change x, y, z = pubsub_evt.data @@ -868,14 +480,6 @@ class Viewer(wx.Panel): Publisher.sendMessage(('Set scroll position', 'SAGITAL'), coord[0]) - def OnZoomMoveRight(self, evt, obj): - if (self.right_pressed): - evt.Dolly() - evt.OnRightButtonDown() - - def OnZoomRightClick(self, evt, obj): - evt.StartDolly() - def get_slice_data(self, render): #for slice_data in self.slice_data_list: #if slice_data.renderer is render: @@ -993,7 +597,7 @@ class Viewer(wx.Panel): Publisher.subscribe(self.UpdateWindowLevelValue,\ 'Update window level value') - #Publisher.subscribe(self.__set_cross_visibility,\ + #Publisher.subscribe(self._set_cross_visibility,\ # 'Set cross visibility') ### Publisher.subscribe(self.__set_layout, @@ -1225,6 +829,7 @@ class Viewer(wx.Panel): self.interactor.Render() self.EnableText() + self.wl_text.Hide() ## Insert cursor self.SetInteractorStyle(const.STATE_DEFAULT) @@ -1260,10 +865,10 @@ class Viewer(wx.Panel): pos = pubsub_evt.data self.cross.SetFocalPoint(pos) - def __set_cross_visibility(self, visibility): + def _set_cross_visibility(self, visibility): self.cross_actor.SetVisibility(visibility) - def __set_editor_cursor_visibility(self, visibility): + def _set_editor_cursor_visibility(self, visibility): for slice_data in self.slice_data_list: slice_data.cursor.actor.SetVisibility(visibility) @@ -1484,35 +1089,6 @@ class Viewer(wx.Panel): coord[index] = extent_min[index] return coord - def OnInsertLinearMeasurePoint(self, obj, evt): - x,y = self.interactor.GetEventPosition() - render = self.interactor.FindPokedRenderer(x, y) - slice_data = self.get_slice_data(render) - slice_number = slice_data.number - self.pick.Pick(x, y, 0, render) - x, y, z = self.pick.GetPickPosition() - print x, y, z - if self.pick.GetViewProp(): - self.render_to_add = slice_data.renderer - Publisher.sendMessage("Add measurement point", - ((x, y,z), const.LINEAR, ORIENTATIONS[self.orientation], - slice_number)) - self.interactor.Render() - - def OnInsertAngularMeasurePoint(self, obj, evt): - x,y = self.interactor.GetEventPosition() - render = self.interactor.FindPokedRenderer(x, y) - slice_data = self.get_slice_data(render) - slice_number = slice_data.number - self.pick.Pick(x, y, 0, render) - x, y, z = self.pick.GetPickPosition() - if self.pick.GetViewProp(): - self.render_to_add = slice_data.renderer - Publisher.sendMessage("Add measurement point", - ((x, y,z), const.ANGULAR, ORIENTATIONS[self.orientation], - slice_number)) - self.interactor.Render() - def ReloadActualSlice(self, pubsub_evt): pos = self.scroll.GetThumbPosition() self.set_slice_number(pos) -- libgit2 0.21.2