Commit f7b5fc4d6a0a17b0359510217568644ca344dc5a
1 parent
071dff96
Exists in
interactor_style
Created a new interactor style to handle the cross tool
Showing
2 changed files
with
264 additions
and
141 deletions
Show diff stats
invesalius/data/styles.py
... | ... | @@ -19,8 +19,145 @@ |
19 | 19 | |
20 | 20 | import vtk |
21 | 21 | |
22 | +from wx.lib.pubsub import pub as Publisher | |
23 | + | |
22 | 24 | import constants as const |
23 | 25 | |
26 | +class ZoomInteractorStyle(vtk.vtkInteractorStyleImage): | |
27 | + """ | |
28 | + Interactor style responsible for zoom the camera. | |
29 | + """ | |
30 | + def __init__(self): | |
31 | + self.right_pressed = False | |
32 | + | |
33 | + # Zoom using right button | |
34 | + self.AddObserver("RightButtonPressEvent",self.OnZoomRightClick) | |
35 | + self.AddObserver("MouseMoveEvent", self.OnZoomRightMove) | |
36 | + self.AddObserver("RightButtonReleaseEvent", self.OnZoomRightRelease) | |
37 | + | |
38 | + def OnZoomRightMove(self, evt, obj): | |
39 | + if (self.right_pressed): | |
40 | + evt.Dolly() | |
41 | + evt.OnRightButtonDown() | |
42 | + | |
43 | + def OnZoomRightClick(self, evt, obj): | |
44 | + self.right_pressed = 1 | |
45 | + evt.StartDolly() | |
46 | + | |
47 | + def OnZoomRightRelease(self, evt, obj): | |
48 | + self.right_pressed = False | |
49 | + | |
50 | +class CrossInteractorStyle(ZoomInteractorStyle): | |
51 | + """ | |
52 | + Interactor style responsible for the Cross. | |
53 | + """ | |
54 | + def __init__(self, orientation, slice_data): | |
55 | + ZoomInteractorStyle.__init__(self) | |
56 | + | |
57 | + self.orientation = orientation | |
58 | + self.slice_actor = slice_data.actor | |
59 | + self.slice_data = slice_data | |
60 | + | |
61 | + self.left_pressed = False | |
62 | + self.picker = vtk.vtkWorldPointPicker() | |
63 | + | |
64 | + self.AddObserver("MouseMoveEvent", self.OnCrossMove) | |
65 | + self.AddObserver("LeftButtonPressEvent", self.OnCrossMouseClick) | |
66 | + self.AddObserver("LeftButtonReleaseEvent", self.OnReleaseLeftButton) | |
67 | + | |
68 | + def OnCrossMouseClick(self, obj, evt): | |
69 | + self.left_pressed = True | |
70 | + iren = obj.GetInteractor() | |
71 | + self.ChangeCrossPosition(iren) | |
72 | + | |
73 | + def OnCrossMove(self, obj, evt): | |
74 | + # The user moved the mouse with left button pressed | |
75 | + if self.left_pressed: | |
76 | + print "OnCrossMove interactor style" | |
77 | + iren = obj.GetInteractor() | |
78 | + self.ChangeCrossPosition(iren) | |
79 | + | |
80 | + def OnReleaseLeftButton(self, obj, evt): | |
81 | + self.left_pressed = False | |
82 | + | |
83 | + def ChangeCrossPosition(self, iren): | |
84 | + mouse_x, mouse_y = iren.GetEventPosition() | |
85 | + ren = iren.GetRenderWindow().GetRenderers().GetFirstRenderer() | |
86 | + self.picker.Pick(mouse_x, mouse_y, 0, ren) | |
87 | + | |
88 | + # Get in what slice data the click occurred | |
89 | + # pick to get click position in the 3d world | |
90 | + coord_cross = self.get_coordinate_cursor() | |
91 | + position = self.slice_actor.GetInput().FindPoint(coord_cross) | |
92 | + # Forcing focal point to be setted in the center of the pixel. | |
93 | + coord_cross = self.slice_actor.GetInput().GetPoint(position) | |
94 | + | |
95 | + coord = self.calcultate_scroll_position(position) | |
96 | + self.ScrollSlice(coord) | |
97 | + | |
98 | + Publisher.sendMessage('Update cross position', coord_cross) | |
99 | + Publisher.sendMessage('Set ball reference position based on bound', | |
100 | + coord_cross) | |
101 | + Publisher.sendMessage('Set camera in volume', coord_cross) | |
102 | + Publisher.sendMessage('Render volume viewer') | |
103 | + | |
104 | + iren.Render() | |
105 | + | |
106 | + def get_coordinate_cursor(self): | |
107 | + # Find position | |
108 | + x, y, z = self.picker.GetPickPosition() | |
109 | + bounds = self.slice_actor.GetBounds() | |
110 | + if bounds[0] == bounds[1]: | |
111 | + x = bounds[0] | |
112 | + elif bounds[2] == bounds[3]: | |
113 | + y = bounds[2] | |
114 | + elif bounds[4] == bounds[5]: | |
115 | + z = bounds[4] | |
116 | + return x, y, z | |
117 | + | |
118 | + def calcultate_scroll_position(self, position): | |
119 | + # Based in the given coord (x, y, z), returns a list with the scroll positions for each | |
120 | + # orientation, being the first position the sagital, second the coronal | |
121 | + # and the last, axial. | |
122 | + | |
123 | + if self.orientation == 'AXIAL': | |
124 | + image_width = self.slice_actor.GetInput().GetDimensions()[0] | |
125 | + axial = self.slice_data.number | |
126 | + coronal = position / image_width | |
127 | + sagital = position % image_width | |
128 | + | |
129 | + elif self.orientation == 'CORONAL': | |
130 | + image_width = self.slice_actor.GetInput().GetDimensions()[0] | |
131 | + axial = position / image_width | |
132 | + coronal = self.slice_data.number | |
133 | + sagital = position % image_width | |
134 | + | |
135 | + elif self.orientation == 'SAGITAL': | |
136 | + image_width = self.slice_actor.GetInput().GetDimensions()[1] | |
137 | + axial = position / image_width | |
138 | + coronal = position % image_width | |
139 | + sagital = self.slice_data.number | |
140 | + | |
141 | + return sagital, coronal, axial | |
142 | + | |
143 | + def ScrollSlice(self, coord): | |
144 | + if self.orientation == "AXIAL": | |
145 | + Publisher.sendMessage(('Set scroll position', 'SAGITAL'), | |
146 | + coord[0]) | |
147 | + Publisher.sendMessage(('Set scroll position', 'CORONAL'), | |
148 | + coord[1]) | |
149 | + elif self.orientation == "SAGITAL": | |
150 | + Publisher.sendMessage(('Set scroll position', 'AXIAL'), | |
151 | + coord[2]) | |
152 | + Publisher.sendMessage(('Set scroll position', 'CORONAL'), | |
153 | + coord[1]) | |
154 | + elif self.orientation == "CORONAL": | |
155 | + Publisher.sendMessage(('Set scroll position', 'AXIAL'), | |
156 | + coord[2]) | |
157 | + Publisher.sendMessage(('Set scroll position', 'SAGITAL'), | |
158 | + coord[0]) | |
159 | + | |
160 | + | |
24 | 161 | class ViewerStyle: |
25 | 162 | def __init__(self): |
26 | 163 | self.interactor = None | ... | ... |
invesalius/data/viewer_slice.py
... | ... | @@ -27,6 +27,8 @@ import numpy |
27 | 27 | import vtk |
28 | 28 | from vtk.wx.wxVTKRenderWindowInteractor import wxVTKRenderWindowInteractor |
29 | 29 | |
30 | +import styles | |
31 | + | |
30 | 32 | import wx |
31 | 33 | from wx.lib.pubsub import pub as Publisher |
32 | 34 | |
... | ... | @@ -173,127 +175,142 @@ class Viewer(wx.Panel): |
173 | 175 | interactor.SetInteractorStyle(style) |
174 | 176 | |
175 | 177 | def SetInteractorStyle(self, state): |
176 | - self.state = state | |
177 | - action = {const.SLICE_STATE_CROSS: | |
178 | - { | |
179 | - "MouseMoveEvent": self.OnCrossMove, | |
180 | - "LeftButtonPressEvent": self.OnCrossMouseClick, | |
181 | - }, | |
182 | - const.SLICE_STATE_EDITOR: | |
183 | - { | |
184 | - "MouseMoveEvent": self.OnBrushMove, | |
185 | - "LeftButtonPressEvent": self.OnBrushClick, | |
186 | - "LeftButtonReleaseEvent": self.OnBrushRelease, | |
187 | - "EnterEvent": self.OnEnterInteractor, | |
188 | - "LeaveEvent": self.OnLeaveInteractor | |
189 | - }, | |
190 | - const.STATE_PAN: | |
191 | - { | |
192 | - "MouseMoveEvent": self.OnPanMove, | |
193 | - "LeftButtonPressEvent": self.OnPanClick, | |
194 | - "LeftButtonReleaseEvent": self.OnVtkRightRelease | |
195 | - }, | |
196 | - const.STATE_SPIN: | |
197 | - { | |
198 | - "MouseMoveEvent": self.OnSpinMove, | |
199 | - "LeftButtonPressEvent": self.OnSpinClick, | |
200 | - "LeftButtonReleaseEvent": self.OnVtkRightRelease | |
201 | - }, | |
202 | - const.STATE_ZOOM: | |
203 | - { | |
204 | - "MouseMoveEvent": self.OnZoomMoveLeft, | |
205 | - "LeftButtonPressEvent": self.OnZoomLeftClick, | |
206 | - "LeftButtonReleaseEvent": self.OnVtkRightRelease | |
207 | - }, | |
208 | - const.SLICE_STATE_SCROLL: | |
209 | - { | |
210 | - "MouseMoveEvent": self.OnChangeSliceMove, | |
211 | - "LeftButtonPressEvent": self.OnChangeSliceClick, | |
212 | - }, | |
213 | - const.STATE_WL: | |
214 | - { | |
215 | - "MouseMoveEvent": self.OnWindowLevelMove, | |
216 | - "LeftButtonPressEvent": self.OnWindowLevelClick, | |
217 | - }, | |
218 | - const.STATE_DEFAULT: | |
219 | - { | |
220 | - }, | |
221 | - const.STATE_MEASURE_DISTANCE: | |
222 | - { | |
223 | - "LeftButtonPressEvent": self.OnInsertLinearMeasurePoint | |
224 | - }, | |
225 | - const.STATE_MEASURE_ANGLE: | |
226 | - { | |
227 | - "LeftButtonPressEvent": self.OnInsertAngularMeasurePoint | |
228 | - }, | |
229 | - } | |
178 | + if state == const.SLICE_STATE_CROSS: | |
179 | + style = styles.CrossInteractorStyle(self.orientation, | |
180 | + self.slice_data) | |
181 | + self.style = style | |
182 | + self.interactor.SetInteractorStyle(style) | |
183 | + self.interactor.Render() | |
230 | 184 | |
185 | + ## Zoom using right button | |
186 | + #style.AddObserver("RightButtonPressEvent",self.OnZoomRightClick) | |
187 | + #style.AddObserver("MouseMoveEvent", self.OnZoomMoveRight) | |
188 | + #style.AddObserver("RightButtonReleaseEvent", self.OnVtkRightRelease) | |
189 | + | |
190 | + #Scroll change slice | |
191 | + style.AddObserver("MouseWheelForwardEvent",self.OnScrollForward) | |
192 | + style.AddObserver("MouseWheelBackwardEvent", self.OnScrollBackward) | |
231 | 193 | |
232 | - if state == const.SLICE_STATE_CROSS: | |
233 | 194 | self.__set_cross_visibility(1) |
234 | 195 | Publisher.sendMessage('Activate ball reference') |
235 | 196 | else: |
236 | - self.__set_cross_visibility(0) | |
237 | - Publisher.sendMessage('Deactivate ball reference') | |
197 | + self.state = state | |
198 | + action = { | |
199 | + const.SLICE_STATE_EDITOR: | |
200 | + { | |
201 | + "MouseMoveEvent": self.OnBrushMove, | |
202 | + "LeftButtonPressEvent": self.OnBrushClick, | |
203 | + "LeftButtonReleaseEvent": self.OnBrushRelease, | |
204 | + "EnterEvent": self.OnEnterInteractor, | |
205 | + "LeaveEvent": self.OnLeaveInteractor | |
206 | + }, | |
207 | + const.STATE_PAN: | |
208 | + { | |
209 | + "MouseMoveEvent": self.OnPanMove, | |
210 | + "LeftButtonPressEvent": self.OnPanClick, | |
211 | + "LeftButtonReleaseEvent": self.OnVtkRightRelease | |
212 | + }, | |
213 | + const.STATE_SPIN: | |
214 | + { | |
215 | + "MouseMoveEvent": self.OnSpinMove, | |
216 | + "LeftButtonPressEvent": self.OnSpinClick, | |
217 | + "LeftButtonReleaseEvent": self.OnVtkRightRelease | |
218 | + }, | |
219 | + const.STATE_ZOOM: | |
220 | + { | |
221 | + "MouseMoveEvent": self.OnZoomMoveLeft, | |
222 | + "LeftButtonPressEvent": self.OnZoomLeftClick, | |
223 | + "LeftButtonReleaseEvent": self.OnVtkRightRelease | |
224 | + }, | |
225 | + const.SLICE_STATE_SCROLL: | |
226 | + { | |
227 | + "MouseMoveEvent": self.OnChangeSliceMove, | |
228 | + "LeftButtonPressEvent": self.OnChangeSliceClick, | |
229 | + }, | |
230 | + const.STATE_WL: | |
231 | + { | |
232 | + "MouseMoveEvent": self.OnWindowLevelMove, | |
233 | + "LeftButtonPressEvent": self.OnWindowLevelClick, | |
234 | + }, | |
235 | + const.STATE_DEFAULT: | |
236 | + { | |
237 | + }, | |
238 | + const.STATE_MEASURE_DISTANCE: | |
239 | + { | |
240 | + "LeftButtonPressEvent": self.OnInsertLinearMeasurePoint | |
241 | + }, | |
242 | + const.STATE_MEASURE_ANGLE: | |
243 | + { | |
244 | + "LeftButtonPressEvent": self.OnInsertAngularMeasurePoint | |
245 | + }, | |
246 | + } | |
247 | + | |
248 | + | |
249 | + if state == const.SLICE_STATE_CROSS: | |
250 | + self.__set_cross_visibility(1) | |
251 | + Publisher.sendMessage('Activate ball reference') | |
252 | + else: | |
253 | + self.__set_cross_visibility(0) | |
254 | + Publisher.sendMessage('Deactivate ball reference') | |
238 | 255 | |
239 | - if state == const.STATE_WL: | |
240 | - self.on_wl = True | |
241 | - self.wl_text.Show() | |
242 | - else: | |
243 | - self.on_wl = False | |
244 | - self.wl_text.Hide() | |
256 | + if state == const.STATE_WL: | |
257 | + self.on_wl = True | |
258 | + self.wl_text.Show() | |
259 | + else: | |
260 | + self.on_wl = False | |
261 | + self.wl_text.Hide() | |
245 | 262 | |
246 | - self.__set_editor_cursor_visibility(0) | |
247 | - | |
248 | - # Bind method according to current mode | |
249 | - if(state == const.STATE_ZOOM_SL): | |
250 | - style = vtk.vtkInteractorStyleRubberBandZoom() | |
263 | + self.__set_editor_cursor_visibility(0) | |
264 | + | |
265 | + # Bind method according to current mode | |
266 | + if(state == const.STATE_ZOOM_SL): | |
267 | + style = vtk.vtkInteractorStyleRubberBandZoom() | |
251 | 268 | |
252 | - style.AddObserver("RightButtonPressEvent", self.QuitRubberBandZoom) | |
253 | - #style.AddObserver("RightButtonPressEvent", self.EnterRubberBandZoom) | |
269 | + style.AddObserver("RightButtonPressEvent", self.QuitRubberBandZoom) | |
270 | + #style.AddObserver("RightButtonPressEvent", self.EnterRubberBandZoom) | |
254 | 271 | |
255 | - else: | |
256 | - style = vtk.vtkInteractorStyleImage() | |
257 | - | |
258 | - # Check each event available for each state | |
259 | - for event in action[state]: | |
260 | - # Bind event | |
261 | - style.AddObserver(event, | |
262 | - action[state][event]) | |
263 | - | |
264 | - # Common to all styles | |
265 | - # Mouse Buttons' presses / releases | |
266 | - style.AddObserver("LeftButtonPressEvent", self.OnLeftClick) | |
267 | - style.AddObserver("LeftButtonReleaseEvent", self.OnReleaseLeftButton) | |
268 | - style.AddObserver("RightButtonPressEvent", self.OnRightClick) | |
269 | - style.AddObserver("RightButtonReleaseEvent", self.OnReleaseRightButton) | |
270 | - | |
271 | - # Zoom using right button | |
272 | - style.AddObserver("RightButtonPressEvent",self.OnZoomRightClick) | |
273 | - style.AddObserver("MouseMoveEvent", self.OnZoomMoveRight) | |
274 | - style.AddObserver("RightButtonReleaseEvent", self.OnVtkRightRelease) | |
275 | - | |
276 | - #Scroll change slice | |
277 | - style.AddObserver("MouseWheelForwardEvent",self.OnScrollForward) | |
278 | - style.AddObserver("MouseWheelBackwardEvent", self.OnScrollBackward) | |
279 | - | |
280 | - if ((state == const.STATE_ZOOM) or (state == const.STATE_ZOOM_SL)): | |
281 | - self.interactor.Bind(wx.EVT_LEFT_DCLICK, self.OnUnZoom) | |
282 | - else: | |
283 | - self.interactor.Bind(wx.EVT_LEFT_DCLICK, self.OnUnSpinPan) | |
272 | + else: | |
273 | + style = vtk.vtkInteractorStyleImage() | |
274 | + | |
275 | + # Check each event available for each state | |
276 | + for event in action[state]: | |
277 | + # Bind event | |
278 | + style.AddObserver(event, | |
279 | + action[state][event]) | |
280 | + | |
281 | + # Common to all styles | |
282 | + # Mouse Buttons' presses / releases | |
283 | + style.AddObserver("LeftButtonPressEvent", self.OnLeftClick) | |
284 | + style.AddObserver("LeftButtonReleaseEvent", self.OnReleaseLeftButton) | |
285 | + style.AddObserver("RightButtonPressEvent", self.OnRightClick) | |
286 | + style.AddObserver("RightButtonReleaseEvent", self.OnReleaseRightButton) | |
287 | + | |
288 | + # Zoom using right button | |
289 | + style.AddObserver("RightButtonPressEvent",self.OnZoomRightClick) | |
290 | + style.AddObserver("MouseMoveEvent", self.OnZoomMoveRight) | |
291 | + style.AddObserver("RightButtonReleaseEvent", self.OnVtkRightRelease) | |
292 | + | |
293 | + #Scroll change slice | |
294 | + style.AddObserver("MouseWheelForwardEvent",self.OnScrollForward) | |
295 | + style.AddObserver("MouseWheelBackwardEvent", self.OnScrollBackward) | |
296 | + | |
297 | + if ((state == const.STATE_ZOOM) or (state == const.STATE_ZOOM_SL)): | |
298 | + self.interactor.Bind(wx.EVT_LEFT_DCLICK, self.OnUnZoom) | |
299 | + else: | |
300 | + self.interactor.Bind(wx.EVT_LEFT_DCLICK, self.OnUnSpinPan) | |
284 | 301 | |
285 | - # Measures are using vtkPropPicker because they need to get which actor | |
286 | - # was picked. | |
287 | - if state in (const.STATE_MEASURE_DISTANCE, const.STATE_MEASURE_ANGLE): | |
288 | - self.pick = vtk.vtkPropPicker() | |
289 | - self.interactor.SetPicker(self.pick) | |
290 | - else: | |
291 | - self.pick = vtk.vtkWorldPointPicker() | |
292 | - self.interactor.SetPicker(self.pick) | |
302 | + # Measures are using vtkPropPicker because they need to get which actor | |
303 | + # was picked. | |
304 | + if state in (const.STATE_MEASURE_DISTANCE, const.STATE_MEASURE_ANGLE): | |
305 | + self.pick = vtk.vtkPropPicker() | |
306 | + self.interactor.SetPicker(self.pick) | |
307 | + else: | |
308 | + self.pick = vtk.vtkWorldPointPicker() | |
309 | + self.interactor.SetPicker(self.pick) | |
293 | 310 | |
294 | - self.style = style | |
295 | - self.interactor.SetInteractorStyle(style) | |
296 | - self.interactor.Render() | |
311 | + self.style = style | |
312 | + self.interactor.SetInteractorStyle(style) | |
313 | + self.interactor.Render() | |
297 | 314 | |
298 | 315 | def QuitRubberBandZoom(self, evt, obj): |
299 | 316 | style = vtk.vtkInteractorStyleImage() |
... | ... | @@ -808,37 +825,6 @@ class Viewer(wx.Panel): |
808 | 825 | self.slice_.apply_slice_buffer_to_mask(self.orientation) |
809 | 826 | self._flush_buffer = False |
810 | 827 | |
811 | - def OnCrossMouseClick(self, evt, obj): | |
812 | - self.ChangeCrossPosition() | |
813 | - | |
814 | - def OnCrossMove(self, evt, obj): | |
815 | - # The user moved the mouse with left button pressed | |
816 | - if self.left_pressed: | |
817 | - self.ChangeCrossPosition() | |
818 | - | |
819 | - def ChangeCrossPosition(self): | |
820 | - mouse_x, mouse_y = self.interactor.GetEventPosition() | |
821 | - renderer = self.slice_data.renderer | |
822 | - self.pick.Pick(mouse_x, mouse_y, 0, renderer) | |
823 | - | |
824 | - # Get in what slice data the click occurred | |
825 | - # pick to get click position in the 3d world | |
826 | - coord_cross = self.get_coordinate_cursor() | |
827 | - position = self.slice_data.actor.GetInput().FindPoint(coord_cross) | |
828 | - # Forcing focal point to be setted in the center of the pixel. | |
829 | - coord_cross = self.slice_data.actor.GetInput().GetPoint(position) | |
830 | - | |
831 | - coord = self.calcultate_scroll_position(position) | |
832 | - self.ScrollSlice(coord) | |
833 | - | |
834 | - Publisher.sendMessage('Update cross position', coord_cross) | |
835 | - Publisher.sendMessage('Set ball reference position based on bound', | |
836 | - coord_cross) | |
837 | - Publisher.sendMessage('Set camera in volume', coord_cross) | |
838 | - Publisher.sendMessage('Render volume viewer') | |
839 | - | |
840 | - self.interactor.Render() | |
841 | - | |
842 | 828 | def Navigation(self, pubsub_evt): |
843 | 829 | # Get point from base change |
844 | 830 | x, y, z = pubsub_evt.data | ... | ... |