Commit 1c123311cf31987f045159fac792421054050feb

Authored by Thiago Franco de Moraes
Committed by GitHub
1 parent 1a5b94be
Exists in master

Create Styles 3D (#245)

* Added Zoom interactor styles
* Added Zoom by region interactor style
* Added WWWL interactor style 3D
* Added Panning style
* Added Spin style
* Added Linear measure style
* Added Angular measure style and cleanups in linear and angular styels
* Added insert seed style
* Remove old event methods from volume viewer
* Showing WW&WL text in Viewer Volume
* Remove old SetInteractorStyle from volume viewer
* Removed VOLUME_STYLES from constants
invesalius/constants.py
... ... @@ -582,11 +582,6 @@ SLICE_STYLES.append(STATE_MEASURE_DENSITY)
582 582 SLICE_STYLES.append(STATE_MEASURE_DENSITY_ELLIPSE)
583 583 SLICE_STYLES.append(STATE_MEASURE_DENSITY_POLYGON)
584 584  
585   -VOLUME_STYLES = TOOL_STATES + [VOLUME_STATE_SEED, STATE_MEASURE_DISTANCE,
586   - STATE_MEASURE_ANGLE]
587   -VOLUME_STYLES.append(STATE_DEFAULT)
588   -
589   -
590 585 STYLE_LEVEL = {SLICE_STATE_EDITOR: 1,
591 586 SLICE_STATE_WATERSHED: 1,
592 587 SLICE_STATE_MASK_FFILL: 2,
... ...
invesalius/control.py
... ... @@ -174,7 +174,7 @@ class Controller():
174 174 #Publisher.sendMessage("Enable state project", state=False)
175 175 Publisher.sendMessage('Set project name')
176 176 Publisher.sendMessage("Stop Config Recording")
177   - Publisher.sendMessage("Set slice interaction style", style=const.STATE_DEFAULT)
  177 + Publisher.sendMessage("Enable style", style=const.STATE_DEFAULT)
178 178  
179 179 # Import TIFF, BMP, JPEG or PNG
180 180 dirpath = dialog.ShowImportBitmapDirDialog(self.frame)
... ... @@ -198,7 +198,7 @@ class Controller():
198 198 #Publisher.sendMessage("Enable state project", state=False)
199 199 Publisher.sendMessage('Set project name')
200 200 Publisher.sendMessage("Stop Config Recording")
201   - Publisher.sendMessage("Set slice interaction style", style=const.STATE_DEFAULT)
  201 + Publisher.sendMessage("Enable style", style=const.STATE_DEFAULT)
202 202 # Import project
203 203 dirpath = dialog.ShowImportDirDialog(self.frame)
204 204 if dirpath and not os.listdir(dirpath):
... ... @@ -219,7 +219,7 @@ class Controller():
219 219 # Publisher.sendMessage("Enable state project", state=False)
220 220 Publisher.sendMessage('Set project name')
221 221 Publisher.sendMessage("Stop Config Recording")
222   - Publisher.sendMessage("Set slice interaction style", style=const.STATE_DEFAULT)
  222 + Publisher.sendMessage("Enable style", style=const.STATE_DEFAULT)
223 223  
224 224 # Warning for limited support to Analyze format
225 225 if id_type == const.ID_ANALYZE_IMPORT:
... ... @@ -373,7 +373,7 @@ class Controller():
373 373 Publisher.sendMessage('End busy cursor')
374 374  
375 375 def CloseProject(self):
376   - Publisher.sendMessage('Set slice interaction style', style=const.STATE_DEFAULT)
  376 + Publisher.sendMessage('Enable style', style=const.STATE_DEFAULT)
377 377 Publisher.sendMessage('Hide content panel')
378 378 Publisher.sendMessage('Close project data')
379 379  
... ...
invesalius/data/styles_3d.py 0 → 100644
... ... @@ -0,0 +1,461 @@
  1 +# --------------------------------------------------------------------------
  2 +# Software: InVesalius - Software de Reconstrucao 3D de Imagens Medicas
  3 +# Copyright: (C) 2001 Centro de Pesquisas Renato Archer
  4 +# Homepage: http://www.softwarepublico.gov.br
  5 +# Contact: invesalius@cti.gov.br
  6 +# License: GNU - GPL 2 (LICENSE.txt/LICENCA.txt)
  7 +# --------------------------------------------------------------------------
  8 +# Este programa e software livre; voce pode redistribui-lo e/ou
  9 +# modifica-lo sob os termos da Licenca Publica Geral GNU, conforme
  10 +# publicada pela Free Software Foundation; de acordo com a versao 2
  11 +# da Licenca.
  12 +#
  13 +# Este programa eh distribuido na expectativa de ser util, mas SEM
  14 +# QUALQUER GARANTIA; sem mesmo a garantia implicita de
  15 +# COMERCIALIZACAO ou de ADEQUACAO A QUALQUER PROPOSITO EM
  16 +# PARTICULAR. Consulte a Licenca Publica Geral GNU para obter mais
  17 +# detalhes.
  18 +# --------------------------------------------------------------------------
  19 +
  20 +import vtk
  21 +import wx
  22 +
  23 +import invesalius.constants as const
  24 +import invesalius.project as prj
  25 +
  26 +from pubsub import pub as Publisher
  27 +
  28 +
  29 +PROP_MEASURE = 0.8
  30 +
  31 +
  32 +class Base3DInteractorStyle(vtk.vtkInteractorStyleTrackballCamera):
  33 + def __init__(self, viewer):
  34 + self.right_pressed = False
  35 + self.left_pressed = False
  36 + self.middle_pressed = False
  37 +
  38 + self.AddObserver("LeftButtonPressEvent", self.OnPressLeftButton)
  39 + self.AddObserver("LeftButtonReleaseEvent", self.OnReleaseLeftButton)
  40 +
  41 + self.AddObserver("RightButtonPressEvent", self.OnPressRightButton)
  42 + self.AddObserver("RightButtonReleaseEvent", self.OnReleaseRightButton)
  43 +
  44 + self.AddObserver("MiddleButtonPressEvent", self.OnMiddleButtonPressEvent)
  45 + self.AddObserver("MiddleButtonReleaseEvent", self.OnMiddleButtonReleaseEvent)
  46 +
  47 + def OnPressLeftButton(self, evt, obj):
  48 + self.left_pressed = True
  49 +
  50 + def OnReleaseLeftButton(self, evt, obj):
  51 + self.left_pressed = False
  52 +
  53 + def OnPressRightButton(self, evt, obj):
  54 + self.right_pressed = True
  55 +
  56 + def OnReleaseRightButton(self, evt, obj):
  57 + self.right_pressed = False
  58 +
  59 + def OnMiddleButtonPressEvent(self, evt, obj):
  60 + self.middle_pressed = True
  61 +
  62 + def OnMiddleButtonReleaseEvent(self, evt, obj):
  63 + self.middle_pressed = False
  64 +
  65 +
  66 +class DefaultInteractorStyle(Base3DInteractorStyle):
  67 + """
  68 + Interactor style responsible for Default functionalities:
  69 + * Zoom moving mouse with right button pressed;
  70 + * Change the slices with the scroll.
  71 + """
  72 + def __init__(self, viewer):
  73 + super().__init__(viewer)
  74 + self.state_code = const.STATE_DEFAULT
  75 +
  76 + self.viewer = viewer
  77 +
  78 + # Zoom using right button
  79 + self.AddObserver("LeftButtonPressEvent",self.OnRotateLeftClick)
  80 + self.AddObserver("LeftButtonReleaseEvent",self.OnRotateLeftRelease)
  81 +
  82 + self.AddObserver("RightButtonPressEvent",self.OnZoomRightClick)
  83 + self.AddObserver("RightButtonReleaseEvent",self.OnZoomRightRelease)
  84 +
  85 + self.AddObserver("MouseMoveEvent", self.OnMouseMove)
  86 +
  87 + self.AddObserver("MouseWheelForwardEvent",self.OnScrollForward)
  88 + self.AddObserver("MouseWheelBackwardEvent", self.OnScrollBackward)
  89 + self.AddObserver("EnterEvent", self.OnFocus)
  90 +
  91 + def OnFocus(self, evt, obj):
  92 + self.viewer.SetFocus()
  93 +
  94 + def OnMouseMove(self, evt, obj):
  95 + if self.left_pressed:
  96 + evt.Rotate()
  97 + evt.OnLeftButtonDown()
  98 +
  99 + elif self.right_pressed:
  100 + evt.Dolly()
  101 + evt.OnRightButtonDown()
  102 +
  103 + elif self.middle_pressed:
  104 + evt.Pan()
  105 + evt.OnMiddleButtonDown()
  106 +
  107 + def OnRotateLeftClick(self, evt, obj):
  108 + evt.StartRotate()
  109 +
  110 + def OnRotateLeftRelease(self, evt, obj):
  111 + evt.OnLeftButtonUp()
  112 + evt.EndRotate()
  113 +
  114 + def OnZoomRightClick(self, evt, obj):
  115 + evt.StartDolly()
  116 +
  117 + def OnZoomRightRelease(self, evt, obj):
  118 + evt.OnRightButtonUp()
  119 + evt.EndDolly()
  120 +
  121 + def OnScrollForward(self, evt, obj):
  122 + self.OnMouseWheelForward()
  123 +
  124 + def OnScrollBackward(self, evt, obj):
  125 + self.OnMouseWheelBackward()
  126 +
  127 +
  128 +class ZoomInteractorStyle(DefaultInteractorStyle):
  129 + """
  130 + Interactor style responsible for zoom with movement of the mouse and the
  131 + left mouse button clicked.
  132 + """
  133 + def __init__(self, viewer):
  134 + super().__init__(viewer)
  135 +
  136 + self.state_code = const.STATE_ZOOM
  137 +
  138 + self.viewer = viewer
  139 +
  140 + self.RemoveObservers("LeftButtonPressEvent")
  141 + self.RemoveObservers("LeftButtonReleaseEvent")
  142 +
  143 + self.AddObserver("LeftButtonPressEvent", self.OnPressLeftButton)
  144 + self.AddObserver("LeftButtonReleaseEvent", self.OnReleaseLeftButton)
  145 +
  146 + self.viewer.interactor.Bind(wx.EVT_LEFT_DCLICK, self.OnUnZoom)
  147 +
  148 + def SetUp(self):
  149 + Publisher.sendMessage('Toggle toolbar item',
  150 + _id=self.state_code, value=True)
  151 +
  152 + def CleanUp(self):
  153 + self.viewer.interactor.Unbind(wx.EVT_LEFT_DCLICK)
  154 + Publisher.sendMessage('Toggle toolbar item',
  155 + _id=self.state_code, value=False)
  156 +
  157 + def OnPressLeftButton(self, evt, obj):
  158 + self.right_pressed = True
  159 +
  160 + def OnReleaseLeftButton(self, obj, evt):
  161 + self.right_pressed = False
  162 +
  163 + def OnUnZoom(self, evt):
  164 + ren = self.viewer.ren
  165 + ren.ResetCamera()
  166 + ren.ResetCameraClippingRange()
  167 + self.viewer.interactor.Render()
  168 +
  169 +
  170 +class ZoomSLInteractorStyle(vtk.vtkInteractorStyleRubberBandZoom):
  171 + """
  172 + Interactor style responsible for zoom by selecting a region.
  173 + """
  174 + def __init__(self, viewer):
  175 + self.viewer = viewer
  176 + self.viewer.interactor.Bind(wx.EVT_LEFT_DCLICK, self.OnUnZoom)
  177 +
  178 + self.state_code = const.STATE_ZOOM_SL
  179 +
  180 + def SetUp(self):
  181 + Publisher.sendMessage('Toggle toolbar item',
  182 + _id=self.state_code, value=True)
  183 +
  184 + def CleanUp(self):
  185 + self.viewer.interactor.Unbind(wx.EVT_LEFT_DCLICK)
  186 + Publisher.sendMessage('Toggle toolbar item',
  187 + _id=self.state_code, value=False)
  188 +
  189 + def OnUnZoom(self, evt):
  190 + ren = self.viewer.ren
  191 + ren.ResetCamera()
  192 + ren.ResetCameraClippingRange()
  193 + self.viewer.interactor.Render()
  194 +
  195 +
  196 +class PanMoveInteractorStyle(DefaultInteractorStyle):
  197 + """
  198 + Interactor style responsible for translate the camera.
  199 + """
  200 + def __init__(self, viewer):
  201 + super().__init__(viewer)
  202 +
  203 + self.state_code = const.STATE_PAN
  204 +
  205 + self.viewer = viewer
  206 +
  207 + self.panning = False
  208 +
  209 + self.AddObserver("MouseMoveEvent", self.OnPanMove)
  210 + self.viewer.interactor.Bind(wx.EVT_LEFT_DCLICK, self.OnUnspan)
  211 +
  212 + def SetUp(self):
  213 + Publisher.sendMessage('Toggle toolbar item',
  214 + _id=self.state_code, value=True)
  215 +
  216 + def CleanUp(self):
  217 + self.viewer.interactor.Unbind(wx.EVT_LEFT_DCLICK)
  218 + Publisher.sendMessage('Toggle toolbar item',
  219 + _id=self.state_code, value=False)
  220 +
  221 + def OnPressLeftButton(self, evt, obj):
  222 + self.panning = True
  223 +
  224 + def OnReleaseLeftButton(self, evt, obj):
  225 + self.panning = False
  226 +
  227 + def OnPanMove(self, obj, evt):
  228 + if self.panning:
  229 + obj.Pan()
  230 + obj.OnRightButtonDown()
  231 +
  232 + def OnUnspan(self, evt):
  233 + self.viewer.ren.ResetCamera()
  234 + self.viewer.interactor.Render()
  235 +
  236 +
  237 +class SpinInteractorStyle(DefaultInteractorStyle):
  238 + """
  239 + Interactor style responsible for spin the camera.
  240 + """
  241 + def __init__(self, viewer):
  242 + DefaultInteractorStyle.__init__(self, viewer)
  243 + self.state_code = const.STATE_SPIN
  244 + self.viewer = viewer
  245 + self.spinning = False
  246 + self.AddObserver("MouseMoveEvent", self.OnSpinMove)
  247 +
  248 + def SetUp(self):
  249 + Publisher.sendMessage('Toggle toolbar item', _id=self.state_code, value=True)
  250 +
  251 + def CleanUp(self):
  252 + Publisher.sendMessage('Toggle toolbar item', _id=self.state_code, value=False)
  253 +
  254 + def OnPressLeftButton(self, evt, obj):
  255 + self.spinning = True
  256 +
  257 + def OnReleaseLeftButton(self, evt, obj):
  258 + self.spinning = False
  259 +
  260 + def OnSpinMove(self, evt, obj):
  261 + if self.spinning:
  262 + evt.Spin()
  263 + evt.OnRightButtonDown()
  264 +
  265 +
  266 +class WWWLInteractorStyle(DefaultInteractorStyle):
  267 + """
  268 + Interactor style responsible for Window Level & Width functionality.
  269 + """
  270 + def __init__(self, viewer):
  271 + super().__init__(viewer)
  272 + self.state_code = const.STATE_WL
  273 +
  274 + self.viewer = viewer
  275 +
  276 + self.last_x = 0
  277 + self.last_y = 0
  278 +
  279 + self.changing_wwwl = False
  280 +
  281 + self.RemoveObservers("LeftButtonPressEvent")
  282 + self.RemoveObservers("LeftButtonReleaseEvent")
  283 +
  284 + self.AddObserver("MouseMoveEvent", self.OnWindowLevelMove)
  285 + self.AddObserver("LeftButtonPressEvent", self.OnWindowLevelClick)
  286 + self.AddObserver("LeftButtonReleaseEvent", self.OnWindowLevelRelease)
  287 +
  288 + def SetUp(self):
  289 + Publisher.sendMessage('Toggle toolbar item', _id=self.state_code, value=True)
  290 + self.viewer.on_wl = True
  291 + if self.viewer.raycasting_volume:
  292 + self.viewer.text.Show()
  293 + self.viewer.interactor.Render()
  294 +
  295 + def CleanUp(self):
  296 + Publisher.sendMessage('Toggle toolbar item', _id=self.state_code, value=False)
  297 + self.viewer.on_wl = True
  298 + self.viewer.text.Hide()
  299 + self.viewer.interactor.Render()
  300 +
  301 + def OnWindowLevelMove(self, obj, evt):
  302 + if self.changing_wwwl:
  303 + iren = obj.GetInteractor()
  304 + mouse_x, mouse_y = iren.GetEventPosition()
  305 + diff_x = mouse_x - self.last_x
  306 + diff_y = mouse_y - self.last_y
  307 + self.last_x, self.last_y = mouse_x, mouse_y
  308 + Publisher.sendMessage('Set raycasting relative window and level',
  309 + diff_wl=diff_x, diff_ww=diff_y)
  310 + Publisher.sendMessage('Refresh raycasting widget points')
  311 + Publisher.sendMessage('Render volume viewer')
  312 +
  313 + def OnWindowLevelClick(self, obj, evt):
  314 + iren = obj.GetInteractor()
  315 + self.last_x, self.last_y = iren.GetLastEventPosition()
  316 + self.changing_wwwl = True
  317 +
  318 + def OnWindowLevelRelease(self, obj, evt):
  319 + self.changing_wwwl = False
  320 +
  321 +
  322 +class LinearMeasureInteractorStyle(DefaultInteractorStyle):
  323 + """
  324 + Interactor style responsible for insert linear measurements.
  325 + """
  326 + def __init__(self, viewer):
  327 + super().__init__(viewer)
  328 + self.viewer = viewer
  329 + self.state_code = const.STATE_MEASURE_DISTANCE
  330 + self.measure_picker = vtk.vtkPropPicker()
  331 +
  332 + proj = prj.Project()
  333 + self._radius = min(proj.spacing) * PROP_MEASURE
  334 +
  335 + self.RemoveObservers("LeftButtonPressEvent")
  336 + self.AddObserver("LeftButtonPressEvent", self.OnInsertLinearMeasurePoint)
  337 +
  338 + def SetUp(self):
  339 + Publisher.sendMessage('Toggle toolbar item',
  340 + _id=self.state_code, value=True)
  341 +
  342 + def CleanUp(self):
  343 + Publisher.sendMessage('Toggle toolbar item',
  344 + _id=self.state_code, value=False)
  345 + Publisher.sendMessage("Remove incomplete measurements")
  346 +
  347 + def OnInsertLinearMeasurePoint(self, obj, evt):
  348 + x,y = self.viewer.interactor.GetEventPosition()
  349 + self.measure_picker.Pick(x, y, 0, self.viewer.ren)
  350 + x, y, z = self.measure_picker.GetPickPosition()
  351 + if self.measure_picker.GetActor():
  352 + self.left_pressed = False
  353 + Publisher.sendMessage("Add measurement point",
  354 + position=(x, y,z),
  355 + type=const.LINEAR,
  356 + location=const.SURFACE,
  357 + radius=self._radius)
  358 + self.viewer.interactor.Render()
  359 + else:
  360 + self.left_pressed = True
  361 +
  362 +
  363 +class AngularMeasureInteractorStyle(DefaultInteractorStyle):
  364 + """
  365 + Interactor style responsible for insert linear measurements.
  366 + """
  367 + def __init__(self, viewer):
  368 + super().__init__(viewer)
  369 + self.viewer = viewer
  370 + self.state_code = const.STATE_MEASURE_DISTANCE
  371 + self.measure_picker = vtk.vtkPropPicker()
  372 +
  373 + proj = prj.Project()
  374 + self._radius = min(proj.spacing) * PROP_MEASURE
  375 +
  376 + self.RemoveObservers("LeftButtonPressEvent")
  377 + self.AddObserver("LeftButtonPressEvent", self.OnInsertAngularMeasurePoint)
  378 +
  379 + def SetUp(self):
  380 + Publisher.sendMessage('Toggle toolbar item', _id=self.state_code, value=True)
  381 +
  382 + def CleanUp(self):
  383 + Publisher.sendMessage('Toggle toolbar item', _id=self.state_code, value=False)
  384 + Publisher.sendMessage("Remove incomplete measurements")
  385 +
  386 + def OnInsertAngularMeasurePoint(self, obj, evt):
  387 + x,y = self.viewer.interactor.GetEventPosition()
  388 + self.measure_picker.Pick(x, y, 0, self.viewer.ren)
  389 + x, y, z = self.measure_picker.GetPickPosition()
  390 + if self.measure_picker.GetActor():
  391 + self.left_pressed = False
  392 + Publisher.sendMessage("Add measurement point",
  393 + position=(x, y,z),
  394 + type=const.ANGULAR,
  395 + location=const.SURFACE,
  396 + radius=self._radius)
  397 + self.viewer.interactor.Render()
  398 + else:
  399 + self.left_pressed = True
  400 +
  401 +
  402 +class SeedInteractorStyle(DefaultInteractorStyle):
  403 + """
  404 + Interactor style responsible for select sub surfaces.
  405 + """
  406 + def __init__(self, viewer):
  407 + super().__init__(viewer)
  408 + self.viewer = viewer
  409 + self.picker = vtk.vtkPointPicker()
  410 +
  411 + self.RemoveObservers("LeftButtonPressEvent")
  412 + self.AddObserver("LeftButtonPressEvent", self.OnInsertSeed)
  413 +
  414 + def OnInsertSeed(self, obj, evt):
  415 + x,y = self.viewer.interactor.GetEventPosition()
  416 + self.picker.Pick(x, y, 0, self.viewer.ren)
  417 + point_id = self.picker.GetPointId()
  418 + if point_id > -1:
  419 + self.viewer.seed_points.append(point_id)
  420 + self.viewer.interactor.Render()
  421 + else:
  422 + self.left_pressed = True
  423 +
  424 +
  425 +class Styles:
  426 + styles = {
  427 + const.STATE_DEFAULT: DefaultInteractorStyle,
  428 + const.STATE_ZOOM: ZoomInteractorStyle,
  429 + const.STATE_ZOOM_SL: ZoomSLInteractorStyle,
  430 + const.STATE_PAN: PanMoveInteractorStyle,
  431 + const.STATE_SPIN: SpinInteractorStyle,
  432 + const.STATE_WL: WWWLInteractorStyle,
  433 + const.STATE_MEASURE_DISTANCE: LinearMeasureInteractorStyle,
  434 + const.STATE_MEASURE_ANGLE: AngularMeasureInteractorStyle,
  435 + const.VOLUME_STATE_SEED: SeedInteractorStyle,
  436 + }
  437 +
  438 + @classmethod
  439 + def add_style(cls, style_cls, level=1):
  440 + if style_cls in cls.styles.values():
  441 + for style_id in cls.styles:
  442 + if cls.styles[style_id] == style_cls:
  443 + const.STYLE_LEVEL[style_id] = level
  444 + return style_id
  445 +
  446 + new_style_id = max(cls.styles) + 1
  447 + cls.styles[new_style_id] = style_cls
  448 + const.STYLE_LEVEL[new_style_id] = level
  449 + return new_style_id
  450 +
  451 + @classmethod
  452 + def remove_style(cls, style_id):
  453 + del cls.styles[style_id]
  454 +
  455 + @classmethod
  456 + def get_style(cls, style):
  457 + return cls.styles[style]
  458 +
  459 + @classmethod
  460 + def has_style(cls, style):
  461 + return style in cls.styles
... ...
invesalius/data/viewer_volume.py
... ... @@ -38,6 +38,7 @@ from imageio import imsave
38 38 import invesalius.constants as const
39 39 import invesalius.data.bases as bases
40 40 import invesalius.data.slice_ as sl
  41 +import invesalius.data.styles_3d as styles
41 42 import invesalius.data.transformations as tr
42 43 import invesalius.data.vtk_utils as vtku
43 44 import invesalius.project as prj
... ... @@ -46,6 +47,7 @@ import invesalius.utils as utils
46 47  
47 48 from invesalius import inv_paths
48 49  
  50 +
49 51 if sys.platform == 'win32':
50 52 try:
51 53 import win32api
... ... @@ -70,14 +72,14 @@ class Viewer(wx.Panel):
70 72  
71 73 self.staticballs = []
72 74  
73   - style = vtk.vtkInteractorStyleTrackballCamera()
74   - self.style = style
  75 + self.style = None
75 76  
76 77 interactor = wxVTKRenderWindowInteractor(self, -1, size = self.GetSize())
77   - interactor.SetInteractorStyle(style)
78 78 self.interactor = interactor
79 79 self.interactor.SetRenderWhenDisabled(True)
80 80  
  81 + self.enable_style(const.STATE_DEFAULT)
  82 +
81 83 sizer = wx.BoxSizer(wx.VERTICAL)
82 84 sizer.Add(interactor, 1, wx.EXPAND)
83 85 self.sizer = sizer
... ... @@ -149,8 +151,6 @@ class Viewer(wx.Panel):
149 151 #self.measure_picker.SetTolerance(0.005)
150 152 self.measures = []
151 153  
152   - self._last_state = 0
153   -
154 154 self.repositioned_axial_plan = 0
155 155 self.repositioned_sagital_plan = 0
156 156 self.repositioned_coronal_plan = 0
... ... @@ -255,7 +255,7 @@ class Viewer(wx.Panel):
255 255 # Publisher.subscribe(self.SetVolumeCamera, 'Set camera in volume')
256 256 Publisher.subscribe(self.SetVolumeCameraState, 'Update volume camera state')
257 257  
258   - Publisher.subscribe(self.OnEnableStyle, 'Enable style')
  258 + Publisher.subscribe(self.enable_style, 'Enable style')
259 259 Publisher.subscribe(self.OnDisableStyle, 'Disable style')
260 260  
261 261 Publisher.subscribe(self.OnHideText,
... ... @@ -506,7 +506,6 @@ class Viewer(wx.Panel):
506 506  
507 507 self.interaction_style.Reset()
508 508 self.SetInteractorStyle(const.STATE_DEFAULT)
509   - self._last_state = const.STATE_DEFAULT
510 509  
511 510 def OnHideText(self):
512 511 self.text.Hide()
... ... @@ -1441,147 +1440,26 @@ class Viewer(wx.Panel):
1441 1440 imsave('/tmp/polygon.png', arr)
1442 1441  
1443 1442 def SetInteractorStyle(self, state):
1444   - action = {
1445   - const.STATE_PAN:
1446   - {
1447   - "MouseMoveEvent": self.OnPanMove,
1448   - "LeftButtonPressEvent": self.OnPanClick,
1449   - "LeftButtonReleaseEvent": self.OnReleasePanClick
1450   - },
1451   - const.STATE_ZOOM:
1452   - {
1453   - "MouseMoveEvent": self.OnZoomMove,
1454   - "LeftButtonPressEvent": self.OnZoomClick,
1455   - "LeftButtonReleaseEvent": self.OnReleaseZoomClick,
1456   - },
1457   - const.STATE_SPIN:
1458   - {
1459   - "MouseMoveEvent": self.OnSpinMove,
1460   - "LeftButtonPressEvent": self.OnSpinClick,
1461   - "LeftButtonReleaseEvent": self.OnReleaseSpinClick,
1462   - },
1463   - const.STATE_WL:
1464   - {
1465   - "MouseMoveEvent": self.OnWindowLevelMove,
1466   - "LeftButtonPressEvent": self.OnWindowLevelClick,
1467   - "LeftButtonReleaseEvent":self.OnWindowLevelRelease
1468   - },
1469   - const.STATE_DEFAULT:
1470   - {
1471   - },
1472   - const.VOLUME_STATE_SEED:
1473   - {
1474   - "LeftButtonPressEvent": self.OnInsertSeed
1475   - },
1476   - const.STATE_MEASURE_DISTANCE:
1477   - {
1478   - "LeftButtonPressEvent": self.OnInsertLinearMeasurePoint
1479   - },
1480   - const.STATE_MEASURE_ANGLE:
1481   - {
1482   - "LeftButtonPressEvent": self.OnInsertAngularMeasurePoint
1483   - }
1484   - }
1485   -
1486   - if self._last_state in (const.STATE_MEASURE_DISTANCE,
1487   - const.STATE_MEASURE_ANGLE):
1488   - if self.measures and not self.measures[-1].text_actor:
1489   - del self.measures[-1]
1490   -
1491   - if state == const.STATE_WL:
1492   - self.on_wl = True
1493   - if self.raycasting_volume:
1494   - self.text.Show()
1495   - self.interactor.Render()
1496   - else:
1497   - self.on_wl = False
1498   - self.text.Hide()
1499   - self.interactor.Render()
  1443 + cleanup = getattr(self.style, 'CleanUp', None)
  1444 + if cleanup:
  1445 + self.style.CleanUp()
1500 1446  
1501   - if state in (const.STATE_MEASURE_DISTANCE,
1502   - const.STATE_MEASURE_ANGLE):
1503   - self.interactor.SetPicker(self.measure_picker)
  1447 + del self.style
1504 1448  
1505   - if (state == const.STATE_ZOOM_SL):
1506   - style = vtk.vtkInteractorStyleRubberBandZoom()
1507   - self.interactor.SetInteractorStyle(style)
1508   - self.style = style
1509   - else:
1510   - style = vtk.vtkInteractorStyleTrackballCamera()
1511   - self.interactor.SetInteractorStyle(style)
1512   - self.style = style
1513   -
1514   - # Check each event available for each mode
1515   - for event in action.get(state, []):
1516   - # Bind event
1517   - style.AddObserver(event,action[state][event])
1518   -
1519   - self._last_state = state
1520   -
1521   - def OnSpinMove(self, evt, obj):
1522   - if (self.mouse_pressed):
1523   - evt.Spin()
1524   - evt.OnRightButtonDown()
1525   -
1526   - def OnSpinClick(self, evt, obj):
1527   - self.mouse_pressed = 1
1528   - evt.StartSpin()
  1449 + style = styles.Styles.get_style(state)(self)
1529 1450  
1530   - def OnReleaseSpinClick(self,evt,obj):
1531   - self.mouse_pressed = 0
1532   - evt.EndSpin()
1533   -
1534   - def OnZoomMove(self, evt, obj):
1535   - if (self.mouse_pressed):
1536   - evt.Dolly()
1537   - evt.OnRightButtonDown()
1538   -
1539   - def OnZoomClick(self, evt, obj):
1540   - self.mouse_pressed = 1
1541   - evt.StartDolly()
1542   -
1543   - def OnReleaseZoomClick(self,evt,obj):
1544   - self.mouse_pressed = 0
1545   - evt.EndDolly()
  1451 + setup = getattr(style, 'SetUp', None)
  1452 + if setup:
  1453 + style.SetUp()
1546 1454  
1547   - def OnPanMove(self, evt, obj):
1548   - if (self.mouse_pressed):
1549   - evt.Pan()
1550   - evt.OnRightButtonDown()
1551   -
1552   - def OnPanClick(self, evt, obj):
1553   - self.mouse_pressed = 1
1554   - evt.StartPan()
1555   -
1556   - def OnReleasePanClick(self,evt,obj):
1557   - self.mouse_pressed = 0
1558   - evt.EndPan()
1559   -
1560   - def OnWindowLevelMove(self, obj, evt):
1561   - if self.onclick and self.raycasting_volume:
1562   - mouse_x, mouse_y = self.interactor.GetEventPosition()
1563   - diff_x = mouse_x - self.last_x
1564   - diff_y = mouse_y - self.last_y
1565   - self.last_x, self.last_y = mouse_x, mouse_y
1566   - Publisher.sendMessage('Set raycasting relative window and level',
1567   - diff_wl=diff_x, diff_ww=diff_y)
1568   - Publisher.sendMessage('Refresh raycasting widget points')
1569   - self.interactor.Render()
1570   -
1571   - def OnWindowLevelClick(self, obj, evt):
1572   - if const.RAYCASTING_WWWL_BLUR:
1573   - self.style.StartZoom()
1574   - self.onclick = True
1575   - mouse_x, mouse_y = self.interactor.GetEventPosition()
1576   - self.last_x, self.last_y = mouse_x, mouse_y
  1455 + self.style = style
  1456 + self.interactor.SetInteractorStyle(style)
  1457 + self.interactor.Render()
1577 1458  
1578   - def OnWindowLevelRelease(self, obj, evt):
1579   - self.onclick = False
1580   - if const.RAYCASTING_WWWL_BLUR:
1581   - self.style.EndZoom()
  1459 + self.state = state
1582 1460  
1583   - def OnEnableStyle(self, style):
1584   - if (style in const.VOLUME_STYLES):
  1461 + def enable_style(self, style):
  1462 + if styles.Styles.has_style(style):
1585 1463 new_state = self.interaction_style.AddState(style)
1586 1464 self.SetInteractorStyle(new_state)
1587 1465 else:
... ... @@ -1849,73 +1727,6 @@ class Viewer(wx.Panel):
1849 1727 def AppendActor(self, actor):
1850 1728 self.ren.AddActor(actor)
1851 1729  
1852   - def OnInsertSeed(self, obj, evt):
1853   - x,y = self.interactor.GetEventPosition()
1854   - #x,y = obj.GetLastEventPosition()
1855   - self.picker.Pick(x, y, 0, self.ren)
1856   - point_id = self.picker.GetPointId()
1857   - self.seed_points.append(point_id)
1858   - self.interactor.Render()
1859   -
1860   - def OnInsertLinearMeasurePoint(self, obj, evt):
1861   - x,y = self.interactor.GetEventPosition()
1862   - self.measure_picker.Pick(x, y, 0, self.ren)
1863   - x, y, z = self.measure_picker.GetPickPosition()
1864   -
1865   - proj = prj.Project()
1866   - radius = min(proj.spacing) * PROP_MEASURE
1867   - if self.measure_picker.GetActor():
1868   - # if not self.measures or self.measures[-1].IsComplete():
1869   - # m = measures.LinearMeasure(self.ren)
1870   - # m.AddPoint(x, y, z)
1871   - # self.measures.append(m)
1872   - # else:
1873   - # m = self.measures[-1]
1874   - # m.AddPoint(x, y, z)
1875   - # if m.IsComplete():
1876   - # Publisher.sendMessage("Add measure to list",
1877   - # (u"3D", _(u"%.3f mm" % m.GetValue())))
1878   - Publisher.sendMessage("Add measurement point",
1879   - position=(x, y,z),
1880   - type=const.LINEAR,
1881   - location=const.SURFACE,
1882   - radius=radius)
1883   - self.interactor.Render()
1884   -
1885   - def OnInsertAngularMeasurePoint(self, obj, evt):
1886   - x,y = self.interactor.GetEventPosition()
1887   - self.measure_picker.Pick(x, y, 0, self.ren)
1888   - x, y, z = self.measure_picker.GetPickPosition()
1889   -
1890   - proj = prj.Project()
1891   - radius = min(proj.spacing) * PROP_MEASURE
1892   - if self.measure_picker.GetActor():
1893   - # if not self.measures or self.measures[-1].IsComplete():
1894   - # m = measures.AngularMeasure(self.ren)
1895   - # m.AddPoint(x, y, z)
1896   - # self.measures.append(m)
1897   - # else:
1898   - # m = self.measures[-1]
1899   - # m.AddPoint(x, y, z)
1900   - # if m.IsComplete():
1901   - # index = len(self.measures) - 1
1902   - # name = "M"
1903   - # colour = m.colour
1904   - # type_ = _("Angular")
1905   - # location = u"3D"
1906   - # value = u"%.2f˚"% m.GetValue()
1907   - # msg = 'Update measurement info in GUI',
1908   - # Publisher.sendMessage(msg,
1909   - # (index, name, colour,
1910   - # type_, location,
1911   - # value))
1912   - Publisher.sendMessage("Add measurement point",
1913   - position=(x, y,z),
1914   - type=const.ANGULAR,
1915   - location=const.SURFACE,
1916   - radius=radius)
1917   - self.interactor.Render()
1918   -
1919 1730 def Reposition3DPlane(self, plane_label):
1920 1731 if not(self.added_actor) and not(self.raycasting_volume):
1921 1732 if not(self.repositioned_axial_plan) and (plane_label == 'Axial'):
... ...
invesalius/gui/data_notebook.py
... ... @@ -1030,16 +1030,17 @@ class MeasuresListCtrlPanel(InvListCtrl):
1030 1030 self.RemoveMeasurements()
1031 1031  
1032 1032 def OnRemoveGUIMeasure(self, measure_index):
1033   - self.DeleteItem(measure_index)
1034   -
1035   - old_dict = self._list_index
1036   - new_dict = {}
1037   - j = 0
1038   - for i in old_dict:
1039   - if i != measure_index:
1040   - new_dict[j] = old_dict[i]
1041   - j+=1
1042   - self._list_index = new_dict
  1033 + if measure_index in self._list_index:
  1034 + self.DeleteItem(measure_index)
  1035 +
  1036 + old_dict = self._list_index
  1037 + new_dict = {}
  1038 + j = 0
  1039 + for i in old_dict:
  1040 + if i != measure_index:
  1041 + new_dict[j] = old_dict[i]
  1042 + j+=1
  1043 + self._list_index = new_dict
1043 1044  
1044 1045 def RemoveMeasurements(self):
1045 1046 """
... ...