Commit e4409cea4d000886cca7ec3a60a3acb7dd82a9cb

Authored by tatiana
1 parent f0ac71e4

FIX: Volume interaction, ENH: Slice interaction

.gitattributes
... ... @@ -144,6 +144,7 @@ invesalius/reader/dicom.py -text
144 144 invesalius/reader/dicom_grouper.py -text
145 145 invesalius/reader/dicom_reader.py -text
146 146 invesalius/session.py -text
  147 +invesalius/style.py -text
147 148 invesalius/utils.py -text
148 149 invesalius/version.py -text
149 150 presets/raycasting/Airways[!!-~]II.plist -text
... ...
invesalius/constants.py
... ... @@ -239,7 +239,7 @@ ID_TO_BMP = {VOL_FRONT: ["Front", os.path.join(ICON_DIR, "view_front.png")],
239 239 }
240 240  
241 241 # if 1, use vtkVolumeRaycastMapper, if 0, use vtkFixedPointVolumeRayCastMapper
242   -TYPE_RAYCASTING_MAPPER = 0
  242 +TYPE_RAYCASTING_MAPPER = 1
243 243  
244 244 folder=RAYCASTING_PRESETS_DIRECTORY= os.path.abspath(os.path.join("..",
245 245 "presets",
... ... @@ -334,25 +334,45 @@ ID_PRINT_SCREENSHOT, ID_EXIT] = [wx.NewId() for number in range(10)]
334 334 [ID_VIEW_FULL, ID_VIEW_TEXT, ID_VIEW_3D_BACKGROUND] =\
335 335 [wx.NewId() for number in range(3)]
336 336  
337   -
  337 +ID_ABOUT = wx.NewId()
338 338  
339 339 #---------------------------------------------------------
340   -STATE_DEFAULT = 0
341   -SLICE_STATE_EDITOR = 1
342   -STATE_WL = 2
343   -STATE_SPIN = 3
344   -STATE_ZOOM = 4
345   -STATE_ZOOM_SL = 5
346   -SLICE_STATE_CROSS = 6
347   -SLICE_STATE_SCROLL = 7
348   -STATE_PAN = 8
349   -
350   -LEVEL = {STATE_DEFAULT: 0,
351   - SLICE_STATE_EDITOR: 1,
352   - STATE_WL: 2,
353   - STATE_SPIN: 2,
354   - STATE_ZOOM: 2,
355   - STATE_ZOOM_SL: 2,
356   - SLICE_STATE_CROSS: 2,
357   - SLICE_STATE_SCROLL: 2,
358   - STATE_PAN:2}
  340 +STATE_DEFAULT = 1000
  341 +STATE_WL = 1001
  342 +STATE_SPIN = 1002
  343 +STATE_ZOOM = 1003
  344 +STATE_ZOOM_SL = 1004
  345 +STATE_PAN = 1005
  346 +SLICE_STATE_CROSS = 1006
  347 +SLICE_STATE_SCROLL = 1007
  348 +SLICE_STATE_EDITOR = 1008
  349 +
  350 +
  351 +#STATE_DEFAULT = wx.NewId()
  352 +
  353 +TOOL_STATES = [ STATE_WL, STATE_SPIN, STATE_ZOOM,
  354 + STATE_ZOOM_SL, STATE_PAN] #=\
  355 +# [wx.NewId() for number in range(5)]
  356 +
  357 +TOOL_SLICE_STATES = [SLICE_STATE_CROSS, SLICE_STATE_SCROLL]# =\
  358 +# [wx.NewId() for number in range(2)]
  359 +
  360 +#SLICE_STATE_EDITOR = wx.NewId()
  361 +
  362 +SLICE_STYLES = TOOL_STATES + TOOL_SLICE_STATES
  363 +SLICE_STYLES.append(STATE_DEFAULT)
  364 +SLICE_STYLES.append(SLICE_STATE_EDITOR)
  365 +
  366 +VOLUME_STYLES = TOOL_STATES + []
  367 +VOLUME_STYLES.append(STATE_DEFAULT)
  368 +
  369 +
  370 +STYLE_LEVEL = {SLICE_STATE_EDITOR: 1,
  371 + SLICE_STATE_CROSS: 2,
  372 + SLICE_STATE_SCROLL: 2,
  373 + STATE_DEFAULT: 0,
  374 + STATE_WL: 2,
  375 + STATE_SPIN: 2,
  376 + STATE_ZOOM: 2,
  377 + STATE_ZOOM_SL: 2,
  378 + STATE_PAN:2}
... ...
invesalius/data/cursor_actors.py
... ... @@ -34,7 +34,7 @@ class CursorCircle:
34 34 self.colour = (0.0, 0.0, 1.0)
35 35 self.opacity = 1
36 36 self.radius = 15.0
37   - self.position = (0 ,0, 1)
  37 + #self.position = (0.5,0.5, 1)
38 38 self.points = []
39 39 self.orientation = "AXIAL"
40 40 self.spacing = (1, 1, 1)
... ... @@ -64,8 +64,8 @@ class CursorCircle:
64 64 actor.SetMapper(mapper)
65 65 actor.GetProperty().SetOpacity(self.opacity)
66 66 actor.GetProperty().SetColor(self.colour)
67   - actor.SetPosition(self.position)
68   - actor.SetVisibility(1)
  67 + #actor.SetPosition(self.position)
  68 + actor.SetVisibility(0)
69 69 actor.PickableOff()
70 70  
71 71 def __calculate_area_pixels(self):
... ... @@ -209,7 +209,7 @@ class CursorRectangle:
209 209 self.y_length = 30
210 210  
211 211 self.dimension = (self.x_length, self.y_length)
212   - self.position = (0 ,0)
  212 + #self.position = (0 ,0)
213 213 self.orientation = "AXIAL"
214 214 self.spacing = (1, 1, 1)
215 215  
... ... @@ -287,7 +287,7 @@ class CursorRectangle:
287 287 actor.SetMapper(mapper)
288 288 actor.GetProperty().SetOpacity(self.opacity)
289 289 actor.GetProperty().SetColor(self.colour)
290   - actor.SetVisibility(1)
  290 + actor.SetVisibility(0)
291 291  
292 292 def __calculate_area_pixels(self):
293 293 xc = 0
... ...
invesalius/data/slice_.py
... ... @@ -24,7 +24,7 @@ import wx.lib.pubsub as ps
24 24 import constants as const
25 25 import imagedata_utils as iu
26 26 from mask import Mask
27   -import mode as md
  27 +import style as st
28 28 from project import Project
29 29 import session as ses
30 30 from utils import Singleton
... ... @@ -42,7 +42,7 @@ class Slice(object):
42 42 self.blend_filter = None
43 43  
44 44 self.num_gradient = 0
45   - self.mode = md.SliceMode()
  45 + self.interaction_style = st.StyleStateManager()
46 46  
47 47 self.__bind_events()
48 48  
... ... @@ -89,6 +89,25 @@ class Slice(object):
89 89  
90 90 ps.Publisher().subscribe(self.OnCloseProject, 'Close project data')
91 91  
  92 +
  93 +
  94 + ps.Publisher().subscribe(self.OnEnableStyle, 'Enable style')
  95 + ps.Publisher().subscribe(self.OnDisableStyle, 'Disable style')
  96 +
  97 +
  98 + def OnEnableStyle(self, pubsub_evt):
  99 + state = pubsub_evt.data
  100 + if (state in const.SLICE_STYLES):
  101 + new_state = self.interaction_style.AddState(state)
  102 + ps.Publisher().sendMessage('Set slice interaction style', new_state)
  103 +
  104 +
  105 + def OnDisableStyle(self, pubsub_evt):
  106 + state = pubsub_evt.data
  107 + if (state in const.SLICE_STYLES):
  108 + new_state = self.interaction_style.RemoveState(state)
  109 + ps.Publisher().sendMessage('Set slice interaction style', new_state)
  110 +
92 111 def OnCloseProject(self, pubsub_evt):
93 112 self.CloseProject()
94 113  
... ...
invesalius/data/slice_data.py
... ... @@ -33,6 +33,8 @@ class SliceData(object):
33 33 def __init__(self):
34 34 self.actor = None
35 35 self.cursor = None
  36 + self.text = None
  37 +
36 38 self.number = 0
37 39 self.orientation = 'AXIAL'
38 40 self.renderer = None
... ... @@ -48,6 +50,7 @@ class SliceData(object):
48 50 text.SetPosition(const.TEXT_POS_LEFT_DOWN)
49 51 text.SetVerticalJustificationToBottom()
50 52 text.SetValue(self.number)
  53 + #text.ShadowOff()
51 54 self.text = text
52 55  
53 56 def __create_line_actor(self, line):
... ...
invesalius/data/viewer_slice.py
... ... @@ -49,7 +49,7 @@ class Viewer(wx.Panel):
49 49 #self.SetBackgroundColour(colour)
50 50  
51 51 # Interactor additional style
52   - self.modes = []#['DEFAULT']
  52 + #self.modes = []#['DEFAULT']
53 53 self.left_pressed = 0
54 54 self.right_pressed = 0
55 55 self.last_position_mouse_move = ()
... ... @@ -60,6 +60,7 @@ class Viewer(wx.Panel):
60 60 # is the number of rows
61 61 self.layout = (1, 1)
62 62 self.orientation_texts = []
  63 +
63 64 self.__init_gui()
64 65  
65 66 self.orientation = orientation
... ... @@ -74,6 +75,7 @@ class Viewer(wx.Panel):
74 75 # VTK pipeline and actors
75 76 #self.__config_interactor()
76 77 self.pick = vtk.vtkPropPicker()
  78 + self.cross_actor = vtk.vtkActor()
77 79  
78 80 self.__bind_events()
79 81 self.__bind_events_wx()
... ... @@ -152,7 +154,7 @@ class Viewer(wx.Panel):
152 154 self.ren = ren
153 155  
154 156  
155   - def SetState(self, state):
  157 + def SetInteractorStyle(self, state):
156 158 self.state = state
157 159 action = {const.SLICE_STATE_CROSS:
158 160 {
... ... @@ -198,6 +200,13 @@ class Viewer(wx.Panel):
198 200 {
199 201 }
200 202 }
  203 + if state == const.SLICE_STATE_CROSS:
  204 + self.__set_cross_visibility(1)
  205 + else:
  206 + self.__set_cross_visibility(0)
  207 +
  208 + self.__set_editor_cursor_visibility(0)
  209 +
201 210  
202 211 # Bind method according to current mode
203 212 if(state == const.STATE_ZOOM_SL):
... ... @@ -238,6 +247,7 @@ class Viewer(wx.Panel):
238 247  
239 248 self.style = style
240 249 self.interactor.SetInteractorStyle(style)
  250 + self.interactor.Render()
241 251  
242 252 def QuitRubberBandZoom(self, evt, obj):
243 253 style = vtk.vtkInteractorStyleImage()
... ... @@ -599,7 +609,9 @@ class Viewer(wx.Panel):
599 609 ps.Publisher().sendMessage('Update slice viewer')
600 610  
601 611 def OnBrushMove(self, evt, obj):
602   -
  612 +
  613 + self.__set_editor_cursor_visibility(1)
  614 +
603 615 mouse_x, mouse_y = self.interactor.GetEventPosition()
604 616 render = self.interactor.FindPokedRenderer(mouse_x, mouse_y)
605 617 slice_data = self.get_slice_data(render)
... ... @@ -786,14 +798,14 @@ class Viewer(wx.Panel):
786 798 ps.Publisher().subscribe(self.UpdateWindowLevelValue,\
787 799 'Update window level value')
788 800  
789   - ps.Publisher().subscribe(self.__set_cross_visibility,\
790   - 'Set cross visibility')
  801 + #ps.Publisher().subscribe(self.__set_cross_visibility,\
  802 + # 'Set cross visibility')
791 803 ###
792 804 ps.Publisher().subscribe(self.__set_layout,
793 805 'Set slice viewer layout')
794 806  
795   - ps.Publisher().subscribe(self.OnSetMode,
796   - 'Set slice mode')
  807 + ps.Publisher().subscribe(self.OnSetInteractorStyle,
  808 + 'Set slice interaction style')
797 809 ps.Publisher().subscribe(self.OnCloseProject, 'Close project data')
798 810  
799 811 def OnCloseProject(self, pubsub_evt):
... ... @@ -814,9 +826,9 @@ class Viewer(wx.Panel):
814 826 self.pick = vtk.vtkPropPicker()
815 827  
816 828  
817   - def OnSetMode(self, pubsub_evt):
  829 + def OnSetInteractorStyle(self, pubsub_evt):
818 830 state = pubsub_evt.data
819   - self.SetState(state)
  831 + self.SetInteractorStyle(state)
820 832  
821 833  
822 834 def ChangeBrushOperation(self, pubsub_evt):
... ... @@ -834,6 +846,7 @@ class Viewer(wx.Panel):
834 846 def LoadImagedata(self, pubsub_evt):
835 847 imagedata, mask_dict = pubsub_evt.data
836 848 self.SetInput(imagedata, mask_dict)
  849 +
837 850  
838 851 def LoadRenderers(self, imagedata):
839 852 number_renderers = self.layout[0] * self.layout[1]
... ... @@ -911,7 +924,7 @@ class Viewer(wx.Panel):
911 924 slice_ = sl.Slice()
912 925 if slice_.imagedata is None:
913 926 slice_.SetInput(imagedata, mask_dict)
914   -
  927 +
915 928 #actor = vtk.vtkImageActor()
916 929 #actor.SetInput(slice_.GetOutput())
917 930 self.LoadRenderers(slice_.GetOutput())
... ... @@ -940,7 +953,7 @@ class Viewer(wx.Panel):
940 953  
941 954 self.EnableText()
942 955 # Insert cursor
943   - self.SetState(const.STATE_DEFAULT)
  956 + self.SetInteractorStyle(const.STATE_DEFAULT)
944 957  
945 958 self.__build_cross_lines()
946 959  
... ... @@ -1074,10 +1087,12 @@ class Viewer(wx.Panel):
1074 1087 #print "actor bounds", slice_data.actor.GetBounds()
1075 1088 #print
1076 1089  
1077   - def __set_cross_visibility(self, pubsub_evt):
1078   - visibility = pubsub_evt.data
  1090 + def __set_cross_visibility(self, visibility):
1079 1091 self.cross_actor.SetVisibility(visibility)
1080   - self.interactor.Render()
  1092 +
  1093 + def __set_editor_cursor_visibility(self, visibility):
  1094 + for slice_data in self.slice_data_list:
  1095 + slice_data.cursor.actor.SetVisibility(visibility)
1081 1096  
1082 1097 def __update_cursor_position(self, slice_data, position):
1083 1098 x, y, z = position
... ...
invesalius/data/viewer_volume.py
... ... @@ -28,12 +28,15 @@ import constants as const
28 28 import project as prj
29 29 import data.vtk_utils as vtku
30 30 from gui.widgets.clut_raycasting import CLUTRaycastingWidget
  31 +import style as st
31 32  
32 33 class Viewer(wx.Panel):
33 34 def __init__(self, parent):
34 35 wx.Panel.__init__(self, parent, size=wx.Size(320, 320))
35 36 self.SetBackgroundColour(wx.Colour(0, 0, 0))
36 37  
  38 + self.interaction_style = st.StyleStateManager()
  39 +
37 40 style = vtk.vtkInteractorStyleTrackballCamera()
38 41 self.style = style
39 42  
... ... @@ -74,44 +77,53 @@ class Viewer(wx.Panel):
74 77  
75 78 self.mouse_pressed = 0
76 79  
77   - def SetMode(self, new_mode):
78   -
79   - action = {
80   - 'PAN':{
81   - "MouseMoveEvent": self.OnPanMove,
82   - "LeftButtonPressEvent": self.OnPanClick,
83   - "LeftButtonReleaseEvent": self.OnReleasePanClick
84   - },
85   - 'ZOOM':{
86   - "MouseMoveEvent": self.OnZoomMove,
87   - "LeftButtonPressEvent": self.OnZoomClick,
88   - "LeftButtonReleaseEvent": self.OnReleaseZoomClick,
89   - },
90   - 'SPIN':{
91   - "MouseMoveEvent": self.OnSpinMove,
92   - "LeftButtonPressEvent": self.OnSpinClick,
93   - "LeftButtonReleaseEvent": self.OnReleaseSpinClick,
94   - },
95   - 'WINDOWLEVEL':{
96   - "MouseMoveEvent": self.OnWindowLevelMove,
97   - "LeftButtonPressEvent": self.OnWindowLevelClick,
98   - "LeftButtonReleaseEvent":self.OnWindowLevelRelease
99   - }
  80 + def SetInteractorStyle(self, state):
  81 + action = {
  82 + const.STATE_PAN:
  83 + {
  84 + "MouseMoveEvent": self.OnPanMove,
  85 + "LeftButtonPressEvent": self.OnPanClick,
  86 + "LeftButtonReleaseEvent": self.OnReleasePanClick
  87 + },
  88 + const.STATE_ZOOM:
  89 + {
  90 + "MouseMoveEvent": self.OnZoomMove,
  91 + "LeftButtonPressEvent": self.OnZoomClick,
  92 + "LeftButtonReleaseEvent": self.OnReleaseZoomClick,
  93 + },
  94 + #const.STATE_SPIN:
  95 + # {
  96 + # "MouseMoveEvent": self.OnSpinMove,
  97 + # "LeftButtonPressEvent": self.OnSpinClick,
  98 + # "LeftButtonReleaseEvent": self.OnReleaseSpinClick,
  99 + # },
  100 + const.STATE_SPIN:
  101 + {
  102 + },
  103 + const.STATE_WL:
  104 + {
  105 + "MouseMoveEvent": self.OnWindowLevelMove,
  106 + "LeftButtonPressEvent": self.OnWindowLevelClick,
  107 + "LeftButtonReleaseEvent":self.OnWindowLevelRelease
  108 + },
  109 + const.STATE_DEFAULT:
  110 + {
  111 + }
100 112 }
101 113  
102   - if (new_mode == 'ZOOMSELECT'):
103   - style = vtk.vtkInteractorStyleRubberBandZoom()
104   - self.interactor.SetInteractorStyle(style)
105   - self.style = style
106   - else:
107   - style = vtk.vtkInteractorStyleTrackballCamera()
108   - self.interactor.SetInteractorStyle(style)
109   - self.style = style
  114 + if (state == const.STATE_ZOOM_SL):
  115 + style = vtk.vtkInteractorStyleRubberBandZoom()
  116 + self.interactor.SetInteractorStyle(style)
  117 + self.style = style
  118 + else:
  119 + style = vtk.vtkInteractorStyleTrackballCamera()
  120 + self.interactor.SetInteractorStyle(style)
  121 + self.style = style
110 122  
111   - # Check each event available for each mode
112   - for event in action[new_mode]:
113   - # Bind event
114   - style.AddObserver(event,action[new_mode][event])
  123 + # Check each event available for each mode
  124 + for event in action[state]:
  125 + # Bind event
  126 + style.AddObserver(event,action[state][event])
115 127  
116 128 def OnSpinMove(self, evt, obj):
117 129 if (self.mouse_pressed):
... ... @@ -153,6 +165,22 @@ class Viewer(wx.Panel):
153 165 self.mouse_pressed = 0
154 166 evt.EndPan()
155 167  
  168 + def SetStyle(self, pubsub_evt):
  169 + print "SetStyle"
  170 + mode = pubsub_evt.data
  171 +
  172 + if (mode == const.MODE_ZOOM_SELECTION):
  173 + self.SetMode('ZOOMSELECT')
  174 + elif(mode == const.MODE_MOVE):
  175 + self.SetMode('PAN')
  176 + elif(mode == const.MODE_ZOOM):
  177 + self.SetMode('ZOOM')
  178 + elif(mode == const.MODE_ROTATE):
  179 + self.SetMode('SPIN')
  180 + elif(mode == const.MODE_WW_WL):
  181 + self.SetMode('WINDOWLEVEL')
  182 +
  183 +
156 184 def SetNewMode(self, pubsub_evt):
157 185 mode = pubsub_evt.topic[1]
158 186  
... ... @@ -262,20 +290,26 @@ class Viewer(wx.Panel):
262 290  
263 291 ps.Publisher().subscribe(self.ResetCamClippingRange, 'Reset cam clipping range')
264 292  
265   - ps.Publisher().subscribe(self.SetNewMode,
266   - ('Set interaction mode',
267   - const.MODE_ZOOM_SELECTION))
268   - ps.Publisher().subscribe(self.SetNewMode,
269   - ('Set interaction mode',
270   - const.MODE_ZOOM))
271   -
272   - ps.Publisher().subscribe(self.SetNewMode,
273   - ('Set interaction mode',
274   - const.MODE_MOVE))
275 293  
276   - ps.Publisher().subscribe(self.SetNewMode,
277   - ('Set interaction mode',
278   - const.MODE_ROTATE))
  294 + ps.Publisher().subscribe(self.OnEnableStyle, 'Enable style')
  295 + ps.Publisher().subscribe(self.OnDisableStyle, 'Disable style')
  296 +
  297 +
  298 + def OnEnableStyle(self, pubsub_evt):
  299 + state = pubsub_evt.data
  300 + if (state in const.VOLUME_STYLES):
  301 + new_state = self.interaction_style.AddState(state)
  302 + self.SetInteractorStyle(new_state)
  303 + else:
  304 + #level = const.STYLE_LEVEL[state]
  305 + new_state = self.interaction_style.RemoveState(state)
  306 + self.SetInteractorStyle(new_state)
  307 +
  308 + def OnDisableStyle(self, pubsub_evt):
  309 + state = pubsub_evt.data
  310 + new_state = self.interaction_style.RemoveState(state)
  311 + self.SetInteractorStyle(new_state)
  312 +
279 313  
280 314 def ResetCamClippingRange(self, pubsub_evt):
281 315 self.ren.ResetCamera()
... ... @@ -680,4 +714,4 @@ class SlicePlane:
680 714 x,y = evt.GetLastEventPosition()
681 715 self.picker.Pick(x, y, 0, self.ren1)
682 716 point_id = self.picker.GetPointId()
683   -
684 717 \ No newline at end of file
  718 +
... ...
invesalius/gui/default_tasks.py
... ... @@ -19,6 +19,7 @@
19 19  
20 20 import wx
21 21 import wx.lib.foldpanelbar as fpb
  22 +import wx.lib.pubsub as ps
22 23  
23 24 import task_exporter as exporter
24 25 import task_slice as slice_
... ... @@ -182,9 +183,11 @@ class UpperTaskPanel(wx.Panel):
182 183 collapsed=True, foldIcons=image_list)
183 184 style = fold_panel.GetCaptionStyle(item)
184 185 col = style.GetFirstColour()
185   -
186   - fold_panel.AddFoldPanelWindow(item, slice_.TaskPanel(item), Spacing= 0,
  186 + slice_panel = slice_.TaskPanel(item)
  187 + fold_panel.AddFoldPanelWindow(item, slice_panel, Spacing= 0,
187 188 leftSpacing=0, rightSpacing=0)
  189 + self.__id_slice = item.GetId()
  190 + self.slice_panel = slice_panel
188 191 #fold_panel.Expand(fold_panel.GetFoldPanel(1))
189 192  
190 193 # Fold 3
... ... @@ -211,3 +214,21 @@ class UpperTaskPanel(wx.Panel):
211 214  
212 215 fold_panel.AddFoldPanelWindow(item, exporter.TaskPanel(item),
213 216 Spacing= 0, leftSpacing=0, rightSpacing=0)
  217 +
  218 + self.fold_panel = fold_panel
  219 + self.__bind_evt()
  220 +
  221 + def __bind_evt(self):
  222 + self.fold_panel.Bind(fpb.EVT_CAPTIONBAR, self.OnFoldPressCaption)
  223 +
  224 + def OnFoldPressCaption(self, evt):
  225 + id = evt.GetTag().GetId()
  226 + closed = evt.GetFoldStatus()
  227 +
  228 + if self.__id_slice == id:
  229 + ps.Publisher().sendMessage('Retrieve task slice style')
  230 + else:
  231 + ps.Publisher().sendMessage('Disable task slice style')
  232 +
  233 +
  234 + evt.Skip()
... ...
invesalius/gui/frame.py
... ... @@ -34,18 +34,18 @@ import project as prj
34 34 import session as ses
35 35  
36 36 # Object toolbar
37   -OBJ_TOOLS = [ID_ZOOM, ID_ZOOM_SELECT, ID_ROTATE, ID_MOVE,
38   -ID_CONTRAST] = [wx.NewId() for number in range(5)]
39   -MODE_BY_ID = {ID_ZOOM: const.STATE_ZOOM,
40   - ID_ZOOM_SELECT: const.STATE_ZOOM_SL,
41   - ID_ROTATE: const.STATE_SPIN,
42   - ID_MOVE: const.STATE_PAN,
43   - ID_CONTRAST: const.STATE_WL}
  37 +#OBJ_TOOLS = [ID_ZOOM, ID_ZOOM_SELECT, ID_ROTATE, ID_MOVE,
  38 +#ID_CONTRAST] = [wx.NewId() for number in range(5)]
  39 +#MODE_BY_ID = {ID_ZOOM: const.STATE_ZOOM,
  40 +# ID_ZOOM_SELECT: const.STATE_ZOOM_SL,
  41 +# ID_ROTATE: const.STATE_SPIN,
  42 +# ID_MOVE: const.STATE_PAN,
  43 +# ID_CONTRAST: const.STATE_WL}
44 44  
45 45 # Slice toolbar
46   -SLICE_TOOLS = [ID_SLICE_SCROLL, ID_CROSS] = [wx.NewId() for number in range(2)]
47   -SLICE_MODE_BY_ID = {ID_SLICE_SCROLL: const.SLICE_STATE_SCROLL,
48   - ID_CROSS: const.SLICE_STATE_CROSS}
  46 +#SLICE_TOOLS = [ID_SLICE_SCROLL, ID_CROSS] = [wx.NewId() for number in range(2)]
  47 +#SLICE_MODE_BY_ID = {ID_SLICE_SCROLL: const.SLICE_STATE_SCROLL,
  48 +# ID_CROSS: const.SLICE_STATE_CROSS}
49 49  
50 50 # Layout toolbar
51 51 VIEW_TOOLS = [ID_LAYOUT, ID_TEXT] = [wx.NewId() for number in range(2)]
... ... @@ -110,6 +110,7 @@ class Frame(wx.Frame):
110 110 def __bind_events_wx(self):
111 111 self.Bind(wx.EVT_SIZE, self.OnSize)
112 112 self.Bind(wx.EVT_MENU, self.OnMenuClick)
  113 + #self.Bind(wx.EVT_CLOSE, self.OnExit)
113 114  
114 115 def __init_aui(self):
115 116  
... ... @@ -236,8 +237,13 @@ class Frame(wx.Frame):
236 237 self.SaveAsProject()
237 238 elif id == const.ID_PROJECT_CLOSE:
238 239 self.CloseProject()
239   - elif id == const.ID_EXIT:
240   - self.Exit()
  240 + #elif id == const.ID_EXIT:
  241 + # self.OnExit(evt)
  242 + elif id == const.ID_ABOUT:
  243 + self.ShowAbout()
  244 +
  245 + def ShowAbout(self):
  246 + dlg.ShowAboutDialog(self)
241 247  
242 248 def ImportDicom(self):
243 249 ps.Publisher().sendMessage('Show import directory dialog')
... ... @@ -252,10 +258,17 @@ class Frame(wx.Frame):
252 258 ps.Publisher().sendMessage('Show save dialog', False)
253 259  
254 260 def CloseProject(self):
  261 + print "CloseProject"
255 262 ps.Publisher().sendMessage('Close Project')
256 263  
  264 + def OnExit(self, event):
  265 + print "OnExit"
  266 + self.Exit()
  267 + event.Skip()
  268 +
257 269 def Exit(self):
258   - print "TODO: Exit"
  270 + print "Exit"
  271 + ps.Publisher().sendMessage('Close Project')
259 272  
260 273 def ShowTask(self, pubsub_evt):
261 274 self.aui_manager.GetPane("Tasks").Show()
... ... @@ -343,7 +356,7 @@ class MenuBar(wx.MenuBar):
343 356 help_menu.Append(105, "Getting Started...")
344 357 #help_menu.Append(108, "User Manual...")
345 358 help_menu.AppendSeparator()
346   - help_menu.Append(106, "About...")
  359 + help_menu.Append(const.ID_ABOUT, "About...")
347 360 #help_menu.Append(107, "Check For Updates Now...")
348 361  
349 362 # TODO: Check what is necessary under MacOS to show Groo and not Python
... ... @@ -422,7 +435,7 @@ class StatusBar(wx.StatusBar):
422 435 self.SetStatusText(label, 0)
423 436 if (int(value) >= 99):
424 437 self.SetStatusText("",0)
425   - if sys.platform != 'linux2':
  438 + if sys.platform == 'win32':
426 439 wx.SafeYield()
427 440  
428 441  
... ... @@ -606,25 +619,25 @@ class ObjectToolBar(wx.ToolBar):
606 619 "tool_contrast.png"),
607 620 wx.BITMAP_TYPE_PNG)
608 621  
609   - self.AddLabelTool(ID_ZOOM,
  622 + self.AddLabelTool(const.STATE_ZOOM,
610 623 "Zoom",
611 624 BMP_ZOOM,
612 625 kind = wx.ITEM_CHECK)
613 626  
614   - self.AddLabelTool(ID_ZOOM_SELECT,
  627 + self.AddLabelTool(const.STATE_ZOOM_SL,
615 628 "Zoom based on selection",
616 629 BMP_ZOOM_SELECT,
617 630 kind = wx.ITEM_CHECK)
618 631  
619   - self.AddLabelTool(ID_ROTATE,
  632 + self.AddLabelTool(const.STATE_SPIN,
620 633 "Rotate", BMP_ROTATE,
621 634 kind = wx.ITEM_CHECK)
622 635  
623   - self.AddLabelTool(ID_MOVE,
  636 + self.AddLabelTool(const.STATE_PAN,
624 637 "Move", BMP_MOVE,
625 638 kind = wx.ITEM_CHECK)
626 639  
627   - self.AddLabelTool(ID_CONTRAST,
  640 + self.AddLabelTool(const.STATE_WL,
628 641 "Window and Level", BMP_CONTRAST,
629 642 kind = wx.ITEM_CHECK)
630 643 self.Realize()
... ... @@ -641,17 +654,12 @@ class ObjectToolBar(wx.ToolBar):
641 654 id = evt.GetId()
642 655 state = self.GetToolState(id)
643 656 if state:
644   - ps.Publisher().sendMessage('Enable mode',
645   - MODE_BY_ID[id])
646   -
  657 + ps.Publisher().sendMessage('Enable style', id)
647 658 ps.Publisher().sendMessage('Untoggle slice toolbar items')
648 659 else:
649   - ps.Publisher().sendMessage('Disable mode',
650   - MODE_BY_ID[id])
651   -
652   -
  660 + ps.Publisher().sendMessage('Disable style', id)
653 661  
654   - for item in OBJ_TOOLS:
  662 + for item in const.TOOL_STATES:
655 663 state = self.GetToolState(item)
656 664 if state and (item != id):
657 665 self.ToggleTool(item, False)
... ... @@ -660,7 +668,7 @@ class ObjectToolBar(wx.ToolBar):
660 668  
661 669  
662 670 def UntoggleAllItems(self, pubsub_evt=None):
663   - for id in OBJ_TOOLS:
  671 + for id in const.TOOL_STATES:
664 672 state = self.GetToolState(id)
665 673 if state:
666 674 self.ToggleTool(id, False)
... ... @@ -695,9 +703,9 @@ class SliceToolBar(wx.ToolBar):
695 703 BMP_CROSS = wx.Bitmap(os.path.join(const.ICON_DIR, "cross.png"),
696 704 wx.BITMAP_TYPE_PNG)
697 705  
698   - self.AddCheckTool(ID_SLICE_SCROLL, BMP_SLICE)
699 706  
700   - self.AddCheckTool(ID_CROSS, BMP_CROSS)
  707 + self.AddCheckTool(const.SLICE_STATE_SCROLL, BMP_SLICE)
  708 + self.AddCheckTool(const.SLICE_STATE_CROSS, BMP_CROSS)
701 709  
702 710 self.Realize()
703 711  
... ... @@ -711,23 +719,14 @@ class SliceToolBar(wx.ToolBar):
711 719 def OnClick(self, evt):
712 720 id = evt.GetId()
713 721 state = self.GetToolState(id)
714   -
  722 +
715 723 if state:
716   - ps.Publisher().sendMessage('Enable mode',
717   - SLICE_MODE_BY_ID[id])
  724 + ps.Publisher().sendMessage('Enable style', id)
718 725 ps.Publisher().sendMessage('Untoggle object toolbar items')
719 726 else:
720   - ps.Publisher().sendMessage('Disable mode',
721   - SLICE_MODE_BY_ID[id])
722   - if id == ID_CROSS:
723   - if state:
724   - ps.Publisher().sendMessage('Set cross visibility', 1)
725   - else:
726   - ps.Publisher().sendMessage('Set cross visibility', 0)
727   - else:
728   - ps.Publisher().sendMessage('Set cross visibility', 0)
  727 + ps.Publisher().sendMessage('Disable style', id)
729 728  
730   - for item in SLICE_TOOLS:
  729 + for item in const.TOOL_SLICE_STATES:
731 730 state = self.GetToolState(item)
732 731 if state and (item != id):
733 732 self.ToggleTool(item, False)
... ... @@ -736,11 +735,11 @@ class SliceToolBar(wx.ToolBar):
736 735  
737 736  
738 737 def UntoggleAllItem(self, pubsub_evt):
739   - for id in SLICE_TOOLS:
  738 + for id in const.TOOL_SLICE_STATES:
740 739 state = self.GetToolState(id)
741 740 if state:
742 741 self.ToggleTool(id, False)
743   - if id == ID_CROSS:
  742 + if id == const.SLICE_STATE_CROSS:
744 743 ps.Publisher().sendMessage('Set cross visibility', 0)
745 744  
746 745 # ---------------------------------------------------------------------
... ...
invesalius/gui/task_slice.py
... ... @@ -205,7 +205,7 @@ class InnerFoldPanel(wx.Panel):
205 205 fold_panel.ApplyCaptionStyle(item, style)
206 206 fold_panel.AddFoldPanelWindow(item, EditionTools(item), Spacing= 0,
207 207 leftSpacing=0, rightSpacing=0)
208   - self.editor_panel_id = item.GetId()
  208 + self.__id_editor = item.GetId()
209 209 self.last_panel_opened = None
210 210  
211 211 #fold_panel.Expand(fold_panel.GetFoldPanel(1))
... ... @@ -217,23 +217,45 @@ class InnerFoldPanel(wx.Panel):
217 217 self.SetSizer(sizer)
218 218 self.Update()
219 219 self.SetAutoLayout(1)
220   - fold_panel.Bind(fpb.EVT_CAPTIONBAR, self.OnFoldPressCaption)
221   -
  220 +
  221 + self.fold_panel = fold_panel
  222 + self.last_style = None
  223 +
  224 + self.__bind_evt()
  225 + self.__bind_pubsub_evt()
  226 +
  227 + def __bind_evt(self):
  228 + self.fold_panel.Bind(fpb.EVT_CAPTIONBAR, self.OnFoldPressCaption)
  229 +
  230 + def __bind_pubsub_evt(self):
  231 + ps.Publisher().subscribe(self.OnRetrieveStyle, 'Retrieve task slice style')
  232 + ps.Publisher().subscribe(self.OnDisableStyle, 'Disable task slice style')
  233 +
222 234 def OnFoldPressCaption(self, evt):
223   -
224   - if (self.editor_panel_id == evt.GetTag().GetId()):
225   - if not(evt.GetFoldStatus()):
226   - ps.Publisher().sendMessage('Enable mode', const.SLICE_STATE_EDITOR)
  235 + id = evt.GetTag().GetId()
  236 + closed = evt.GetFoldStatus()
  237 +
  238 + if self.__id_editor == id:
  239 + if closed:
  240 + ps.Publisher().sendMessage('Disable style', const.SLICE_STATE_EDITOR)
  241 + self.last_style = None
227 242 else:
228   - ps.Publisher().sendMessage('Disable mode', const.SLICE_STATE_EDITOR)
  243 + ps.Publisher().sendMessage('Enable style', const.SLICE_STATE_EDITOR)
  244 + self.last_style = const.SLICE_STATE_EDITOR
229 245 else:
230   - if(self.last_panel_opened == self.editor_panel_id):
231   - ps.Publisher().sendMessage('Disable mode', const.SLICE_STATE_EDITOR)
232   -
233   - self.last_panel_opened = evt.GetTag().GetId()
  246 + ps.Publisher().sendMessage('Disable style', const.SLICE_STATE_EDITOR)
  247 + self.last_style = None
234 248  
235 249 evt.Skip()
236 250  
  251 + def OnRetrieveStyle(self, pubsub_evt):
  252 + if (self.last_style == const.SLICE_STATE_EDITOR):
  253 + ps.Publisher().sendMessage('Enable style', const.SLICE_STATE_EDITOR)
  254 +
  255 + def OnDisableStyle(self, pubsub_evt):
  256 + if (self.last_style == const.SLICE_STATE_EDITOR):
  257 + ps.Publisher().sendMessage('Disable style', const.SLICE_STATE_EDITOR)
  258 +
237 259 def GetMaskSelected(self):
238 260 x= self.mask_prop_panel.GetMaskSelected()
239 261 return self.mask_prop_panel.GetMaskSelected()
... ...
invesalius/style.py 0 → 100644
... ... @@ -0,0 +1,112 @@
  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 wx.lib.pubsub as ps
  21 +
  22 +
  23 +# mode.py
  24 +# to be instanced inside Controller (control.py)
  25 +
  26 +
  27 +
  28 +# IMPORTANT: When adding a new state, remember o insert it into LEVEL
  29 +# dictionary
  30 +
  31 +
  32 +# RULE:
  33 +# default is the only level 0
  34 +# states controlled somehow by taskmenu are level 1
  35 +# states controlled by toolbar are level 2
  36 +#LEVEL = {SLICE_STATE_DEFAULT: 0,
  37 +# SLICE_STATE_EDITOR: 1,
  38 +# SLICE_STATE_WL: 2,
  39 +# SLICE_STATE_SPIN: 2,
  40 +# SLICE_STATE_ZOOM: 2,
  41 +# SLICE_STATE_ZOOM_SL: 2}
  42 +#----------------------
  43 +# TODO: Add to viewer_slice.py:
  44 +
  45 +#ps.Publisher().subscribe(self.OnSetMode, 'Set slice mode')
  46 +
  47 +#def OnSetMode(self, pubsub_evt):
  48 +# mode = pubsub_evt.data
  49 + # according to mode, set cursor, interaction, etc
  50 +#----------------------
  51 +# TODO: Add GUI classes (frame, tasks related to slice, toolbar):
  52 +
  53 +# always bind to this class (regarding slice mode) and not to
  54 +# viewer_slice directly
  55 +
  56 +# example - pseudo code
  57 +#def OnToggleButtonSpin(self, evt)
  58 +# if evt.toggle: # doesn't exist, just to illustrate
  59 +# ps.Publisher().sendMessage('Enable mode', const.SLICE_STATE_ZOOM)
  60 +# else:
  61 +# ps.Publisher().subscribe('Disable mode', const.SLICE_STATE_ZOOM)
  62 +
  63 +
  64 +#----------------------
  65 +
  66 +
  67 +import constants as const
  68 +
  69 +class StyleStateManager(object):
  70 +# don't need to be singleton, only needs to be instantiated inside
  71 +# (Controller) self.slice_mode = SliceMode()
  72 +
  73 + def __init__(self):
  74 + self.stack = {}
  75 +
  76 + # push default value to stack
  77 + self.stack[const.STYLE_LEVEL[const.STATE_DEFAULT]] = \
  78 + const.STATE_DEFAULT
  79 +
  80 + def AddState(self, state):
  81 +
  82 + level = const.STYLE_LEVEL[state]
  83 + max_level = max(self.stack.keys())
  84 +
  85 + # Insert new state into stack
  86 + self.stack[level] = state
  87 +
  88 +
  89 + new_max_level = max(self.stack.keys())
  90 + return self.stack[new_max_level]
  91 +
  92 + def RemoveState(self, state):
  93 + level = const.STYLE_LEVEL[state]
  94 + if level in self.stack.keys():
  95 + max_level = max(self.stack.keys())
  96 +
  97 + # Remove item from stack
  98 + self.stack.pop(level)
  99 +
  100 + # New max level
  101 + new_max_level = max(self.stack.keys())
  102 +
  103 + # Only will affect InVesalius behaviour if the highest
  104 + # level in stack has been removed
  105 + if level == max_level:
  106 + new_state = self.stack[new_max_level]
  107 +
  108 + return self.stack[new_max_level]
  109 +
  110 + max_level = max(self.stack.keys())
  111 + return self.stack[max_level]
  112 +
... ...