Commit f95ee382033e9f87e7452877aba92ddcd57154c1

Authored by otaviocarlos
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
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
... ... @@ -346,6 +346,7 @@ class UpperTaskPanel(wx.Panel):
346 346 self.fold_panel.GetFoldPanel(4).Show()
347 347  
348 348 else:
  349 + Publisher.sendMessage('Deactive target button')
349 350 self.fold_panel.GetFoldPanel(4).Hide()
350 351 self.sizer.Layout()
351 352  
... ...
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 +
... ...