From 1c123311cf31987f045159fac792421054050feb Mon Sep 17 00:00:00 2001 From: Thiago Franco de Moraes Date: Sun, 6 Sep 2020 17:05:23 -0300 Subject: [PATCH] Create Styles 3D (#245) --- invesalius/constants.py | 5 ----- invesalius/control.py | 8 ++++---- invesalius/data/styles_3d.py | 461 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ invesalius/data/viewer_volume.py | 229 ++++++++++++++++++++----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- invesalius/gui/data_notebook.py | 21 +++++++++++---------- 5 files changed, 496 insertions(+), 228 deletions(-) create mode 100644 invesalius/data/styles_3d.py diff --git a/invesalius/constants.py b/invesalius/constants.py index 53e1ac2..fd0d20b 100644 --- a/invesalius/constants.py +++ b/invesalius/constants.py @@ -582,11 +582,6 @@ SLICE_STYLES.append(STATE_MEASURE_DENSITY) SLICE_STYLES.append(STATE_MEASURE_DENSITY_ELLIPSE) SLICE_STYLES.append(STATE_MEASURE_DENSITY_POLYGON) -VOLUME_STYLES = TOOL_STATES + [VOLUME_STATE_SEED, STATE_MEASURE_DISTANCE, - STATE_MEASURE_ANGLE] -VOLUME_STYLES.append(STATE_DEFAULT) - - STYLE_LEVEL = {SLICE_STATE_EDITOR: 1, SLICE_STATE_WATERSHED: 1, SLICE_STATE_MASK_FFILL: 2, diff --git a/invesalius/control.py b/invesalius/control.py index 6a63e8b..001cb5b 100644 --- a/invesalius/control.py +++ b/invesalius/control.py @@ -174,7 +174,7 @@ class Controller(): #Publisher.sendMessage("Enable state project", state=False) Publisher.sendMessage('Set project name') Publisher.sendMessage("Stop Config Recording") - Publisher.sendMessage("Set slice interaction style", style=const.STATE_DEFAULT) + Publisher.sendMessage("Enable style", style=const.STATE_DEFAULT) # Import TIFF, BMP, JPEG or PNG dirpath = dialog.ShowImportBitmapDirDialog(self.frame) @@ -198,7 +198,7 @@ class Controller(): #Publisher.sendMessage("Enable state project", state=False) Publisher.sendMessage('Set project name') Publisher.sendMessage("Stop Config Recording") - Publisher.sendMessage("Set slice interaction style", style=const.STATE_DEFAULT) + Publisher.sendMessage("Enable style", style=const.STATE_DEFAULT) # Import project dirpath = dialog.ShowImportDirDialog(self.frame) if dirpath and not os.listdir(dirpath): @@ -219,7 +219,7 @@ class Controller(): # Publisher.sendMessage("Enable state project", state=False) Publisher.sendMessage('Set project name') Publisher.sendMessage("Stop Config Recording") - Publisher.sendMessage("Set slice interaction style", style=const.STATE_DEFAULT) + Publisher.sendMessage("Enable style", style=const.STATE_DEFAULT) # Warning for limited support to Analyze format if id_type == const.ID_ANALYZE_IMPORT: @@ -373,7 +373,7 @@ class Controller(): Publisher.sendMessage('End busy cursor') def CloseProject(self): - Publisher.sendMessage('Set slice interaction style', style=const.STATE_DEFAULT) + Publisher.sendMessage('Enable style', style=const.STATE_DEFAULT) Publisher.sendMessage('Hide content panel') Publisher.sendMessage('Close project data') diff --git a/invesalius/data/styles_3d.py b/invesalius/data/styles_3d.py new file mode 100644 index 0000000..87dad9f --- /dev/null +++ b/invesalius/data/styles_3d.py @@ -0,0 +1,461 @@ +# -------------------------------------------------------------------------- +# Software: InVesalius - Software de Reconstrucao 3D de Imagens Medicas +# Copyright: (C) 2001 Centro de Pesquisas Renato Archer +# Homepage: http://www.softwarepublico.gov.br +# Contact: invesalius@cti.gov.br +# License: GNU - GPL 2 (LICENSE.txt/LICENCA.txt) +# -------------------------------------------------------------------------- +# Este programa e software livre; voce pode redistribui-lo e/ou +# modifica-lo sob os termos da Licenca Publica Geral GNU, conforme +# publicada pela Free Software Foundation; de acordo com a versao 2 +# da Licenca. +# +# Este programa eh distribuido na expectativa de ser util, mas SEM +# QUALQUER GARANTIA; sem mesmo a garantia implicita de +# COMERCIALIZACAO ou de ADEQUACAO A QUALQUER PROPOSITO EM +# PARTICULAR. Consulte a Licenca Publica Geral GNU para obter mais +# detalhes. +# -------------------------------------------------------------------------- + +import vtk +import wx + +import invesalius.constants as const +import invesalius.project as prj + +from pubsub import pub as Publisher + + +PROP_MEASURE = 0.8 + + +class Base3DInteractorStyle(vtk.vtkInteractorStyleTrackballCamera): + def __init__(self, viewer): + self.right_pressed = False + self.left_pressed = False + self.middle_pressed = False + + self.AddObserver("LeftButtonPressEvent", self.OnPressLeftButton) + self.AddObserver("LeftButtonReleaseEvent", self.OnReleaseLeftButton) + + self.AddObserver("RightButtonPressEvent", self.OnPressRightButton) + self.AddObserver("RightButtonReleaseEvent", self.OnReleaseRightButton) + + self.AddObserver("MiddleButtonPressEvent", self.OnMiddleButtonPressEvent) + self.AddObserver("MiddleButtonReleaseEvent", self.OnMiddleButtonReleaseEvent) + + 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 + + def OnReleaseRightButton(self, evt, obj): + self.right_pressed = False + + def OnMiddleButtonPressEvent(self, evt, obj): + self.middle_pressed = True + + def OnMiddleButtonReleaseEvent(self, evt, obj): + self.middle_pressed = False + + +class DefaultInteractorStyle(Base3DInteractorStyle): + """ + Interactor style responsible for Default functionalities: + * Zoom moving mouse with right button pressed; + * Change the slices with the scroll. + """ + def __init__(self, viewer): + super().__init__(viewer) + self.state_code = const.STATE_DEFAULT + + self.viewer = viewer + + # Zoom using right button + self.AddObserver("LeftButtonPressEvent",self.OnRotateLeftClick) + self.AddObserver("LeftButtonReleaseEvent",self.OnRotateLeftRelease) + + self.AddObserver("RightButtonPressEvent",self.OnZoomRightClick) + self.AddObserver("RightButtonReleaseEvent",self.OnZoomRightRelease) + + self.AddObserver("MouseMoveEvent", self.OnMouseMove) + + self.AddObserver("MouseWheelForwardEvent",self.OnScrollForward) + self.AddObserver("MouseWheelBackwardEvent", self.OnScrollBackward) + self.AddObserver("EnterEvent", self.OnFocus) + + def OnFocus(self, evt, obj): + self.viewer.SetFocus() + + def OnMouseMove(self, evt, obj): + if self.left_pressed: + evt.Rotate() + evt.OnLeftButtonDown() + + elif self.right_pressed: + evt.Dolly() + evt.OnRightButtonDown() + + elif self.middle_pressed: + evt.Pan() + evt.OnMiddleButtonDown() + + def OnRotateLeftClick(self, evt, obj): + evt.StartRotate() + + def OnRotateLeftRelease(self, evt, obj): + evt.OnLeftButtonUp() + evt.EndRotate() + + def OnZoomRightClick(self, evt, obj): + evt.StartDolly() + + def OnZoomRightRelease(self, evt, obj): + evt.OnRightButtonUp() + evt.EndDolly() + + def OnScrollForward(self, evt, obj): + self.OnMouseWheelForward() + + def OnScrollBackward(self, evt, obj): + self.OnMouseWheelBackward() + + +class ZoomInteractorStyle(DefaultInteractorStyle): + """ + Interactor style responsible for zoom with movement of the mouse and the + left mouse button clicked. + """ + def __init__(self, viewer): + super().__init__(viewer) + + self.state_code = const.STATE_ZOOM + + self.viewer = viewer + + self.RemoveObservers("LeftButtonPressEvent") + self.RemoveObservers("LeftButtonReleaseEvent") + + self.AddObserver("LeftButtonPressEvent", self.OnPressLeftButton) + self.AddObserver("LeftButtonReleaseEvent", self.OnReleaseLeftButton) + + self.viewer.interactor.Bind(wx.EVT_LEFT_DCLICK, self.OnUnZoom) + + def SetUp(self): + Publisher.sendMessage('Toggle toolbar item', + _id=self.state_code, value=True) + + def CleanUp(self): + self.viewer.interactor.Unbind(wx.EVT_LEFT_DCLICK) + Publisher.sendMessage('Toggle toolbar item', + _id=self.state_code, value=False) + + def OnPressLeftButton(self, evt, obj): + self.right_pressed = True + + def OnReleaseLeftButton(self, obj, evt): + self.right_pressed = False + + def OnUnZoom(self, evt): + ren = self.viewer.ren + ren.ResetCamera() + ren.ResetCameraClippingRange() + 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) + + self.state_code = const.STATE_ZOOM_SL + + def SetUp(self): + Publisher.sendMessage('Toggle toolbar item', + _id=self.state_code, value=True) + + def CleanUp(self): + self.viewer.interactor.Unbind(wx.EVT_LEFT_DCLICK) + Publisher.sendMessage('Toggle toolbar item', + _id=self.state_code, value=False) + + def OnUnZoom(self, evt): + ren = self.viewer.ren + ren.ResetCamera() + ren.ResetCameraClippingRange() + self.viewer.interactor.Render() + + +class PanMoveInteractorStyle(DefaultInteractorStyle): + """ + Interactor style responsible for translate the camera. + """ + def __init__(self, viewer): + super().__init__(viewer) + + self.state_code = const.STATE_PAN + + self.viewer = viewer + + self.panning = False + + self.AddObserver("MouseMoveEvent", self.OnPanMove) + self.viewer.interactor.Bind(wx.EVT_LEFT_DCLICK, self.OnUnspan) + + def SetUp(self): + Publisher.sendMessage('Toggle toolbar item', + _id=self.state_code, value=True) + + def CleanUp(self): + self.viewer.interactor.Unbind(wx.EVT_LEFT_DCLICK) + Publisher.sendMessage('Toggle toolbar item', + _id=self.state_code, value=False) + + def OnPressLeftButton(self, evt, obj): + self.panning = True + + def OnReleaseLeftButton(self, evt, obj): + self.panning = False + + def OnPanMove(self, obj, evt): + if self.panning: + obj.Pan() + obj.OnRightButtonDown() + + def OnUnspan(self, evt): + self.viewer.ren.ResetCamera() + self.viewer.interactor.Render() + + +class SpinInteractorStyle(DefaultInteractorStyle): + """ + Interactor style responsible for spin the camera. + """ + def __init__(self, viewer): + DefaultInteractorStyle.__init__(self, viewer) + self.state_code = const.STATE_SPIN + self.viewer = viewer + self.spinning = False + self.AddObserver("MouseMoveEvent", self.OnSpinMove) + + def SetUp(self): + Publisher.sendMessage('Toggle toolbar item', _id=self.state_code, value=True) + + def CleanUp(self): + Publisher.sendMessage('Toggle toolbar item', _id=self.state_code, value=False) + + def OnPressLeftButton(self, evt, obj): + self.spinning = True + + def OnReleaseLeftButton(self, evt, obj): + self.spinning = False + + def OnSpinMove(self, evt, obj): + if self.spinning: + evt.Spin() + evt.OnRightButtonDown() + + +class WWWLInteractorStyle(DefaultInteractorStyle): + """ + Interactor style responsible for Window Level & Width functionality. + """ + def __init__(self, viewer): + super().__init__(viewer) + self.state_code = const.STATE_WL + + self.viewer = viewer + + self.last_x = 0 + self.last_y = 0 + + self.changing_wwwl = False + + self.RemoveObservers("LeftButtonPressEvent") + self.RemoveObservers("LeftButtonReleaseEvent") + + self.AddObserver("MouseMoveEvent", self.OnWindowLevelMove) + self.AddObserver("LeftButtonPressEvent", self.OnWindowLevelClick) + self.AddObserver("LeftButtonReleaseEvent", self.OnWindowLevelRelease) + + def SetUp(self): + Publisher.sendMessage('Toggle toolbar item', _id=self.state_code, value=True) + self.viewer.on_wl = True + if self.viewer.raycasting_volume: + self.viewer.text.Show() + self.viewer.interactor.Render() + + def CleanUp(self): + Publisher.sendMessage('Toggle toolbar item', _id=self.state_code, value=False) + self.viewer.on_wl = True + self.viewer.text.Hide() + self.viewer.interactor.Render() + + def OnWindowLevelMove(self, obj, evt): + if self.changing_wwwl: + iren = obj.GetInteractor() + mouse_x, mouse_y = iren.GetEventPosition() + diff_x = mouse_x - self.last_x + diff_y = mouse_y - self.last_y + self.last_x, self.last_y = mouse_x, mouse_y + Publisher.sendMessage('Set raycasting relative window and level', + diff_wl=diff_x, diff_ww=diff_y) + Publisher.sendMessage('Refresh raycasting widget points') + Publisher.sendMessage('Render volume viewer') + + def OnWindowLevelClick(self, obj, evt): + iren = obj.GetInteractor() + self.last_x, self.last_y = iren.GetLastEventPosition() + self.changing_wwwl = True + + def OnWindowLevelRelease(self, obj, evt): + self.changing_wwwl = False + + +class LinearMeasureInteractorStyle(DefaultInteractorStyle): + """ + Interactor style responsible for insert linear measurements. + """ + def __init__(self, viewer): + super().__init__(viewer) + self.viewer = viewer + self.state_code = const.STATE_MEASURE_DISTANCE + self.measure_picker = vtk.vtkPropPicker() + + proj = prj.Project() + self._radius = min(proj.spacing) * PROP_MEASURE + + self.RemoveObservers("LeftButtonPressEvent") + self.AddObserver("LeftButtonPressEvent", self.OnInsertLinearMeasurePoint) + + def SetUp(self): + Publisher.sendMessage('Toggle toolbar item', + _id=self.state_code, value=True) + + def CleanUp(self): + Publisher.sendMessage('Toggle toolbar item', + _id=self.state_code, value=False) + Publisher.sendMessage("Remove incomplete measurements") + + def OnInsertLinearMeasurePoint(self, obj, evt): + x,y = self.viewer.interactor.GetEventPosition() + self.measure_picker.Pick(x, y, 0, self.viewer.ren) + x, y, z = self.measure_picker.GetPickPosition() + if self.measure_picker.GetActor(): + self.left_pressed = False + Publisher.sendMessage("Add measurement point", + position=(x, y,z), + type=const.LINEAR, + location=const.SURFACE, + radius=self._radius) + self.viewer.interactor.Render() + else: + self.left_pressed = True + + +class AngularMeasureInteractorStyle(DefaultInteractorStyle): + """ + Interactor style responsible for insert linear measurements. + """ + def __init__(self, viewer): + super().__init__(viewer) + self.viewer = viewer + self.state_code = const.STATE_MEASURE_DISTANCE + self.measure_picker = vtk.vtkPropPicker() + + proj = prj.Project() + self._radius = min(proj.spacing) * PROP_MEASURE + + self.RemoveObservers("LeftButtonPressEvent") + self.AddObserver("LeftButtonPressEvent", self.OnInsertAngularMeasurePoint) + + def SetUp(self): + Publisher.sendMessage('Toggle toolbar item', _id=self.state_code, value=True) + + def CleanUp(self): + Publisher.sendMessage('Toggle toolbar item', _id=self.state_code, value=False) + Publisher.sendMessage("Remove incomplete measurements") + + def OnInsertAngularMeasurePoint(self, obj, evt): + x,y = self.viewer.interactor.GetEventPosition() + self.measure_picker.Pick(x, y, 0, self.viewer.ren) + x, y, z = self.measure_picker.GetPickPosition() + if self.measure_picker.GetActor(): + self.left_pressed = False + Publisher.sendMessage("Add measurement point", + position=(x, y,z), + type=const.ANGULAR, + location=const.SURFACE, + radius=self._radius) + self.viewer.interactor.Render() + else: + self.left_pressed = True + + +class SeedInteractorStyle(DefaultInteractorStyle): + """ + Interactor style responsible for select sub surfaces. + """ + def __init__(self, viewer): + super().__init__(viewer) + self.viewer = viewer + self.picker = vtk.vtkPointPicker() + + self.RemoveObservers("LeftButtonPressEvent") + self.AddObserver("LeftButtonPressEvent", self.OnInsertSeed) + + def OnInsertSeed(self, obj, evt): + x,y = self.viewer.interactor.GetEventPosition() + self.picker.Pick(x, y, 0, self.viewer.ren) + point_id = self.picker.GetPointId() + if point_id > -1: + self.viewer.seed_points.append(point_id) + self.viewer.interactor.Render() + else: + self.left_pressed = True + + +class Styles: + styles = { + const.STATE_DEFAULT: DefaultInteractorStyle, + const.STATE_ZOOM: ZoomInteractorStyle, + const.STATE_ZOOM_SL: ZoomSLInteractorStyle, + const.STATE_PAN: PanMoveInteractorStyle, + const.STATE_SPIN: SpinInteractorStyle, + const.STATE_WL: WWWLInteractorStyle, + const.STATE_MEASURE_DISTANCE: LinearMeasureInteractorStyle, + const.STATE_MEASURE_ANGLE: AngularMeasureInteractorStyle, + const.VOLUME_STATE_SEED: SeedInteractorStyle, + } + + @classmethod + def add_style(cls, style_cls, level=1): + if style_cls in cls.styles.values(): + for style_id in cls.styles: + if cls.styles[style_id] == style_cls: + const.STYLE_LEVEL[style_id] = level + return style_id + + new_style_id = max(cls.styles) + 1 + cls.styles[new_style_id] = style_cls + const.STYLE_LEVEL[new_style_id] = level + return new_style_id + + @classmethod + def remove_style(cls, style_id): + del cls.styles[style_id] + + @classmethod + def get_style(cls, style): + return cls.styles[style] + + @classmethod + def has_style(cls, style): + return style in cls.styles diff --git a/invesalius/data/viewer_volume.py b/invesalius/data/viewer_volume.py index be1c356..b21454f 100644 --- a/invesalius/data/viewer_volume.py +++ b/invesalius/data/viewer_volume.py @@ -38,6 +38,7 @@ from imageio import imsave import invesalius.constants as const import invesalius.data.bases as bases import invesalius.data.slice_ as sl +import invesalius.data.styles_3d as styles import invesalius.data.transformations as tr import invesalius.data.vtk_utils as vtku import invesalius.project as prj @@ -46,6 +47,7 @@ import invesalius.utils as utils from invesalius import inv_paths + if sys.platform == 'win32': try: import win32api @@ -70,14 +72,14 @@ class Viewer(wx.Panel): self.staticballs = [] - style = vtk.vtkInteractorStyleTrackballCamera() - self.style = style + self.style = None interactor = wxVTKRenderWindowInteractor(self, -1, size = self.GetSize()) - interactor.SetInteractorStyle(style) self.interactor = interactor self.interactor.SetRenderWhenDisabled(True) + self.enable_style(const.STATE_DEFAULT) + sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(interactor, 1, wx.EXPAND) self.sizer = sizer @@ -149,8 +151,6 @@ class Viewer(wx.Panel): #self.measure_picker.SetTolerance(0.005) self.measures = [] - self._last_state = 0 - self.repositioned_axial_plan = 0 self.repositioned_sagital_plan = 0 self.repositioned_coronal_plan = 0 @@ -255,7 +255,7 @@ class Viewer(wx.Panel): # Publisher.subscribe(self.SetVolumeCamera, 'Set camera in volume') Publisher.subscribe(self.SetVolumeCameraState, 'Update volume camera state') - Publisher.subscribe(self.OnEnableStyle, 'Enable style') + Publisher.subscribe(self.enable_style, 'Enable style') Publisher.subscribe(self.OnDisableStyle, 'Disable style') Publisher.subscribe(self.OnHideText, @@ -506,7 +506,6 @@ class Viewer(wx.Panel): self.interaction_style.Reset() self.SetInteractorStyle(const.STATE_DEFAULT) - self._last_state = const.STATE_DEFAULT def OnHideText(self): self.text.Hide() @@ -1441,147 +1440,26 @@ class Viewer(wx.Panel): imsave('/tmp/polygon.png', arr) def SetInteractorStyle(self, state): - action = { - const.STATE_PAN: - { - "MouseMoveEvent": self.OnPanMove, - "LeftButtonPressEvent": self.OnPanClick, - "LeftButtonReleaseEvent": self.OnReleasePanClick - }, - const.STATE_ZOOM: - { - "MouseMoveEvent": self.OnZoomMove, - "LeftButtonPressEvent": self.OnZoomClick, - "LeftButtonReleaseEvent": self.OnReleaseZoomClick, - }, - const.STATE_SPIN: - { - "MouseMoveEvent": self.OnSpinMove, - "LeftButtonPressEvent": self.OnSpinClick, - "LeftButtonReleaseEvent": self.OnReleaseSpinClick, - }, - const.STATE_WL: - { - "MouseMoveEvent": self.OnWindowLevelMove, - "LeftButtonPressEvent": self.OnWindowLevelClick, - "LeftButtonReleaseEvent":self.OnWindowLevelRelease - }, - const.STATE_DEFAULT: - { - }, - const.VOLUME_STATE_SEED: - { - "LeftButtonPressEvent": self.OnInsertSeed - }, - const.STATE_MEASURE_DISTANCE: - { - "LeftButtonPressEvent": self.OnInsertLinearMeasurePoint - }, - const.STATE_MEASURE_ANGLE: - { - "LeftButtonPressEvent": self.OnInsertAngularMeasurePoint - } - } - - if self._last_state in (const.STATE_MEASURE_DISTANCE, - const.STATE_MEASURE_ANGLE): - if self.measures and not self.measures[-1].text_actor: - del self.measures[-1] - - if state == const.STATE_WL: - self.on_wl = True - if self.raycasting_volume: - self.text.Show() - self.interactor.Render() - else: - self.on_wl = False - self.text.Hide() - self.interactor.Render() + cleanup = getattr(self.style, 'CleanUp', None) + if cleanup: + self.style.CleanUp() - if state in (const.STATE_MEASURE_DISTANCE, - const.STATE_MEASURE_ANGLE): - self.interactor.SetPicker(self.measure_picker) + del self.style - if (state == const.STATE_ZOOM_SL): - style = vtk.vtkInteractorStyleRubberBandZoom() - self.interactor.SetInteractorStyle(style) - self.style = style - else: - style = vtk.vtkInteractorStyleTrackballCamera() - self.interactor.SetInteractorStyle(style) - self.style = style - - # Check each event available for each mode - for event in action.get(state, []): - # Bind event - style.AddObserver(event,action[state][event]) - - self._last_state = state - - def OnSpinMove(self, evt, obj): - if (self.mouse_pressed): - evt.Spin() - evt.OnRightButtonDown() - - def OnSpinClick(self, evt, obj): - self.mouse_pressed = 1 - evt.StartSpin() + style = styles.Styles.get_style(state)(self) - def OnReleaseSpinClick(self,evt,obj): - self.mouse_pressed = 0 - evt.EndSpin() - - def OnZoomMove(self, evt, obj): - if (self.mouse_pressed): - evt.Dolly() - evt.OnRightButtonDown() - - def OnZoomClick(self, evt, obj): - self.mouse_pressed = 1 - evt.StartDolly() - - def OnReleaseZoomClick(self,evt,obj): - self.mouse_pressed = 0 - evt.EndDolly() + setup = getattr(style, 'SetUp', None) + if setup: + style.SetUp() - def OnPanMove(self, evt, obj): - if (self.mouse_pressed): - evt.Pan() - evt.OnRightButtonDown() - - def OnPanClick(self, evt, obj): - self.mouse_pressed = 1 - evt.StartPan() - - def OnReleasePanClick(self,evt,obj): - self.mouse_pressed = 0 - evt.EndPan() - - def OnWindowLevelMove(self, obj, evt): - if self.onclick and self.raycasting_volume: - mouse_x, mouse_y = self.interactor.GetEventPosition() - diff_x = mouse_x - self.last_x - diff_y = mouse_y - self.last_y - self.last_x, self.last_y = mouse_x, mouse_y - Publisher.sendMessage('Set raycasting relative window and level', - diff_wl=diff_x, diff_ww=diff_y) - Publisher.sendMessage('Refresh raycasting widget points') - self.interactor.Render() - - def OnWindowLevelClick(self, obj, evt): - if const.RAYCASTING_WWWL_BLUR: - self.style.StartZoom() - self.onclick = True - mouse_x, mouse_y = self.interactor.GetEventPosition() - self.last_x, self.last_y = mouse_x, mouse_y + self.style = style + self.interactor.SetInteractorStyle(style) + self.interactor.Render() - def OnWindowLevelRelease(self, obj, evt): - self.onclick = False - if const.RAYCASTING_WWWL_BLUR: - self.style.EndZoom() + self.state = state - def OnEnableStyle(self, style): - if (style in const.VOLUME_STYLES): + def enable_style(self, style): + if styles.Styles.has_style(style): new_state = self.interaction_style.AddState(style) self.SetInteractorStyle(new_state) else: @@ -1849,73 +1727,6 @@ class Viewer(wx.Panel): def AppendActor(self, actor): self.ren.AddActor(actor) - def OnInsertSeed(self, obj, evt): - x,y = self.interactor.GetEventPosition() - #x,y = obj.GetLastEventPosition() - self.picker.Pick(x, y, 0, self.ren) - point_id = self.picker.GetPointId() - self.seed_points.append(point_id) - self.interactor.Render() - - def OnInsertLinearMeasurePoint(self, obj, evt): - x,y = self.interactor.GetEventPosition() - self.measure_picker.Pick(x, y, 0, self.ren) - x, y, z = self.measure_picker.GetPickPosition() - - proj = prj.Project() - radius = min(proj.spacing) * PROP_MEASURE - if self.measure_picker.GetActor(): - # if not self.measures or self.measures[-1].IsComplete(): - # m = measures.LinearMeasure(self.ren) - # m.AddPoint(x, y, z) - # self.measures.append(m) - # else: - # m = self.measures[-1] - # m.AddPoint(x, y, z) - # if m.IsComplete(): - # Publisher.sendMessage("Add measure to list", - # (u"3D", _(u"%.3f mm" % m.GetValue()))) - Publisher.sendMessage("Add measurement point", - position=(x, y,z), - type=const.LINEAR, - location=const.SURFACE, - radius=radius) - self.interactor.Render() - - def OnInsertAngularMeasurePoint(self, obj, evt): - x,y = self.interactor.GetEventPosition() - self.measure_picker.Pick(x, y, 0, self.ren) - x, y, z = self.measure_picker.GetPickPosition() - - proj = prj.Project() - radius = min(proj.spacing) * PROP_MEASURE - if self.measure_picker.GetActor(): - # if not self.measures or self.measures[-1].IsComplete(): - # m = measures.AngularMeasure(self.ren) - # m.AddPoint(x, y, z) - # self.measures.append(m) - # else: - # m = self.measures[-1] - # m.AddPoint(x, y, z) - # if m.IsComplete(): - # index = len(self.measures) - 1 - # name = "M" - # colour = m.colour - # type_ = _("Angular") - # location = u"3D" - # value = u"%.2f˚"% m.GetValue() - # msg = 'Update measurement info in GUI', - # Publisher.sendMessage(msg, - # (index, name, colour, - # type_, location, - # value)) - Publisher.sendMessage("Add measurement point", - position=(x, y,z), - type=const.ANGULAR, - location=const.SURFACE, - radius=radius) - self.interactor.Render() - def Reposition3DPlane(self, plane_label): if not(self.added_actor) and not(self.raycasting_volume): if not(self.repositioned_axial_plan) and (plane_label == 'Axial'): diff --git a/invesalius/gui/data_notebook.py b/invesalius/gui/data_notebook.py index dd034ab..91b61e2 100644 --- a/invesalius/gui/data_notebook.py +++ b/invesalius/gui/data_notebook.py @@ -1030,16 +1030,17 @@ class MeasuresListCtrlPanel(InvListCtrl): self.RemoveMeasurements() def OnRemoveGUIMeasure(self, measure_index): - self.DeleteItem(measure_index) - - old_dict = self._list_index - new_dict = {} - j = 0 - for i in old_dict: - if i != measure_index: - new_dict[j] = old_dict[i] - j+=1 - self._list_index = new_dict + if measure_index in self._list_index: + self.DeleteItem(measure_index) + + old_dict = self._list_index + new_dict = {} + j = 0 + for i in old_dict: + if i != measure_index: + new_dict[j] = old_dict[i] + j+=1 + self._list_index = new_dict def RemoveMeasurements(self): """ -- libgit2 0.21.2