Commit f64959b98097c05e706e81faa74a9aa67820b144

Authored by Renan
2 parents e35fada3 e1b52082
Exists in master

Merge branch 'master' into multimodal_tracking

# Conflicts:
#	invesalius/constants.py
invesalius/constants.py
@@ -830,11 +830,12 @@ TREKKER_CONFIG = {'seed_max': 1, 'step_size': 0.1, 'min_fod': 0.1, 'probe_qualit @@ -830,11 +830,12 @@ TREKKER_CONFIG = {'seed_max': 1, 'step_size': 0.1, 'min_fod': 0.1, 'probe_qualit
830 830
831 MARKER_FILE_MAGICK_STRING = "##INVESALIUS3_MARKER_FILE_" 831 MARKER_FILE_MAGICK_STRING = "##INVESALIUS3_MARKER_FILE_"
832 CURRENT_MARKER_FILE_VERSION = 0 832 CURRENT_MARKER_FILE_VERSION = 0
833 -WILDCARD_MARKER_FILES = _("Marker scanner coord files (*.mkss)|*.mkss") 833 +WILDCARD_MARKER_FILES = _("Marker scanner coord files (*.mkss)|*.mkss")
834 834
835 # Serial port 835 # Serial port
836 BAUD_RATES = [300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200] 836 BAUD_RATES = [300, 1200, 2400, 4800, 9600, 19200, 38400, 57600, 115200]
837 BAUD_RATE_DEFAULT_SELECTION = 4 837 BAUD_RATE_DEFAULT_SELECTION = 4
  838 +PULSE_DURATION_IN_MILLISECONDS = 0.2
838 839
839 #Robot 840 #Robot
840 ROBOT_ElFIN_IP = ['143.107.220.251', '169.254.153.251', '127.0.0.1'] 841 ROBOT_ElFIN_IP = ['143.107.220.251', '169.254.153.251', '127.0.0.1']
@@ -843,4 +844,3 @@ ROBOT_MOTIONS = {"normal": 0, "linear out": 1, "arc": 2} @@ -843,4 +844,3 @@ ROBOT_MOTIONS = {"normal": 0, "linear out": 1, "arc": 2}
843 ROBOT_HEAD_VELOCITY_THRESHOLD = 10 844 ROBOT_HEAD_VELOCITY_THRESHOLD = 10
844 ROBOT_ARC_THRESHOLD_DISTANCE = 100 845 ROBOT_ARC_THRESHOLD_DISTANCE = 100
845 ROBOT_VERSOR_SCALE_FACTOR = 70 846 ROBOT_VERSOR_SCALE_FACTOR = 70
846 -  
invesalius/data/serial_port_connection.py
@@ -22,12 +22,12 @@ import threading @@ -22,12 +22,12 @@ import threading
22 import time 22 import time
23 23
24 import wx 24 import wx
  25 +
  26 +from invesalius import constants
25 from invesalius.pubsub import pub as Publisher 27 from invesalius.pubsub import pub as Publisher
26 28
27 29
28 class SerialPortConnection(threading.Thread): 30 class SerialPortConnection(threading.Thread):
29 - BINARY_PULSE = b'\x01'  
30 -  
31 def __init__(self, com_port, baud_rate, serial_port_queue, event, sleep_nav): 31 def __init__(self, com_port, baud_rate, serial_port_queue, event, sleep_nav):
32 """ 32 """
33 Thread created to communicate using the serial port to interact with software during neuronavigation. 33 Thread created to communicate using the serial port to interact with software during neuronavigation.
@@ -65,7 +65,7 @@ class SerialPortConnection(threading.Thread): @@ -65,7 +65,7 @@ class SerialPortConnection(threading.Thread):
65 65
66 def SendPulse(self): 66 def SendPulse(self):
67 try: 67 try:
68 - self.connection.write(self.BINARY_PULSE) 68 + self.connection.send_break(constants.PULSE_DURATION_IN_MILLISECONDS / 1000)
69 Publisher.sendMessage('Serial port pulse triggered') 69 Publisher.sendMessage('Serial port pulse triggered')
70 except: 70 except:
71 print("Error: Serial port could not be written into.") 71 print("Error: Serial port could not be written into.")
invesalius/data/trackers.py
@@ -301,9 +301,11 @@ def PlhSerialConnection(tracker_id): @@ -301,9 +301,11 @@ def PlhSerialConnection(tracker_id):
301 trck_init = None 301 trck_init = None
302 dlg_port = dlg.SetCOMPort(select_baud_rate=False) 302 dlg_port = dlg.SetCOMPort(select_baud_rate=False)
303 if dlg_port.ShowModal() == ID_OK: 303 if dlg_port.ShowModal() == ID_OK:
304 - com_port = dlg_port.GetValue() 304 + com_port = dlg_port.GetCOMPort()
  305 + baud_rate = 115200
  306 +
305 try: 307 try:
306 - trck_init = serial.Serial(com_port, baudrate=115200, timeout=0.03) 308 + trck_init = serial.Serial(com_port, baudrate=baud_rate, timeout=0.03)
307 309
308 if tracker_id == 2: 310 if tracker_id == 2:
309 # Polhemus FASTRAK needs configurations first 311 # Polhemus FASTRAK needs configurations first
invesalius/data/viewer_volume.py
@@ -726,9 +726,13 @@ class Viewer(wx.Panel): @@ -726,9 +726,13 @@ class Viewer(wx.Panel):
726 self.distthreshold = dist_threshold 726 self.distthreshold = dist_threshold
727 727
728 def ActivateTargetMode(self, evt=None, target_mode=None): 728 def ActivateTargetMode(self, evt=None, target_mode=None):
  729 +
729 vtk_colors = vtk.vtkNamedColors() 730 vtk_colors = vtk.vtkNamedColors()
730 self.target_mode = target_mode 731 self.target_mode = target_mode
731 if self.target_coord and self.target_mode: 732 if self.target_coord and self.target_mode:
  733 + if self.actor_peel:
  734 + self.object_orientation_torus_actor.SetVisibility(0)
  735 + self.obj_projection_arrow_actor.SetVisibility(0)
732 self.CreateTargetAim() 736 self.CreateTargetAim()
733 737
734 # Create a line 738 # Create a line
@@ -831,6 +835,9 @@ class Viewer(wx.Panel): @@ -831,6 +835,9 @@ class Viewer(wx.Panel):
831 835
832 else: 836 else:
833 self.DisableCoilTracker() 837 self.DisableCoilTracker()
  838 + if self.actor_peel:
  839 + self.object_orientation_torus_actor.SetVisibility(1)
  840 + self.obj_projection_arrow_actor.SetVisibility(1)
834 841
835 def OnUpdateObjectTargetGuide(self, m_img, coord): 842 def OnUpdateObjectTargetGuide(self, m_img, coord):
836 843
invesalius/gui/dialogs.py
@@ -4809,15 +4809,16 @@ class SetCOMPort(wx.Dialog): @@ -4809,15 +4809,16 @@ class SetCOMPort(wx.Dialog):
4809 4809
4810 self.CenterOnParent() 4810 self.CenterOnParent()
4811 4811
4812 - def GetValue(self): 4812 + def GetCOMPort(self):
4813 com_port = self.com_port_dropdown.GetString(self.com_port_dropdown.GetSelection()) 4813 com_port = self.com_port_dropdown.GetString(self.com_port_dropdown.GetSelection())
  4814 + return com_port
4814 4815
4815 - if self.select_baud_rate:  
4816 - baud_rate = self.baud_rate_dropdown.GetString(self.baud_rate_dropdown.GetSelection())  
4817 - else:  
4818 - baud_rate = None 4816 + def GetBaudRate(self):
  4817 + if not self.select_baud_rate:
  4818 + return None
4819 4819
4820 - return com_port, baud_rate 4820 + baud_rate = self.baud_rate_dropdown.GetString(self.baud_rate_dropdown.GetSelection())
  4821 + return baud_rate
4821 4822
4822 4823
4823 class ManualWWWLDialog(wx.Dialog): 4824 class ManualWWWLDialog(wx.Dialog):
invesalius/gui/task_navigator.py
@@ -305,7 +305,7 @@ class InnerFoldPanel(wx.Panel): @@ -305,7 +305,7 @@ class InnerFoldPanel(wx.Panel):
305 ctrl.SetValue(False) 305 ctrl.SetValue(False)
306 return 306 return
307 307
308 - com_port = dlg_port.GetValue() 308 + com_port = dlg_port.GetCOMPort()
309 baud_rate = 115200 309 baud_rate = 115200
310 310
311 Publisher.sendMessage('Update serial port', serial_port_in_use=True, com_port=com_port, baud_rate=baud_rate) 311 Publisher.sendMessage('Update serial port', serial_port_in_use=True, com_port=com_port, baud_rate=baud_rate)
@@ -410,16 +410,10 @@ class NeuronavigationPanel(wx.Panel): @@ -410,16 +410,10 @@ class NeuronavigationPanel(wx.Panel):
410 410
411 self.btns_set_fiducial[n + 3] = ctrl 411 self.btns_set_fiducial[n + 3] = ctrl
412 412
413 - # TODO: Find a better allignment between FRE, text and navigate button  
414 - txt_fre = wx.StaticText(self, -1, _('FRE:'))  
415 - txt_icp = wx.StaticText(self, -1, _('Refine:')) 413 + # TODO: Find a better alignment between FRE, text and navigate button
416 414
417 - if pedal_connection is not None and pedal_connection.in_use:  
418 - txt_pedal_pressed = wx.StaticText(self, -1, _('Pedal pressed:'))  
419 - else:  
420 - txt_pedal_pressed = None  
421 -  
422 - # Fiducial registration error text box 415 + # Fiducial registration error text and checkbox
  416 + txt_fre = wx.StaticText(self, -1, _('FRE:'))
423 tooltip = wx.ToolTip(_("Fiducial registration error")) 417 tooltip = wx.ToolTip(_("Fiducial registration error"))
424 txtctrl_fre = wx.TextCtrl(self, value="", size=wx.Size(60, -1), style=wx.TE_CENTRE) 418 txtctrl_fre = wx.TextCtrl(self, value="", size=wx.Size(60, -1), style=wx.TE_CENTRE)
425 txtctrl_fre.SetFont(wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.BOLD)) 419 txtctrl_fre.SetFont(wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.BOLD))
@@ -434,6 +428,8 @@ class NeuronavigationPanel(wx.Panel): @@ -434,6 +428,8 @@ class NeuronavigationPanel(wx.Panel):
434 btn_nav.SetToolTip(tooltip) 428 btn_nav.SetToolTip(tooltip)
435 btn_nav.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnNavigate, btn_nav=btn_nav)) 429 btn_nav.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnNavigate, btn_nav=btn_nav))
436 430
  431 + # "Refine" text and checkbox
  432 + txt_icp = wx.StaticText(self, -1, _('Refine:'))
437 tooltip = wx.ToolTip(_(u"Refine the coregistration")) 433 tooltip = wx.ToolTip(_(u"Refine the coregistration"))
438 checkbox_icp = wx.CheckBox(self, -1, _(' ')) 434 checkbox_icp = wx.CheckBox(self, -1, _(' '))
439 checkbox_icp.SetValue(False) 435 checkbox_icp.SetValue(False)
@@ -442,8 +438,9 @@ class NeuronavigationPanel(wx.Panel): @@ -442,8 +438,9 @@ class NeuronavigationPanel(wx.Panel):
442 checkbox_icp.SetToolTip(tooltip) 438 checkbox_icp.SetToolTip(tooltip)
443 self.checkbox_icp = checkbox_icp 439 self.checkbox_icp = checkbox_icp
444 440
445 - # An indicator for pedal trigger 441 + # "Pedal pressed" text and an indicator (checkbox) for pedal press
446 if pedal_connection is not None and pedal_connection.in_use: 442 if pedal_connection is not None and pedal_connection.in_use:
  443 + txt_pedal_pressed = wx.StaticText(self, -1, _('Pedal pressed:'))
447 tooltip = wx.ToolTip(_(u"Is the pedal pressed")) 444 tooltip = wx.ToolTip(_(u"Is the pedal pressed"))
448 checkbox_pedal_pressed = wx.CheckBox(self, -1, _(' ')) 445 checkbox_pedal_pressed = wx.CheckBox(self, -1, _(' '))
449 checkbox_pedal_pressed.SetValue(False) 446 checkbox_pedal_pressed.SetValue(False)
@@ -454,15 +451,27 @@ class NeuronavigationPanel(wx.Panel): @@ -454,15 +451,27 @@ class NeuronavigationPanel(wx.Panel):
454 451
455 self.checkbox_pedal_pressed = checkbox_pedal_pressed 452 self.checkbox_pedal_pressed = checkbox_pedal_pressed
456 else: 453 else:
  454 + txt_pedal_pressed = None
457 self.checkbox_pedal_pressed = None 455 self.checkbox_pedal_pressed = None
458 456
  457 + # "Lock to target" text and checkbox
  458 + tooltip = wx.ToolTip(_(u"Allow triggering stimulation pulse only if the coil is at the target"))
  459 + lock_to_target_text = wx.StaticText(self, -1, _('Lock to target:'))
  460 + lock_to_target_checkbox = wx.CheckBox(self, -1, _(' '))
  461 + lock_to_target_checkbox.SetValue(False)
  462 + lock_to_target_checkbox.Enable(False)
  463 + lock_to_target_checkbox.Bind(wx.EVT_CHECKBOX, partial(self.OnLockToTargetCheckbox, ctrl=lock_to_target_checkbox))
  464 + lock_to_target_checkbox.SetToolTip(tooltip)
  465 +
  466 + self.lock_to_target_checkbox = lock_to_target_checkbox
  467 +
459 # Image and tracker coordinates number controls 468 # Image and tracker coordinates number controls
460 for m in range(len(self.btns_set_fiducial)): 469 for m in range(len(self.btns_set_fiducial)):
461 for n in range(3): 470 for n in range(3):
462 self.numctrls_fiducial[m].append( 471 self.numctrls_fiducial[m].append(
463 wx.lib.masked.numctrl.NumCtrl(parent=self, integerWidth=4, fractionWidth=1)) 472 wx.lib.masked.numctrl.NumCtrl(parent=self, integerWidth=4, fractionWidth=1))
464 473
465 - # Sizer to group all GUI objects 474 + # Sizers to group all GUI objects
466 choice_sizer = wx.FlexGridSizer(rows=1, cols=2, hgap=5, vgap=5) 475 choice_sizer = wx.FlexGridSizer(rows=1, cols=2, hgap=5, vgap=5)
467 choice_sizer.AddMany([(select_tracker_elem, wx.LEFT), 476 choice_sizer.AddMany([(select_tracker_elem, wx.LEFT),
468 (choice_ref, wx.RIGHT)]) 477 (choice_ref, wx.RIGHT)])
@@ -483,10 +492,13 @@ class NeuronavigationPanel(wx.Panel): @@ -483,10 +492,13 @@ class NeuronavigationPanel(wx.Panel):
483 (txt_icp, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL), 492 (txt_icp, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL),
484 (checkbox_icp, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL)]) 493 (checkbox_icp, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL)])
485 494
486 - pedal_sizer = wx.FlexGridSizer(rows=1, cols=2, hgap=5, vgap=5)  
487 - if HAS_PEDAL_CONNECTION and pedal_connection.in_use:  
488 - pedal_sizer.AddMany([(txt_pedal_pressed, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL),  
489 - (checkbox_pedal_pressed, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL)]) 495 + checkboxes_sizer = wx.FlexGridSizer(rows=1, cols=4, hgap=5, vgap=5)
  496 + checkboxes_sizer.AddMany([(lock_to_target_text, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL),
  497 + (lock_to_target_checkbox, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL)])
  498 +
  499 + if pedal_connection is not None and pedal_connection.in_use:
  500 + checkboxes_sizer.AddMany([(txt_pedal_pressed, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL),
  501 + (checkbox_pedal_pressed, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL)])
490 502
491 group_sizer = wx.FlexGridSizer(rows=10, cols=1, hgap=5, vgap=5) 503 group_sizer = wx.FlexGridSizer(rows=10, cols=1, hgap=5, vgap=5)
492 group_sizer.AddGrowableCol(0, 1) 504 group_sizer.AddGrowableCol(0, 1)
@@ -497,7 +509,7 @@ class NeuronavigationPanel(wx.Panel): @@ -497,7 +509,7 @@ class NeuronavigationPanel(wx.Panel):
497 group_sizer.AddMany([(choice_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL), 509 group_sizer.AddMany([(choice_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL),
498 (coord_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL), 510 (coord_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL),
499 (nav_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL), 511 (nav_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL),
500 - (pedal_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL)]) 512 + (checkboxes_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL)])
501 513
502 main_sizer = wx.BoxSizer(wx.HORIZONTAL) 514 main_sizer = wx.BoxSizer(wx.HORIZONTAL)
503 main_sizer.Add(group_sizer, 1)# wx.ALIGN_CENTER_HORIZONTAL, 10) 515 main_sizer.Add(group_sizer, 1)# wx.ALIGN_CENTER_HORIZONTAL, 10)
@@ -575,7 +587,6 @@ class NeuronavigationPanel(wx.Panel): @@ -575,7 +587,6 @@ class NeuronavigationPanel(wx.Panel):
575 self.ResetICP() 587 self.ResetICP()
576 self.tracker.UpdateUI(self.select_tracker_elem, self.numctrls_fiducial[3:6], self.txtctrl_fre) 588 self.tracker.UpdateUI(self.select_tracker_elem, self.numctrls_fiducial[3:6], self.txtctrl_fre)
577 589
578 -  
579 def UpdatePeelVisualization(self, data): 590 def UpdatePeelVisualization(self, data):
580 self.navigation.peel_loaded = data 591 self.navigation.peel_loaded = data
581 592
@@ -614,6 +625,10 @@ class NeuronavigationPanel(wx.Panel): @@ -614,6 +625,10 @@ class NeuronavigationPanel(wx.Panel):
614 def UpdateTarget(self, coord): 625 def UpdateTarget(self, coord):
615 self.navigation.target = coord 626 self.navigation.target = coord
616 627
  628 + self.lock_to_target_checkbox.Enable(True)
  629 + self.lock_to_target_checkbox.SetValue(True)
  630 + self.navigation.SetLockToTarget(True)
  631 +
617 def EnableACT(self, data): 632 def EnableACT(self, data):
618 self.navigation.enable_act = data 633 self.navigation.enable_act = data
619 634
@@ -644,6 +659,10 @@ class NeuronavigationPanel(wx.Panel): @@ -644,6 +659,10 @@ class NeuronavigationPanel(wx.Panel):
644 self.ResetICP() 659 self.ResetICP()
645 self.tracker.UpdateUI(self.select_tracker_elem, self.numctrls_fiducial[3:6], self.txtctrl_fre) 660 self.tracker.UpdateUI(self.select_tracker_elem, self.numctrls_fiducial[3:6], self.txtctrl_fre)
646 661
  662 + def OnLockToTargetCheckbox(self, evt, ctrl):
  663 + value = ctrl.GetValue()
  664 + self.navigation.SetLockToTarget(value)
  665 +
647 def OnChooseTracker(self, evt, ctrl): 666 def OnChooseTracker(self, evt, ctrl):
648 Publisher.sendMessage('Update status text in GUI', 667 Publisher.sendMessage('Update status text in GUI',
649 label=_("Configuring tracker ...")) 668 label=_("Configuring tracker ..."))
@@ -1394,7 +1413,7 @@ class MarkersPanel(wx.Panel): @@ -1394,7 +1413,7 @@ class MarkersPanel(wx.Panel):
1394 1413
1395 @staticmethod 1414 @staticmethod
1396 def __list_fiducial_labels(): 1415 def __list_fiducial_labels():
1397 - """Return the list of marker labels denoting fucials.""" 1416 + """Return the list of marker labels denoting fiducials."""
1398 return list(itertools.chain(*(const.BTNS_IMG_MARKERS[i].values() for i in const.BTNS_IMG_MARKERS))) 1417 return list(itertools.chain(*(const.BTNS_IMG_MARKERS[i].values() for i in const.BTNS_IMG_MARKERS)))
1399 1418
1400 def UpdateCurrentCoord(self, position): 1419 def UpdateCurrentCoord(self, position):
invesalius/navigation/navigation.py
@@ -177,6 +177,7 @@ class Navigation(): @@ -177,6 +177,7 @@ class Navigation():
177 self.serial_port_connection = None 177 self.serial_port_connection = None
178 178
179 # During navigation 179 # During navigation
  180 + self.lock_to_target = False
180 self.coil_at_target = False 181 self.coil_at_target = False
181 182
182 self.__bind_events() 183 self.__bind_events()
@@ -197,6 +198,9 @@ class Navigation(): @@ -197,6 +198,9 @@ class Navigation():
197 self.com_port = com_port 198 self.com_port = com_port
198 self.baud_rate = baud_rate 199 self.baud_rate = baud_rate
199 200
  201 + def SetLockToTarget(self, value):
  202 + self.lock_to_target = value
  203 +
200 def SetReferenceMode(self, value): 204 def SetReferenceMode(self, value):
201 self.ref_mode_id = value 205 self.ref_mode_id = value
202 206
@@ -223,7 +227,13 @@ class Navigation(): @@ -223,7 +227,13 @@ class Navigation():
223 return fre, fre <= const.FIDUCIAL_REGISTRATION_ERROR_THRESHOLD 227 return fre, fre <= const.FIDUCIAL_REGISTRATION_ERROR_THRESHOLD
224 228
225 def PedalStateChanged(self, state): 229 def PedalStateChanged(self, state):
226 - if state is True and self.coil_at_target and self.serial_port_in_use: 230 + if not self.serial_port_in_use:
  231 + return
  232 +
  233 + permission_to_stimulate = (self.lock_to_target and self.coil_at_target) or \
  234 + not self.lock_to_target
  235 +
  236 + if state and permission_to_stimulate:
227 self.serial_port_connection.SendPulse() 237 self.serial_port_connection.SendPulse()
228 238
229 def StartNavigation(self, tracker): 239 def StartNavigation(self, tracker):