Commit 66fa5cd60953006d0c96842289ec66d8044f07db
1 parent
c6e7c193
Exists in
master
and in
68 other branches
ADD: Inserted cross that follows left mouse clicks on slices 2D
Showing
6 changed files
with
287 additions
and
73 deletions
Show diff stats
invesalius/constants.py
@@ -78,3 +78,10 @@ WINDOW_LEVEL = ({"Abdomen":(350,50), | @@ -78,3 +78,10 @@ WINDOW_LEVEL = ({"Abdomen":(350,50), | ||
78 | "Vasculature - Hard":(240,80), | 78 | "Vasculature - Hard":(240,80), |
79 | "Vasculature - Soft":(650,160) | 79 | "Vasculature - Soft":(650,160) |
80 | }) | 80 | }) |
81 | + | ||
82 | +ORIENTATION_COLOUR = {'AXIAL': (1,0,0), # Red | ||
83 | + 'CORONAL': (0,1,0), # Green | ||
84 | + 'SAGITAL': (0,0,1)} # Blue | ||
85 | +CAM_POSITION = {"AXIAL":(0, 0, 1), "CORONAL":(0, -1, 0), "SAGITAL":(1, 0, 0)} | ||
86 | +CAM_VIEW_UP = {"AXIAL":(0, 1, 0), "CORONAL":(0, 0, 1), "SAGITAL":(0, 0, 1)} | ||
87 | + |
invesalius/data/slice_.py
@@ -28,6 +28,8 @@ class Slice(object): | @@ -28,6 +28,8 @@ class Slice(object): | ||
28 | ps.Publisher().subscribe(self.OnChangeCurrentMask, 'Change mask selected') | 28 | ps.Publisher().subscribe(self.OnChangeCurrentMask, 'Change mask selected') |
29 | ps.Publisher().subscribe(self.CreateSurfaceFromIndex, | 29 | ps.Publisher().subscribe(self.CreateSurfaceFromIndex, |
30 | 'Create surface from index') | 30 | 'Create surface from index') |
31 | + ps.Publisher().subscribe(self.UpdateCursorPosition, | ||
32 | + 'Update cursor position in slice') | ||
31 | 33 | ||
32 | def CreateSurfaceFromIndex(self, pubsub_evt): | 34 | def CreateSurfaceFromIndex(self, pubsub_evt): |
33 | mask_index = pubsub_evt.data | 35 | mask_index = pubsub_evt.data |
@@ -116,7 +118,46 @@ class Slice(object): | @@ -116,7 +118,46 @@ class Slice(object): | ||
116 | blend_imagedata.SetInput(1, imagedata_mask) | 118 | blend_imagedata.SetInput(1, imagedata_mask) |
117 | blend_imagedata.SetBlendModeToNormal() | 119 | blend_imagedata.SetBlendModeToNormal() |
118 | blend_imagedata.GetOutput().ReleaseDataFlagOn() | 120 | blend_imagedata.GetOutput().ReleaseDataFlagOn() |
119 | - self.blend_imagedata = blend_imagedata | 121 | + #self.blend_imagedata = blend_imagedata |
122 | + | ||
123 | + | ||
124 | + #blend_imagedata.GetExtent() | ||
125 | + | ||
126 | + # global values | ||
127 | + CURSOR_X = 0 # SAGITAL | ||
128 | + CURSOR_Y = 0 # CORONAL | ||
129 | + CURSOR_Z = 0 # AXIAL | ||
130 | + | ||
131 | + CURSOR_VALUE = 4095 | ||
132 | + CURSOR_RADIUS = 1000 | ||
133 | + | ||
134 | + cross = vtk.vtkImageCursor3D() | ||
135 | + cross.GetOutput().ReleaseDataFlagOn() | ||
136 | + cross.SetInput(blend_imagedata.GetOutput()) | ||
137 | + cross.SetCursorPosition(CURSOR_X, CURSOR_Y, CURSOR_Z) | ||
138 | + cross.SetCursorValue(CURSOR_VALUE) | ||
139 | + cross.SetCursorRadius(CURSOR_RADIUS) | ||
140 | + cross.Modified() | ||
141 | + self.cross = cross | ||
142 | + | ||
143 | + cast = vtk.vtkImageCast() | ||
144 | + cast.SetInput(cross.GetOutput()) | ||
145 | + cast.GetOutput().SetUpdateExtentToWholeExtent() | ||
146 | + cast.SetOutputScalarTypeToUnsignedChar() | ||
147 | + cast.Update() | ||
148 | + | ||
149 | + self.blend_imagedata = cast | ||
150 | + | ||
151 | + | ||
152 | + def UpdateCursorPosition(self, pubsub_evt): | ||
153 | + | ||
154 | + new_pos = pubsub_evt.data | ||
155 | + self.cross.SetCursorPosition(new_pos) | ||
156 | + self.cross.Modified() | ||
157 | + self.blend_imagedata.Update() | ||
158 | + ps.Publisher().sendMessage('Update slice viewer', None) | ||
159 | + | ||
160 | + | ||
120 | 161 | ||
121 | def __create_background(self, imagedata): | 162 | def __create_background(self, imagedata): |
122 | 163 |
invesalius/data/viewer_slice.py
@@ -24,35 +24,49 @@ import wx | @@ -24,35 +24,49 @@ import wx | ||
24 | import wx.lib.pubsub as ps | 24 | import wx.lib.pubsub as ps |
25 | 25 | ||
26 | import data.slice_ as sl | 26 | import data.slice_ as sl |
27 | - | 27 | +import constants as const |
28 | import project | 28 | import project |
29 | 29 | ||
30 | class Viewer(wx.Panel): | 30 | class Viewer(wx.Panel): |
31 | 31 | ||
32 | def __init__(self, prnt, orientation='AXIAL'): | 32 | def __init__(self, prnt, orientation='AXIAL'): |
33 | wx.Panel.__init__(self, prnt, size=wx.Size(320, 300)) | 33 | wx.Panel.__init__(self, prnt, size=wx.Size(320, 300)) |
34 | - | 34 | + |
35 | + colour = [255*c for c in const.ORIENTATION_COLOUR[orientation]] | ||
36 | + self.SetBackgroundColour(colour) | ||
37 | + | ||
35 | self.__init_gui() | 38 | self.__init_gui() |
36 | - self.__config_interactor() | ||
37 | 39 | ||
38 | self.orientation = orientation | 40 | self.orientation = orientation |
39 | self.slice_number = 0 | 41 | self.slice_number = 0 |
40 | 42 | ||
43 | + # Interactor aditional style | ||
44 | + self.mode = 'DEFAULT' | ||
45 | + self.mouse_pressed = 0 | ||
46 | + | ||
47 | + # VTK pipeline and actors | ||
48 | + self.__config_interactor() | ||
49 | + self.pick = vtk.vtkCellPicker() | ||
50 | + #self.cursor = | ||
51 | + | ||
41 | self.__bind_events() | 52 | self.__bind_events() |
42 | self.__bind_events_wx() | 53 | self.__bind_events_wx() |
43 | 54 | ||
44 | def __init_gui(self): | 55 | def __init_gui(self): |
56 | + | ||
45 | interactor = wxVTKRenderWindowInteractor(self, -1, size=self.GetSize()) | 57 | interactor = wxVTKRenderWindowInteractor(self, -1, size=self.GetSize()) |
46 | 58 | ||
47 | scroll = wx.ScrollBar(self, -1, style=wx.SB_VERTICAL) | 59 | scroll = wx.ScrollBar(self, -1, style=wx.SB_VERTICAL) |
48 | self.scroll = scroll | 60 | self.scroll = scroll |
49 | 61 | ||
50 | sizer = wx.BoxSizer(wx.HORIZONTAL) | 62 | sizer = wx.BoxSizer(wx.HORIZONTAL) |
51 | - self.SetSizer(sizer) | ||
52 | - | ||
53 | - sizer.Add(scroll, 0, wx.EXPAND|wx.GROW) | ||
54 | sizer.Add(interactor, 1, wx.EXPAND|wx.GROW) | 63 | sizer.Add(interactor, 1, wx.EXPAND|wx.GROW) |
55 | - sizer.Fit(self) | 64 | + |
65 | + background_sizer = wx.BoxSizer(wx.HORIZONTAL) | ||
66 | + background_sizer.AddSizer(sizer, 1, wx.EXPAND|wx.GROW|wx.ALL, 2) | ||
67 | + background_sizer.Add(scroll, 0, wx.EXPAND|wx.GROW) | ||
68 | + self.SetSizer(background_sizer) | ||
69 | + background_sizer.Fit(self) | ||
56 | 70 | ||
57 | self.Layout() | 71 | self.Layout() |
58 | self.Update() | 72 | self.Update() |
@@ -61,7 +75,10 @@ class Viewer(wx.Panel): | @@ -61,7 +75,10 @@ class Viewer(wx.Panel): | ||
61 | self.interactor = interactor | 75 | self.interactor = interactor |
62 | 76 | ||
63 | def __config_interactor(self): | 77 | def __config_interactor(self): |
78 | + | ||
64 | style = vtk.vtkInteractorStyleImage() | 79 | style = vtk.vtkInteractorStyleImage() |
80 | + self.style = style | ||
81 | + | ||
65 | ren = vtk.vtkRenderer() | 82 | ren = vtk.vtkRenderer() |
66 | 83 | ||
67 | interactor = self.interactor | 84 | interactor = self.interactor |
@@ -69,26 +86,116 @@ class Viewer(wx.Panel): | @@ -69,26 +86,116 @@ class Viewer(wx.Panel): | ||
69 | interactor.GetRenderWindow().AddRenderer(ren) | 86 | interactor.GetRenderWindow().AddRenderer(ren) |
70 | 87 | ||
71 | self.cam = ren.GetActiveCamera() | 88 | self.cam = ren.GetActiveCamera() |
72 | - | ||
73 | self.ren = ren | 89 | self.ren = ren |
74 | - | 90 | + |
91 | + self.SetMode(self.mode) | ||
92 | + | ||
93 | + def SetMode(self, mode): | ||
94 | + self.mode = mode | ||
95 | + # All modes and bindings | ||
96 | + action = {'DEFAULT': { | ||
97 | + "MouseMoveEvent": self.OnCrossMove, | ||
98 | + "LeftButtonPressEvent": self.OnMouseClick, | ||
99 | + "LeftButtonReleaseEvent": self.OnMouseRelease | ||
100 | + }, | ||
101 | + 'EDITOR': { | ||
102 | + "MouseMoveEvent": self.OnBrushMove, | ||
103 | + "LeftButtonPressEvent": self.OnBrushClick, | ||
104 | + "LeftButtonReleaseEvent": self.OnMouseRelease | ||
105 | + } | ||
106 | + } | ||
107 | + | ||
108 | + # Bind method according to current mode | ||
109 | + style = self.style | ||
110 | + style.AddObserver("MouseMoveEvent", action[mode]["MouseMoveEvent"]) | ||
111 | + style.AddObserver("LeftButtonPressEvent", action[mode]["LeftButtonPressEvent"]) | ||
112 | + style.AddObserver("LeftButtonReleaseEvent", action[mode]["LeftButtonReleaseEvent"]) | ||
113 | + | ||
114 | + def OnMouseClick(self, obj, evt_vtk): | ||
115 | + self.mouse_pressed = 1 | ||
116 | + | ||
117 | + def OnMouseRelease(self, obj, evt_vtk): | ||
118 | + self.mouse_pressed = 0 | ||
119 | + | ||
120 | + def OnBrushClick(self, obj, evt_vtk): | ||
121 | + self.mouse_pressed = 1 | ||
122 | + coord = self.GetCoordinate() | ||
123 | + print "Edit pixel region based on origin:", coord | ||
124 | + | ||
125 | + def OnBrushMove(self, obj, evt_vtk): | ||
126 | + coord = self.GetCoordinate() | ||
127 | + if self.mouse_pressed: | ||
128 | + print "Edit pixel region based on origin:", coord | ||
129 | + | ||
130 | + def OnCrossMove(self, obj, evt_vtk): | ||
131 | + coord = self.GetCoordinate() | ||
132 | + # Update position in other slices | ||
133 | + if self.mouse_pressed: | ||
134 | + ps.Publisher().sendMessage('Update cursor position in slice', coord) | ||
135 | + ps.Publisher().sendMessage(('Set scroll position', 'SAGITAL'), coord[0]) | ||
136 | + ps.Publisher().sendMessage(('Set scroll position', 'CORONAL'), coord[1]) | ||
137 | + ps.Publisher().sendMessage(('Set scroll position', 'AXIAL'), coord[2]) | ||
138 | + | ||
139 | + def GetCoordinate(self): | ||
140 | + | ||
141 | + # Find position | ||
142 | + mouse_x, mouse_y = self.interactor.GetEventPosition() | ||
143 | + self.pick.Pick(mouse_x, mouse_y, 0, self.ren) | ||
144 | + x, y, z = self.pick.GetPickPosition() | ||
145 | + | ||
146 | + # First we fix the position origin, based on vtkActor bounds | ||
147 | + bounds = self.actor.GetBounds() | ||
148 | + bound_xi, bound_xf, bound_yi, bound_yf, bound_zi, bound_zf = bounds | ||
149 | + x = float(x - bound_xi) | ||
150 | + y = float(y - bound_yi) | ||
151 | + z = float(z - bound_zi) | ||
152 | + | ||
153 | + # Then we fix the porpotion, based on vtkImageData spacing | ||
154 | + spacing_x, spacing_y, spacing_z = self.imagedata.GetSpacing() | ||
155 | + x = x/spacing_x | ||
156 | + y = y/spacing_y | ||
157 | + z = z/spacing_z | ||
158 | + | ||
159 | + # Based on the current orientation, we define 3D position | ||
160 | + coordinates = {"SAGITAL": [self.slice_number, y, z], | ||
161 | + "CORONAL": [x, self.slice_number, z], | ||
162 | + "AXIAL": [x, y, self.slice_number]} | ||
163 | + coord = [int(coord) for coord in coordinates[self.orientation]] | ||
164 | + | ||
165 | + # According to vtkImageData extent, we limit min and max value | ||
166 | + # If this is not done, a VTK Error occurs when mouse is pressed outside | ||
167 | + # vtkImageData extent | ||
168 | + extent = self.imagedata.GetWholeExtent() | ||
169 | + extent_min = extent[0], extent[2], extent[4] | ||
170 | + extent_max = extent[1], extent[3], extent[5] | ||
171 | + for index in xrange(3): | ||
172 | + if coord[index] > extent_max[index]: | ||
173 | + coord[index] = extent_max[index] | ||
174 | + elif coord[index] < extent_min[index]: | ||
175 | + coord[index] = extent_min[index] | ||
176 | + | ||
177 | + print "New coordinate: ", coord | ||
178 | + | ||
179 | + return coord | ||
180 | + | ||
75 | def __bind_events(self): | 181 | def __bind_events(self): |
76 | ps.Publisher().subscribe(self.LoadImagedata, 'Load slice to viewer') | 182 | ps.Publisher().subscribe(self.LoadImagedata, 'Load slice to viewer') |
77 | ps.Publisher().subscribe(self.SetColour, 'Change mask colour') | 183 | ps.Publisher().subscribe(self.SetColour, 'Change mask colour') |
78 | ps.Publisher().subscribe(self.UpdateRender, 'Update slice viewer') | 184 | ps.Publisher().subscribe(self.UpdateRender, 'Update slice viewer') |
79 | - ps.Publisher().subscribe(self.SetScrollPosition, ('Set scroll position', | 185 | + ps.Publisher().subscribe(self.ChangeSliceNumber, ('Set scroll position', |
80 | self.orientation)) | 186 | self.orientation)) |
81 | 187 | ||
82 | def __bind_events_wx(self): | 188 | def __bind_events_wx(self): |
83 | self.scroll.Bind(wx.EVT_SCROLL, self.OnScrollBar) | 189 | self.scroll.Bind(wx.EVT_SCROLL, self.OnScrollBar) |
84 | - #self.interactor.Bind(wx.EVT_MOUSEWHEEL, self.OnMouseWheel) | ||
85 | 190 | ||
86 | def LoadImagedata(self, pubsub_evt): | 191 | def LoadImagedata(self, pubsub_evt): |
87 | imagedata = pubsub_evt.data | 192 | imagedata = pubsub_evt.data |
88 | self.SetInput(imagedata) | 193 | self.SetInput(imagedata) |
89 | 194 | ||
90 | def SetInput(self, imagedata): | 195 | def SetInput(self, imagedata): |
196 | + | ||
91 | self.imagedata = imagedata | 197 | self.imagedata = imagedata |
198 | + | ||
92 | ren = self.ren | 199 | ren = self.ren |
93 | interactor = self.interactor | 200 | interactor = self.interactor |
94 | 201 | ||
@@ -101,77 +208,72 @@ class Viewer(wx.Panel): | @@ -101,77 +208,72 @@ class Viewer(wx.Panel): | ||
101 | actor.SetInput(slice_.GetOutput()) | 208 | actor.SetInput(slice_.GetOutput()) |
102 | self.actor = actor | 209 | self.actor = actor |
103 | 210 | ||
211 | + colour = const.ORIENTATION_COLOUR[self.orientation] | ||
212 | + | ||
213 | + text_property = vtk.vtkTextProperty() | ||
214 | + text_property.SetFontSize(16) | ||
215 | + text_property.SetFontFamilyToArial() | ||
216 | + text_property.BoldOn() | ||
217 | + text_property.SetColor(colour) | ||
218 | + | ||
219 | + # Text related to slice number shown | ||
220 | + text_mapper = vtk.vtkTextMapper() | ||
221 | + text_mapper.SetInput("%d"%(self.slice_number)) | ||
222 | + text_mapper.GetTextProperty().ShallowCopy(text_property) | ||
223 | + self.text_mapper = text_mapper | ||
224 | + | ||
225 | + text_actor = vtk.vtkActor2D() | ||
226 | + text_actor.SetMapper(text_mapper) | ||
227 | + text_actor.SetLayerNumber(1) | ||
228 | + text_actor.GetPositionCoordinate().SetValue(self.GetSize()[0] - 80, 20) | ||
229 | + text_actor.SetVisibility(1) | ||
230 | + | ||
104 | ren.AddActor(actor) | 231 | ren.AddActor(actor) |
232 | + ren.AddActor(text_actor) | ||
105 | self.__update_camera() | 233 | self.__update_camera() |
106 | 234 | ||
107 | max_slice_number = actor.GetSliceNumberMax() | 235 | max_slice_number = actor.GetSliceNumberMax() |
108 | self.scroll.SetScrollbar(wx.SB_VERTICAL, 1, max_slice_number, | 236 | self.scroll.SetScrollbar(wx.SB_VERTICAL, 1, max_slice_number, |
109 | max_slice_number) | 237 | max_slice_number) |
238 | + self.SetScrollPosition(0) | ||
110 | 239 | ||
111 | def SetOrientation(self, orientation): | 240 | def SetOrientation(self, orientation): |
112 | self.orientation = orientation | 241 | self.orientation = orientation |
113 | self.__update_camera() | 242 | self.__update_camera() |
114 | 243 | ||
115 | def __update_camera(self): | 244 | def __update_camera(self): |
116 | - cam = self.cam | ||
117 | - | ||
118 | orientation = self.orientation | 245 | orientation = self.orientation |
119 | 246 | ||
120 | - if orientation == "AXIAL": | ||
121 | - cam.SetFocalPoint(0, 0, 0) | ||
122 | - cam.SetPosition(0, 0, 1) | ||
123 | - cam.ComputeViewPlaneNormal() | ||
124 | - cam.SetViewUp(0, 1, 0) | ||
125 | - elif orientation == "CORONAL": | ||
126 | - cam.SetFocalPoint(0, 0, 0) | ||
127 | - cam.SetPosition(0, -1, 0) | ||
128 | - cam.ComputeViewPlaneNormal() | ||
129 | - cam.SetViewUp(0, 0, 1) | ||
130 | - elif orientation == "SAGITAL": | ||
131 | - cam.SetFocalPoint(0, 0, 0) | ||
132 | - cam.SetPosition(1, 0, 0) | ||
133 | - cam.ComputeViewPlaneNormal() | ||
134 | - cam.SetViewUp(0, 0, 1) | ||
135 | - | 247 | + cam = self.cam |
248 | + cam.SetFocalPoint(0, 0, 0) | ||
249 | + cam.SetPosition(const.CAM_POSITION[self.orientation]) | ||
250 | + cam.SetViewUp(const.CAM_VIEW_UP[self.orientation]) | ||
251 | + cam.ComputeViewPlaneNormal() | ||
136 | cam.OrthogonalizeViewUp() | 252 | cam.OrthogonalizeViewUp() |
253 | + cam.ParallelProjectionOn() | ||
254 | + | ||
137 | self.__update_display_extent() | 255 | self.__update_display_extent() |
138 | - cam.ParallelProjectionOn() | 256 | + |
139 | self.ren.ResetCamera() | 257 | self.ren.ResetCamera() |
140 | self.ren.Render() | 258 | self.ren.Render() |
141 | 259 | ||
142 | def __update_display_extent(self): | 260 | def __update_display_extent(self): |
143 | - actor = self.actor | ||
144 | - slice_number = self.slice_number | ||
145 | - extent = self.imagedata.GetWholeExtent() | ||
146 | - if self.orientation == "AXIAL": | ||
147 | - xs = extent[1] - extent[0] + 1 | ||
148 | - ys = extent[3] - extent[2] + 1 | ||
149 | - actor.SetDisplayExtent(extent[0], extent[1], | ||
150 | - extent[2], extent[3], | ||
151 | - slice_number, slice_number) | ||
152 | - elif self.orientation == "CORONAL": | ||
153 | - xs = extent[1] - extent[0] + 1 | ||
154 | - ys = extent[5] - extent[4] + 1 | ||
155 | - actor.SetDisplayExtent(extent[0], extent[1], | ||
156 | - slice_number,slice_number, | ||
157 | - extent[4], extent[5]) | ||
158 | - elif self.orientation == "SAGITAL": | ||
159 | - xs = extent[3] - extent[2] + 1 | ||
160 | - ys = extent[5] - extent[4] + 1 | ||
161 | - actor.SetDisplayExtent(slice_number, slice_number, | ||
162 | - extent[2], extent[3], | ||
163 | - extent[4], extent[5]) | ||
164 | 261 | ||
262 | + pos = self.slice_number | ||
263 | + e = self.imagedata.GetWholeExtent() | ||
264 | + | ||
265 | + new_extent = {"SAGITAL": (pos, pos, e[2], e[3], e[4], e[5]), | ||
266 | + "CORONAL": (e[0], e[1], pos, pos, e[4], e[5]), | ||
267 | + "AXIAL": (e[0], e[1], e[2], e[3], pos, pos)} | ||
268 | + | ||
269 | + self.actor.SetDisplayExtent(new_extent[self.orientation]) | ||
165 | self.ren.ResetCameraClippingRange() | 270 | self.ren.ResetCameraClippingRange() |
166 | self.ren.Render() | 271 | self.ren.Render() |
167 | 272 | ||
168 | def UpdateRender(self, evt): | 273 | def UpdateRender(self, evt): |
169 | self.interactor.Render() | 274 | self.interactor.Render() |
170 | 275 | ||
171 | - def SetScrollPosition(self, pubsub_evt): | ||
172 | - value = pubsub_evt.data | ||
173 | - position = self.scroll.GetThumbPosition() | ||
174 | - position += value | 276 | + def SetScrollPosition(self, position): |
175 | self.scroll.SetThumbPosition(position) | 277 | self.scroll.SetThumbPosition(position) |
176 | self.OnScrollBar() | 278 | self.OnScrollBar() |
177 | 279 | ||
@@ -183,9 +285,16 @@ class Viewer(wx.Panel): | @@ -183,9 +285,16 @@ class Viewer(wx.Panel): | ||
183 | evt.Skip() | 285 | evt.Skip() |
184 | 286 | ||
185 | def SetSliceNumber(self, index): | 287 | def SetSliceNumber(self, index): |
288 | + self.text_mapper.SetInput(str(index)) | ||
186 | self.slice_number = index | 289 | self.slice_number = index |
187 | self.__update_display_extent() | 290 | self.__update_display_extent() |
188 | 291 | ||
292 | + def ChangeSliceNumber(self, pubsub_evt): | ||
293 | + index = pubsub_evt.data | ||
294 | + self.SetSliceNumber(index) | ||
295 | + self.scroll.SetThumbPosition(index) | ||
296 | + self.interactor.Render() | ||
297 | + | ||
189 | def SetColour(self, pubsub_evt): | 298 | def SetColour(self, pubsub_evt): |
190 | colour_wx = pubsub_evt.data | 299 | colour_wx = pubsub_evt.data |
191 | colour_vtk = [colour/float(255) for colour in colour_wx] | 300 | colour_vtk = [colour/float(255) for colour in colour_wx] |
invesalius/data/viewer_volume.py
@@ -61,6 +61,26 @@ class Viewer(wx.Panel): | @@ -61,6 +61,26 @@ class Viewer(wx.Panel): | ||
61 | def __bind_events(self): | 61 | def __bind_events(self): |
62 | ps.Publisher().subscribe(self.LoadActor, 'Load surface actor into viewer') | 62 | ps.Publisher().subscribe(self.LoadActor, 'Load surface actor into viewer') |
63 | ps.Publisher().subscribe(self.UpdateRender, 'Render volume viewer') | 63 | ps.Publisher().subscribe(self.UpdateRender, 'Render volume viewer') |
64 | + ps.Publisher().subscribe(self.ChangeBackgroundColour, | ||
65 | + 'Change volume viewer background colour') | ||
66 | + ps.Publisher().subscribe(self.ShowRaycastingVolume, | ||
67 | + 'Show raycasting volume') | ||
68 | + ps.Publisher().subscribe(self.HideRaycastingVolume, | ||
69 | + 'Hide raycasting volume') | ||
70 | + | ||
71 | + | ||
72 | + def ShowRaycastingVolume(self, pubsub_evt): | ||
73 | + pass | ||
74 | + | ||
75 | + def HideRaycastingVolume(self, pubsub_evt): | ||
76 | + pass | ||
77 | + | ||
78 | + | ||
79 | + | ||
80 | + def ChangeBackgroundColour(self, pubsub_evt): | ||
81 | + colour = pubsub_evt.data | ||
82 | + self.ren.SetBackground(colour) | ||
83 | + self.UpdateRender() | ||
64 | 84 | ||
65 | def LoadActor(self, pubsub_evt): | 85 | def LoadActor(self, pubsub_evt): |
66 | actor = pubsub_evt.data | 86 | actor = pubsub_evt.data |
@@ -76,7 +96,7 @@ class Viewer(wx.Panel): | @@ -76,7 +96,7 @@ class Viewer(wx.Panel): | ||
76 | 96 | ||
77 | self.iren.Render() | 97 | self.iren.Render() |
78 | 98 | ||
79 | - def UpdateRender(self, evt_pubsub): | 99 | + def UpdateRender(self, evt_pubsub=None): |
80 | self.iren.Render() | 100 | self.iren.Render() |
81 | 101 | ||
82 | def CreatePlanes(self): | 102 | def CreatePlanes(self): |
invesalius/gui/default_viewers.py
@@ -21,18 +21,6 @@ import wx.lib.agw.fourwaysplitter as fws | @@ -21,18 +21,6 @@ import wx.lib.agw.fourwaysplitter as fws | ||
21 | import data.viewer_slice as slice_viewer | 21 | import data.viewer_slice as slice_viewer |
22 | import data.viewer_volume as volume_viewer | 22 | import data.viewer_volume as volume_viewer |
23 | 23 | ||
24 | -class SamplePane(wx.Panel): | ||
25 | - """ | ||
26 | - Just a simple test window to put into the splitter. | ||
27 | - """ | ||
28 | - def __init__(self, parent, colour, label): | ||
29 | - wx.Panel.__init__(self, parent, style=wx.BORDER_SUNKEN) | ||
30 | - self.SetBackgroundColour(colour) | ||
31 | - wx.StaticText(self, -1, label, (5,5)) | ||
32 | - | ||
33 | - def SetOtherLabel(self, label): | ||
34 | - wx.StaticText(self, -1, label, (5, 30)) | ||
35 | - | ||
36 | 24 | ||
37 | class Panel(wx.Panel): | 25 | class Panel(wx.Panel): |
38 | def __init__(self, parent): | 26 | def __init__(self, parent): |
@@ -89,7 +77,8 @@ class Panel(wx.Panel): | @@ -89,7 +77,8 @@ class Panel(wx.Panel): | ||
89 | Name("Sagital Slice").Caption("Sagital slice"). | 77 | Name("Sagital Slice").Caption("Sagital slice"). |
90 | MaximizeButton(True).CloseButton(False)) | 78 | MaximizeButton(True).CloseButton(False)) |
91 | 79 | ||
92 | - self.aui_manager.AddPane(volume_viewer.Viewer(self), | 80 | + self.aui_manager.AddPane(VolumeViewerCover(self), |
81 | + #self.aui_manager.AddPane(volume_viewer.Viewer(self) | ||
93 | wx.aui.AuiPaneInfo().Row(1).Name("Volume"). | 82 | wx.aui.AuiPaneInfo().Row(1).Name("Volume"). |
94 | Bottom().Centre().Caption("Volume"). | 83 | Bottom().Centre().Caption("Volume"). |
95 | MaximizeButton(True).CloseButton(False)) | 84 | MaximizeButton(True).CloseButton(False)) |
@@ -150,8 +139,8 @@ class Panel(wx.Panel): | @@ -150,8 +139,8 @@ class Panel(wx.Panel): | ||
150 | Name("Sagittal Slice").Caption("Sagittal slice"). | 139 | Name("Sagittal Slice").Caption("Sagittal slice"). |
151 | MaximizeButton(True).CloseButton(False)) | 140 | MaximizeButton(True).CloseButton(False)) |
152 | 141 | ||
153 | - p4 = volume_viewer.Viewer(self) | ||
154 | - aui_manager.AddPane(p4, | 142 | + #p4 = volume_viewer.Viewer(self) |
143 | + aui_manager.AddPane(VolumeViewerCover, | ||
155 | wx.aui.AuiPaneInfo(). | 144 | wx.aui.AuiPaneInfo(). |
156 | Name("Volume").Caption("Volume"). | 145 | Name("Volume").Caption("Volume"). |
157 | MaximizeButton(True).CloseButton(False)) | 146 | MaximizeButton(True).CloseButton(False)) |
@@ -163,3 +152,50 @@ class Panel(wx.Panel): | @@ -163,3 +152,50 @@ class Panel(wx.Panel): | ||
163 | 152 | ||
164 | 153 | ||
165 | aui_manager.Update() | 154 | aui_manager.Update() |
155 | + | ||
156 | +class VolumeViewerCover(wx.Panel): | ||
157 | + def __init__(self, parent): | ||
158 | + wx.Panel.__init__(self, parent) | ||
159 | + | ||
160 | + sizer = wx.BoxSizer(wx.HORIZONTAL) | ||
161 | + sizer.Add(volume_viewer.Viewer(self), 1, wx.EXPAND|wx.GROW) | ||
162 | + sizer.Add(VolumeToolPanel(self), 0, wx.EXPAND) | ||
163 | + self.SetSizer(sizer) | ||
164 | + sizer.Fit(self) | ||
165 | + | ||
166 | +#import wx.lib.platebtn as pbtn | ||
167 | +import wx.lib.buttons as btn | ||
168 | +import wx.lib.pubsub as ps | ||
169 | +import wx.lib.colourselect as csel | ||
170 | + | ||
171 | +class VolumeToolPanel(wx.Panel): | ||
172 | + def __init__(self, parent): | ||
173 | + wx.Panel.__init__(self, parent, size = (8,100)) | ||
174 | + | ||
175 | + BMP_RAYCASTING = wx.Bitmap("../icons/volume_raycasting.png", wx.BITMAP_TYPE_PNG) | ||
176 | + BMP_RAYCASTING.SetWidth(22) | ||
177 | + BMP_RAYCASTING.SetHeight(22) | ||
178 | + | ||
179 | + button_raycasting=btn.GenBitmapToggleButton(self, 1, BMP_RAYCASTING, size=(24,24)) | ||
180 | + button_raycasting.Bind(wx.EVT_BUTTON, self.OnToggleRaycasting) | ||
181 | + self.button_raycasting = button_raycasting | ||
182 | + | ||
183 | + button_colour= csel.ColourSelect(self, 111,colour=(0,0,0),size=(24,24)) | ||
184 | + button_colour.Bind(csel.EVT_COLOURSELECT, self.OnSelectColour) | ||
185 | + self.button_colour = button_colour | ||
186 | + | ||
187 | + sizer = wx.BoxSizer(wx.VERTICAL) | ||
188 | + sizer.Add(button_colour, 0, wx.ALL, 1) | ||
189 | + sizer.Add(button_raycasting, 0, wx.ALL, 1) | ||
190 | + self.SetSizer(sizer) | ||
191 | + sizer.Fit(self) | ||
192 | + | ||
193 | + def OnSelectColour(self, evt): | ||
194 | + colour = c = [i/255.0 for i in evt.GetValue()] | ||
195 | + ps.Publisher().sendMessage('Change volume viewer background colour', colour) | ||
196 | + | ||
197 | + def OnToggleRaycasting(self, evt): | ||
198 | + if self.button_raycasting.GetToggle(): | ||
199 | + ps.Publisher().sendMessage('Show raycasting volume') | ||
200 | + else: | ||
201 | + ps.Publisher().sendMessage('Hide raycasting volume') |
invesalius/gui/frame.py
@@ -30,7 +30,8 @@ import default_viewers as viewers | @@ -30,7 +30,8 @@ import default_viewers as viewers | ||
30 | class Frame(wx.Frame): | 30 | class Frame(wx.Frame): |
31 | def __init__(self, prnt): | 31 | def __init__(self, prnt): |
32 | wx.Frame.__init__(self, id=-1, name='', parent=prnt, | 32 | wx.Frame.__init__(self, id=-1, name='', parent=prnt, |
33 | - pos=wx.Point(0, 0), size=wx.Size(1024, 768), | 33 | + pos=wx.Point(0, 0), |
34 | + size=wx.Size(1024, 768), #size = wx.DisplaySize(), | ||
34 | style=wx.DEFAULT_FRAME_STYLE, title='InVesalius 3.0') | 35 | style=wx.DEFAULT_FRAME_STYLE, title='InVesalius 3.0') |
35 | self.Center(wx.BOTH) | 36 | self.Center(wx.BOTH) |
36 | self.SetIcon(wx.Icon("../icons/invesalius.ico", wx.BITMAP_TYPE_ICO)) | 37 | self.SetIcon(wx.Icon("../icons/invesalius.ico", wx.BITMAP_TYPE_ICO)) |