Commit f95ee382033e9f87e7452877aba92ddcd57154c1
Committed by
Thiago Franco de Moraes
1 parent
12b38b74
Exists in
master
Navigation updates and improvements (#146)
* Adição do modo DBS * Dbs mode added * Changes in target icon * Changes in colour picker settings * Minor adjustments * Adjustments in target icon * Minor adjustments * ISOTRAK py3 support * Minor adjustments
Showing
8 changed files
with
149 additions
and
59 deletions
Show diff stats
invesalius/constants.py
... | ... | @@ -505,11 +505,12 @@ VTK_WARNING = 0 |
505 | 505 | |
506 | 506 | #---------------------------------------------------------- |
507 | 507 | |
508 | +ID_MODE_NAVIGATION: object | |
508 | 509 | [ID_DICOM_IMPORT, ID_PROJECT_OPEN, ID_PROJECT_SAVE_AS, ID_PROJECT_SAVE, |
509 | 510 | ID_PROJECT_CLOSE, ID_PROJECT_INFO, ID_SAVE_SCREENSHOT, ID_DICOM_LOAD_NET, |
510 | 511 | ID_PRINT_SCREENSHOT, ID_IMPORT_OTHERS_FILES, ID_PREFERENCES, ID_DICOM_NETWORK, |
511 | 512 | ID_TIFF_JPG_PNG, ID_VIEW_INTERPOLATED, ID_MODE_NAVIGATION, ID_ANALYZE_IMPORT, |
512 | -ID_NIFTI_IMPORT, ID_PARREC_IMPORT] = [wx.NewId() for number in range(18)] | |
513 | +ID_NIFTI_IMPORT, ID_PARREC_IMPORT, ID_MODE_DBS] = [wx.NewId() for number in range(19)] | |
513 | 514 | ID_EXIT = wx.ID_EXIT |
514 | 515 | ID_ABOUT = wx.ID_ABOUT |
515 | 516 | ... | ... |
invesalius/data/coordinates.py
... | ... | @@ -174,33 +174,31 @@ def PolhemusSerialCoord(trck_init, trck_id, ref_mode): |
174 | 174 | # aoflt -> 0:letter 1:x 2:y 3:z |
175 | 175 | # this method is not optimized to work with all trackers, only with ISOTRAK |
176 | 176 | # serial connection is obsolete, remove in future |
177 | - trck_init.write("P") | |
177 | + trck_init.write(str.encode("P")) | |
178 | + scale = 10. * np.array([1., 1.0, 1.0]) | |
178 | 179 | lines = trck_init.readlines() |
179 | 180 | |
180 | - coord = None | |
181 | - | |
182 | - if lines[0][0] != '0': | |
181 | + if lines is None: | |
183 | 182 | print("The Polhemus is not connected!") |
184 | 183 | else: |
185 | - for s in lines: | |
186 | - if s[1] == '1': | |
187 | - data = s | |
188 | - elif s[1] == '2': | |
189 | - data = s | |
190 | - | |
191 | - # single ref mode | |
192 | - if not ref_mode: | |
193 | - data = data.replace('-', ' -') | |
194 | - data = [s for s in data.split()] | |
195 | - j = 0 | |
196 | - while j == 0: | |
197 | - try: | |
198 | - plh1 = [float(s) for s in data[1:len(data)]] | |
199 | - j = 1 | |
200 | - except: | |
201 | - print("error!!") | |
202 | - | |
203 | - coord = data[0:6] | |
184 | + data = lines[0] | |
185 | + data = data.replace(str.encode('-'), str.encode(' -')) | |
186 | + data = [s for s in data.split()] | |
187 | + data = [float(s) for s in data[1:len(data)]] | |
188 | + probe = np.array([data[0] * scale[0], data[1] * scale[1], data[2] * scale[2], data[3], data[4], data[5]]) | |
189 | + | |
190 | + if ref_mode: | |
191 | + data2 = lines[1] | |
192 | + data2 = data2.replace(str.encode('-'), str.encode(' -')) | |
193 | + data2 = [s for s in data2.split()] | |
194 | + data2 = [float(s) for s in data2[1:len(data2)]] | |
195 | + reference = np.array( | |
196 | + [data2[0] * scale[0], data2[1] * scale[1], data2[2] * scale[2], data2[3], data2[4], data2[5]]) | |
197 | + else: | |
198 | + reference = np.zeros(6) | |
199 | + | |
200 | + coord = np.vstack([probe, reference]) | |
201 | + | |
204 | 202 | return coord |
205 | 203 | |
206 | 204 | ... | ... |
invesalius/data/trackers.py
... | ... | @@ -143,31 +143,26 @@ def PlhWrapperConnection(tracker_id): |
143 | 143 | |
144 | 144 | |
145 | 145 | def PlhSerialConnection(tracker_id): |
146 | - try: | |
147 | - import serial | |
148 | - | |
149 | - trck_init = serial.Serial('COM1', baudrate=115200, timeout=0.2) | |
150 | - | |
151 | - if tracker_id == 2: | |
152 | - # Polhemus FASTRAK needs configurations first | |
153 | - trck_init.write(0x02, "u") | |
154 | - trck_init.write(0x02, "F") | |
155 | - elif tracker_id == 3: | |
156 | - # Polhemus ISOTRAK needs to set tracking point from | |
157 | - # center to tip. | |
158 | - trck_init.write("F") | |
159 | - trck_init.write("Y") | |
160 | - | |
161 | - trck_init.write('P') | |
162 | - data = trck_init.readlines() | |
163 | - | |
164 | - if not data: | |
165 | - trck_init = None | |
166 | - print('Could not connect to Polhemus serial without error.') | |
167 | - | |
168 | - except: | |
146 | + import serial | |
147 | + | |
148 | + trck_init = serial.Serial('COM1', baudrate=115200, timeout=0.03) | |
149 | + | |
150 | + if tracker_id == 2: | |
151 | + # Polhemus FASTRAK needs configurations first | |
152 | + trck_init.write(0x02, str.encode("u")) | |
153 | + trck_init.write(0x02, str.encode("F")) | |
154 | + elif tracker_id == 3: | |
155 | + # Polhemus ISOTRAK needs to set tracking point from | |
156 | + # center to tip. | |
157 | + trck_init.write(str.encode("u")) | |
158 | + trck_init.write(str.encode("F")) | |
159 | + trck_init.write(str.encode("Y")) | |
160 | + | |
161 | + trck_init.write(str.encode("P")) | |
162 | + data = trck_init.readlines() | |
163 | + if not data: | |
169 | 164 | trck_init = None |
170 | - print('Could not connect to Polhemus serial with error.') | |
165 | + print('Could not connect to Polhemus serial without error.') | |
171 | 166 | |
172 | 167 | return trck_init |
173 | 168 | ... | ... |
invesalius/data/viewer_volume.py
... | ... | @@ -247,6 +247,7 @@ class Viewer(wx.Panel): |
247 | 247 | Publisher.subscribe(self.RemoveMarker, 'Remove marker') |
248 | 248 | Publisher.subscribe(self.BlinkMarker, 'Blink Marker') |
249 | 249 | Publisher.subscribe(self.StopBlinkMarker, 'Stop Blink Marker') |
250 | + Publisher.subscribe(self.SetNewColor, 'Set new color') | |
250 | 251 | |
251 | 252 | # Related to object tracking during neuronavigation |
252 | 253 | Publisher.subscribe(self.OnNavigationStatus, 'Navigation status') |
... | ... | @@ -505,7 +506,7 @@ class Viewer(wx.Panel): |
505 | 506 | mapper.SetInputConnection(ball_ref.GetOutputPort()) |
506 | 507 | |
507 | 508 | prop = vtk.vtkProperty() |
508 | - prop.SetColor(colour) | |
509 | + prop.SetColor(colour[0:3]) | |
509 | 510 | |
510 | 511 | #adding a new actor for the present ball |
511 | 512 | self.staticballs.append(vtk.vtkActor()) |
... | ... | @@ -567,6 +568,11 @@ class Viewer(wx.Panel): |
567 | 568 | self.Refresh() |
568 | 569 | self.index = False |
569 | 570 | |
571 | + def SetNewColor(self, index, color): | |
572 | + self.staticballs[index].GetProperty().SetColor(color) | |
573 | + self.Refresh() | |
574 | + | |
575 | + | |
570 | 576 | def OnTargetMarkerTransparency(self, status, index): |
571 | 577 | if status: |
572 | 578 | self.staticballs[index].GetProperty().SetOpacity(1) |
... | ... | @@ -791,6 +797,10 @@ class Viewer(wx.Panel): |
791 | 797 | for ind in self.arrow_actor_list: |
792 | 798 | self.ren2.AddActor(ind) |
793 | 799 | |
800 | + | |
801 | + x, y, z = bases.flip_x(coord[0:3]) | |
802 | + self.tactor.SetPosition(x-20, y-30, z+20) | |
803 | + | |
794 | 804 | self.Refresh() |
795 | 805 | |
796 | 806 | def OnUpdateTargetCoordinates(self, coord): |
... | ... | @@ -817,7 +827,7 @@ class Viewer(wx.Panel): |
817 | 827 | tactor.SetMapper(mapper) |
818 | 828 | tactor.GetProperty().SetColor(1.0, 0.25, 0.0) |
819 | 829 | tactor.SetScale(5) |
820 | - tactor.SetPosition(self.target_coord[0]+10, self.target_coord[1]+30, self.target_coord[2]+20) | |
830 | + #tactor.SetPosition(self.target_coord[0]+10, self.target_coord[1]+30, self.target_coord[2]+20) | |
821 | 831 | self.ren.AddActor(tactor) |
822 | 832 | self.tactor = tactor |
823 | 833 | tactor.SetCamera(self.ren.GetActiveCamera()) |
... | ... | @@ -892,7 +902,7 @@ class Viewer(wx.Panel): |
892 | 902 | |
893 | 903 | self.dummy_coil_actor = vtk.vtkActor() |
894 | 904 | self.dummy_coil_actor.SetMapper(obj_mapper) |
895 | - self.dummy_coil_actor.GetProperty().SetOpacity(0.4) | |
905 | + self.dummy_coil_actor.GetProperty().SetOpacity(0.15) | |
896 | 906 | self.dummy_coil_actor.SetVisibility(1) |
897 | 907 | self.dummy_coil_actor.SetUserMatrix(m_img_vtk) |
898 | 908 | |
... | ... | @@ -1745,7 +1755,7 @@ class SlicePlane: |
1745 | 1755 | plane_x.SetRightButtonAction(0) |
1746 | 1756 | plane_x.SetMiddleButtonAction(0) |
1747 | 1757 | cursor_property = plane_x.GetCursorProperty() |
1748 | - cursor_property.SetOpacity(0) | |
1758 | + cursor_property.SetOpacity(0) | |
1749 | 1759 | |
1750 | 1760 | plane_y = self.plane_y = vtk.vtkImagePlaneWidget() |
1751 | 1761 | plane_y.DisplayTextOff() | ... | ... |
invesalius/gui/default_tasks.py
invesalius/gui/default_viewers.py
... | ... | @@ -34,6 +34,8 @@ from invesalius.gui.widgets.clut_raycasting import CLUTRaycastingWidget, \ |
34 | 34 | EVT_CLUT_CURVE_WL_CHANGE |
35 | 35 | |
36 | 36 | from invesalius.constants import ID_TO_BMP |
37 | + | |
38 | +import invesalius.session as ses | |
37 | 39 | import invesalius.constants as const |
38 | 40 | |
39 | 41 | class Panel(wx.Panel): |
... | ... | @@ -121,6 +123,9 @@ class Panel(wx.Panel): |
121 | 123 | |
122 | 124 | self.aui_manager.Update() |
123 | 125 | |
126 | + if int(ses.Session().mode) != const.MODE_NAVIGATOR: | |
127 | + Publisher.sendMessage('Deactive target button') | |
128 | + | |
124 | 129 | def __bind_events_wx(self): |
125 | 130 | self.aui_manager.Bind(wx.aui.EVT_AUI_PANE_MAXIMIZE, self.OnMaximize) |
126 | 131 | self.aui_manager.Bind(wx.aui.EVT_AUI_PANE_RESTORE, self.OnRestore) |
... | ... | @@ -313,8 +318,6 @@ import wx.lib.buttons as btn |
313 | 318 | import wx.lib.pubsub as ps |
314 | 319 | import wx.lib.colourselect as csel |
315 | 320 | |
316 | -import invesalius.constants as const | |
317 | - | |
318 | 321 | [BUTTON_RAYCASTING, BUTTON_VIEW, BUTTON_SLICE_PLANE, BUTTON_3D_STEREO, BUTTON_TARGET] = [wx.NewId() for num in range(5)] |
319 | 322 | RAYCASTING_TOOLS = wx.NewId() |
320 | 323 | |
... | ... | @@ -431,6 +434,8 @@ class VolumeToolPanel(wx.Panel): |
431 | 434 | Publisher.subscribe(self.DisableVolumeCutMenu, 'Disable volume cut menu') |
432 | 435 | Publisher.subscribe(self.StatusTargetSelect, 'Disable or enable coil tracker') |
433 | 436 | Publisher.subscribe(self.StatusObjTracker, 'Status target button') |
437 | + Publisher.subscribe(self.ActiveTarget, 'Active target button') | |
438 | + Publisher.subscribe(self.DeactiveTarget, 'Deactive target button') | |
434 | 439 | |
435 | 440 | def DisablePreset(self): |
436 | 441 | self.off_item.Check(1) |
... | ... | @@ -465,6 +470,12 @@ class VolumeToolPanel(wx.Panel): |
465 | 470 | self.status_target_select = status |
466 | 471 | self.StatusNavigation() |
467 | 472 | |
473 | + def ActiveTarget(self): | |
474 | + self.button_target.Show() | |
475 | + | |
476 | + def DeactiveTarget(self): | |
477 | + self.button_target.Hide() | |
478 | + | |
468 | 479 | def StatusNavigation(self): |
469 | 480 | if self.status_target_select and self.status_obj_tracker: |
470 | 481 | self.button_target.Enable(1) |
... | ... | @@ -622,7 +633,7 @@ class VolumeToolPanel(wx.Panel): |
622 | 633 | preset_name=ID_TO_NAME[id]) |
623 | 634 | # Enable or disable tools |
624 | 635 | if name != const.RAYCASTING_OFF_LABEL: |
625 | - self.menu_raycasting.Enable(RAYCASTING_TOOLS, 1) | |
636 | + self.menu_raycasting.Enable(RAYCASTING_TOOLS, 1) | |
626 | 637 | else: |
627 | 638 | self.menu_raycasting.Enable(RAYCASTING_TOOLS, 0) |
628 | 639 | ... | ... |
invesalius/gui/frame.py
... | ... | @@ -107,6 +107,7 @@ class Frame(wx.Frame): |
107 | 107 | |
108 | 108 | self.actived_interpolated_slices = main_menu.view_menu |
109 | 109 | self.actived_navigation_mode = main_menu.mode_menu |
110 | + self.actived_dbs_mode = main_menu.mode_dbs | |
110 | 111 | |
111 | 112 | # Set menus, status and task bar |
112 | 113 | self.SetMenuBar(main_menu) |
... | ... | @@ -490,10 +491,17 @@ class Frame(wx.Frame): |
490 | 491 | else: |
491 | 492 | self.OnInterpolatedSlices(False) |
492 | 493 | |
494 | + | |
493 | 495 | elif id == const.ID_MODE_NAVIGATION: |
496 | + Publisher.sendMessage('Deactive dbs folder') | |
497 | + Publisher.sendMessage('Active target button') | |
498 | + self.actived_dbs_mode.Check(0) | |
494 | 499 | st = self.actived_navigation_mode.IsChecked(const.ID_MODE_NAVIGATION) |
495 | 500 | self.OnNavigationMode(st) |
496 | 501 | |
502 | + elif id == const.ID_MODE_DBS: | |
503 | + self.OnDbsMode() | |
504 | + | |
497 | 505 | elif id == const.ID_CROP_MASK: |
498 | 506 | self.OnCropMask() |
499 | 507 | |
... | ... | @@ -503,6 +511,17 @@ class Frame(wx.Frame): |
503 | 511 | elif id == const.ID_CREATE_MASK: |
504 | 512 | Publisher.sendMessage('New mask from shortcut') |
505 | 513 | |
514 | + def OnDbsMode(self): | |
515 | + st = self.actived_dbs_mode.IsChecked() | |
516 | + Publisher.sendMessage('Deactive target button') | |
517 | + if st: | |
518 | + self.OnNavigationMode(st) | |
519 | + Publisher.sendMessage('Active dbs folder') | |
520 | + else: | |
521 | + self.OnNavigationMode(st) | |
522 | + Publisher.sendMessage('Deactive dbs folder') | |
523 | + self.actived_navigation_mode.Check(const.ID_MODE_NAVIGATION,0) | |
524 | + | |
506 | 525 | def OnInterpolatedSlices(self, status): |
507 | 526 | Publisher.sendMessage('Set interpolated slices', flag=status) |
508 | 527 | |
... | ... | @@ -907,7 +926,12 @@ class MenuBar(wx.MenuBar): |
907 | 926 | |
908 | 927 | #Mode |
909 | 928 | self.mode_menu = mode_menu = wx.Menu() |
910 | - mode_menu.Append(const.ID_MODE_NAVIGATION, _(u'Navigation mode'), "", wx.ITEM_CHECK) | |
929 | + nav_menu = wx.Menu() | |
930 | + nav_menu.Append(const.ID_MODE_NAVIGATION, _(u'Transcranial Magnetic Stimulation Mode\tCtrl+T'), "", wx.ITEM_CHECK) | |
931 | + #Under development | |
932 | + self.mode_dbs = nav_menu.Append(const.ID_MODE_DBS, _(u'Deep Brain Stimulation Mode\tCtrl+B'), "", wx.ITEM_CHECK) | |
933 | + self.mode_dbs.Enable(0) | |
934 | + mode_menu.Append(-1,_('Navigation Mode'),nav_menu) | |
911 | 935 | |
912 | 936 | v = self.NavigationModeStatus() |
913 | 937 | self.mode_menu.Check(const.ID_MODE_NAVIGATION, v) |
... | ... | @@ -950,7 +974,6 @@ class MenuBar(wx.MenuBar): |
950 | 974 | |
951 | 975 | def NavigationModeStatus(self): |
952 | 976 | status = int(ses.Session().mode) |
953 | - | |
954 | 977 | if status == 1: |
955 | 978 | return True |
956 | 979 | else: | ... | ... |
invesalius/gui/task_navigator.py
... | ... | @@ -172,6 +172,15 @@ class InnerFoldPanel(wx.Panel): |
172 | 172 | fold_panel.AddFoldPanelWindow(item, mtw, spacing= 0, |
173 | 173 | leftSpacing=0, rightSpacing=0) |
174 | 174 | |
175 | + # Fold 4 - DBS | |
176 | + | |
177 | + self.dbs_item = fold_panel.AddFoldPanel(_("Deep Brain Stimulation"), collapsed=True) | |
178 | + dtw = DbsPanel(self.dbs_item) #Atribuir nova var, criar panel | |
179 | + | |
180 | + fold_panel.ApplyCaptionStyle(self.dbs_item, style) | |
181 | + fold_panel.AddFoldPanelWindow(self.dbs_item, dtw, spacing= 0, | |
182 | + leftSpacing=0, rightSpacing=0) | |
183 | + self.dbs_item.Hide() | |
175 | 184 | |
176 | 185 | # Check box for camera update in volume rendering during navigation |
177 | 186 | tooltip = wx.ToolTip(_("Update camera in volume")) |
... | ... | @@ -220,11 +229,21 @@ class InnerFoldPanel(wx.Panel): |
220 | 229 | self.SetSizer(sizer) |
221 | 230 | self.Update() |
222 | 231 | self.SetAutoLayout(1) |
223 | - | |
232 | + | |
224 | 233 | def __bind_events(self): |
225 | 234 | Publisher.subscribe(self.OnCheckStatus, 'Navigation status') |
226 | 235 | Publisher.subscribe(self.OnShowObject, 'Update track object state') |
227 | 236 | Publisher.subscribe(self.OnVolumeCamera, 'Target navigation mode') |
237 | + Publisher.subscribe(self.OnShowDbs, "Active dbs folder") | |
238 | + Publisher.subscribe(self.OnHideDbs, "Deactive dbs folder") | |
239 | + | |
240 | + def OnShowDbs(self): | |
241 | + self.dbs_item.Show() | |
242 | + | |
243 | + | |
244 | + def OnHideDbs(self): | |
245 | + self.dbs_item.Hide() | |
246 | + | |
228 | 247 | |
229 | 248 | def OnCheckStatus(self, status): |
230 | 249 | if status: |
... | ... | @@ -258,7 +277,6 @@ class InnerFoldPanel(wx.Panel): |
258 | 277 | self.checkcamera.SetValue(0) |
259 | 278 | Publisher.sendMessage('Update volume camera state', camera_state=self.checkcamera.GetValue()) |
260 | 279 | |
261 | - | |
262 | 280 | class NeuronavigationPanel(wx.Panel): |
263 | 281 | def __init__(self, parent): |
264 | 282 | wx.Panel.__init__(self, parent) |
... | ... | @@ -1078,8 +1096,12 @@ class MarkersPanel(wx.Panel): |
1078 | 1096 | menu_id = wx.Menu() |
1079 | 1097 | edit_id = menu_id.Append(0, _('Edit ID')) |
1080 | 1098 | menu_id.Bind(wx.EVT_MENU, self.OnMenuEditMarkerId, edit_id) |
1099 | + color_id = menu_id.Append(2, _('Edit color')) | |
1100 | + menu_id.Bind(wx.EVT_MENU, self.OnMenuSetColor, color_id) | |
1101 | + menu_id.AppendSeparator() | |
1081 | 1102 | target_menu = menu_id.Append(1, _('Set as target')) |
1082 | 1103 | menu_id.Bind(wx.EVT_MENU, self.OnMenuSetTarget, target_menu) |
1104 | + | |
1083 | 1105 | target_menu.Enable(status) |
1084 | 1106 | self.PopupMenu(menu_id) |
1085 | 1107 | menu_id.Destroy() |
... | ... | @@ -1130,6 +1152,26 @@ class MarkersPanel(wx.Panel): |
1130 | 1152 | self.tgt_flag = True |
1131 | 1153 | dlg.NewTarget() |
1132 | 1154 | |
1155 | + def OnMenuSetColor(self, evt): | |
1156 | + index = self.lc.GetFocusedItem() | |
1157 | + cdata = wx.ColourData() | |
1158 | + cdata.SetColour(wx.Colour(self.list_coord[index][6]*255,self.list_coord[index][7]*255,self.list_coord[index][8]*255)) | |
1159 | + dlg = wx.ColourDialog(self, data=cdata) | |
1160 | + dlg.GetColourData().SetChooseFull(True) | |
1161 | + if dlg.ShowModal() == wx.ID_OK: | |
1162 | + self.r, self.g, self.b = dlg.GetColourData().GetColour().Get(includeAlpha=False) | |
1163 | + r = float(self.r) / 255.0 | |
1164 | + g = float(self.g) / 255.0 | |
1165 | + b = float(self.b) / 255.0 | |
1166 | + dlg.Destroy() | |
1167 | + color = [r,g,b] | |
1168 | + | |
1169 | + Publisher.sendMessage('Set new color', index=index, color=color) | |
1170 | + | |
1171 | + self.list_coord[index][6] = r | |
1172 | + self.list_coord[index][7] = g | |
1173 | + self.list_coord[index][8] = b | |
1174 | + | |
1133 | 1175 | def OnDeleteAllMarkers(self, evt=None): |
1134 | 1176 | if self.list_coord: |
1135 | 1177 | if evt is None: |
... | ... | @@ -1316,3 +1358,12 @@ class MarkersPanel(wx.Panel): |
1316 | 1358 | index = self.lc.GetNextSelected(index) |
1317 | 1359 | selection.append(index) |
1318 | 1360 | return selection |
1361 | + | |
1362 | +class DbsPanel(wx.Panel): | |
1363 | + def __init__(self, parent): | |
1364 | + wx.Panel.__init__(self, parent) | |
1365 | + try: | |
1366 | + default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR) | |
1367 | + except AttributeError: | |
1368 | + default_colour = wx.SystemSettings_GetColour(wx.SYS_COLOUR_MENUBAR) | |
1369 | + | ... | ... |