Commit ec2dd6ddb1ce3fc3306b15deca7478f7e7da22f8

Authored by okahilak
Committed by GitHub
1 parent 1de518dc
Exists in master

ADD: Checkbox to allow stimulating when coil is not at target (#393)

Add a "Lock to target" checkbox to the UI. When checked, only allow
triggering stimulation pulse if the coil is at the target. When unchecked,
allow triggering stimulation pulse regardless of the coil location.

Keep the checkbox disabled until a target is selected. When a target
is selected, enable and automatically check the checkbox so that the
default mode of operation is to allow triggering stimulation pulse
only when the coil is at the target.
invesalius/gui/task_navigator.py
@@ -397,16 +397,10 @@ class NeuronavigationPanel(wx.Panel): @@ -397,16 +397,10 @@ class NeuronavigationPanel(wx.Panel):
397 397
398 self.btns_set_fiducial[n + 3] = ctrl 398 self.btns_set_fiducial[n + 3] = ctrl
399 399
400 - # TODO: Find a better allignment between FRE, text and navigate button  
401 - txt_fre = wx.StaticText(self, -1, _('FRE:'))  
402 - txt_icp = wx.StaticText(self, -1, _('Refine:')) 400 + # TODO: Find a better alignment between FRE, text and navigate button
403 401
404 - if pedal_connection is not None and pedal_connection.in_use:  
405 - txt_pedal_pressed = wx.StaticText(self, -1, _('Pedal pressed:'))  
406 - else:  
407 - txt_pedal_pressed = None  
408 -  
409 - # Fiducial registration error text box 402 + # Fiducial registration error text and checkbox
  403 + txt_fre = wx.StaticText(self, -1, _('FRE:'))
410 tooltip = wx.ToolTip(_("Fiducial registration error")) 404 tooltip = wx.ToolTip(_("Fiducial registration error"))
411 txtctrl_fre = wx.TextCtrl(self, value="", size=wx.Size(60, -1), style=wx.TE_CENTRE) 405 txtctrl_fre = wx.TextCtrl(self, value="", size=wx.Size(60, -1), style=wx.TE_CENTRE)
412 txtctrl_fre.SetFont(wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.BOLD)) 406 txtctrl_fre.SetFont(wx.Font(9, wx.DEFAULT, wx.NORMAL, wx.BOLD))
@@ -421,6 +415,8 @@ class NeuronavigationPanel(wx.Panel): @@ -421,6 +415,8 @@ class NeuronavigationPanel(wx.Panel):
421 btn_nav.SetToolTip(tooltip) 415 btn_nav.SetToolTip(tooltip)
422 btn_nav.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnNavigate, btn_nav=btn_nav)) 416 btn_nav.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnNavigate, btn_nav=btn_nav))
423 417
  418 + # "Refine" text and checkbox
  419 + txt_icp = wx.StaticText(self, -1, _('Refine:'))
424 tooltip = wx.ToolTip(_(u"Refine the coregistration")) 420 tooltip = wx.ToolTip(_(u"Refine the coregistration"))
425 checkbox_icp = wx.CheckBox(self, -1, _(' ')) 421 checkbox_icp = wx.CheckBox(self, -1, _(' '))
426 checkbox_icp.SetValue(False) 422 checkbox_icp.SetValue(False)
@@ -429,8 +425,9 @@ class NeuronavigationPanel(wx.Panel): @@ -429,8 +425,9 @@ class NeuronavigationPanel(wx.Panel):
429 checkbox_icp.SetToolTip(tooltip) 425 checkbox_icp.SetToolTip(tooltip)
430 self.checkbox_icp = checkbox_icp 426 self.checkbox_icp = checkbox_icp
431 427
432 - # An indicator for pedal trigger 428 + # "Pedal pressed" text and an indicator (checkbox) for pedal press
433 if pedal_connection is not None and pedal_connection.in_use: 429 if pedal_connection is not None and pedal_connection.in_use:
  430 + txt_pedal_pressed = wx.StaticText(self, -1, _('Pedal pressed:'))
434 tooltip = wx.ToolTip(_(u"Is the pedal pressed")) 431 tooltip = wx.ToolTip(_(u"Is the pedal pressed"))
435 checkbox_pedal_pressed = wx.CheckBox(self, -1, _(' ')) 432 checkbox_pedal_pressed = wx.CheckBox(self, -1, _(' '))
436 checkbox_pedal_pressed.SetValue(False) 433 checkbox_pedal_pressed.SetValue(False)
@@ -441,15 +438,27 @@ class NeuronavigationPanel(wx.Panel): @@ -441,15 +438,27 @@ class NeuronavigationPanel(wx.Panel):
441 438
442 self.checkbox_pedal_pressed = checkbox_pedal_pressed 439 self.checkbox_pedal_pressed = checkbox_pedal_pressed
443 else: 440 else:
  441 + txt_pedal_pressed = None
444 self.checkbox_pedal_pressed = None 442 self.checkbox_pedal_pressed = None
445 443
  444 + # "Lock to target" text and checkbox
  445 + tooltip = wx.ToolTip(_(u"Allow triggering stimulation pulse only if the coil is at the target"))
  446 + lock_to_target_text = wx.StaticText(self, -1, _('Lock to target:'))
  447 + lock_to_target_checkbox = wx.CheckBox(self, -1, _(' '))
  448 + lock_to_target_checkbox.SetValue(False)
  449 + lock_to_target_checkbox.Enable(False)
  450 + lock_to_target_checkbox.Bind(wx.EVT_CHECKBOX, partial(self.OnLockToTargetCheckbox, ctrl=lock_to_target_checkbox))
  451 + lock_to_target_checkbox.SetToolTip(tooltip)
  452 +
  453 + self.lock_to_target_checkbox = lock_to_target_checkbox
  454 +
446 # Image and tracker coordinates number controls 455 # Image and tracker coordinates number controls
447 for m in range(len(self.btns_set_fiducial)): 456 for m in range(len(self.btns_set_fiducial)):
448 for n in range(3): 457 for n in range(3):
449 self.numctrls_fiducial[m].append( 458 self.numctrls_fiducial[m].append(
450 wx.lib.masked.numctrl.NumCtrl(parent=self, integerWidth=4, fractionWidth=1)) 459 wx.lib.masked.numctrl.NumCtrl(parent=self, integerWidth=4, fractionWidth=1))
451 460
452 - # Sizer to group all GUI objects 461 + # Sizers to group all GUI objects
453 choice_sizer = wx.FlexGridSizer(rows=1, cols=2, hgap=5, vgap=5) 462 choice_sizer = wx.FlexGridSizer(rows=1, cols=2, hgap=5, vgap=5)
454 choice_sizer.AddMany([(select_tracker_elem, wx.LEFT), 463 choice_sizer.AddMany([(select_tracker_elem, wx.LEFT),
455 (choice_ref, wx.RIGHT)]) 464 (choice_ref, wx.RIGHT)])
@@ -470,10 +479,13 @@ class NeuronavigationPanel(wx.Panel): @@ -470,10 +479,13 @@ class NeuronavigationPanel(wx.Panel):
470 (txt_icp, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL), 479 (txt_icp, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL),
471 (checkbox_icp, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL)]) 480 (checkbox_icp, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL)])
472 481
473 - pedal_sizer = wx.FlexGridSizer(rows=1, cols=2, hgap=5, vgap=5)  
474 - if HAS_PEDAL_CONNECTION and pedal_connection.in_use:  
475 - pedal_sizer.AddMany([(txt_pedal_pressed, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL),  
476 - (checkbox_pedal_pressed, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL)]) 482 + checkboxes_sizer = wx.FlexGridSizer(rows=1, cols=4, hgap=5, vgap=5)
  483 + checkboxes_sizer.AddMany([(lock_to_target_text, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL),
  484 + (lock_to_target_checkbox, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL)])
  485 +
  486 + if pedal_connection is not None and pedal_connection.in_use:
  487 + checkboxes_sizer.AddMany([(txt_pedal_pressed, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL),
  488 + (checkbox_pedal_pressed, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL)])
477 489
478 group_sizer = wx.FlexGridSizer(rows=10, cols=1, hgap=5, vgap=5) 490 group_sizer = wx.FlexGridSizer(rows=10, cols=1, hgap=5, vgap=5)
479 group_sizer.AddGrowableCol(0, 1) 491 group_sizer.AddGrowableCol(0, 1)
@@ -484,7 +496,7 @@ class NeuronavigationPanel(wx.Panel): @@ -484,7 +496,7 @@ class NeuronavigationPanel(wx.Panel):
484 group_sizer.AddMany([(choice_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL), 496 group_sizer.AddMany([(choice_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL),
485 (coord_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL), 497 (coord_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL),
486 (nav_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL), 498 (nav_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL),
487 - (pedal_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL)]) 499 + (checkboxes_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL)])
488 500
489 main_sizer = wx.BoxSizer(wx.HORIZONTAL) 501 main_sizer = wx.BoxSizer(wx.HORIZONTAL)
490 main_sizer.Add(group_sizer, 1)# wx.ALIGN_CENTER_HORIZONTAL, 10) 502 main_sizer.Add(group_sizer, 1)# wx.ALIGN_CENTER_HORIZONTAL, 10)
@@ -562,7 +574,6 @@ class NeuronavigationPanel(wx.Panel): @@ -562,7 +574,6 @@ class NeuronavigationPanel(wx.Panel):
562 self.ResetICP() 574 self.ResetICP()
563 self.tracker.UpdateUI(self.select_tracker_elem, self.numctrls_fiducial[3:6], self.txtctrl_fre) 575 self.tracker.UpdateUI(self.select_tracker_elem, self.numctrls_fiducial[3:6], self.txtctrl_fre)
564 576
565 -  
566 def UpdatePeelVisualization(self, data): 577 def UpdatePeelVisualization(self, data):
567 self.navigation.peel_loaded = data 578 self.navigation.peel_loaded = data
568 579
@@ -601,6 +612,10 @@ class NeuronavigationPanel(wx.Panel): @@ -601,6 +612,10 @@ class NeuronavigationPanel(wx.Panel):
601 def UpdateTarget(self, coord): 612 def UpdateTarget(self, coord):
602 self.navigation.target = coord 613 self.navigation.target = coord
603 614
  615 + self.lock_to_target_checkbox.Enable(True)
  616 + self.lock_to_target_checkbox.SetValue(True)
  617 + self.navigation.SetLockToTarget(True)
  618 +
604 def EnableACT(self, data): 619 def EnableACT(self, data):
605 self.navigation.enable_act = data 620 self.navigation.enable_act = data
606 621
@@ -629,6 +644,10 @@ class NeuronavigationPanel(wx.Panel): @@ -629,6 +644,10 @@ class NeuronavigationPanel(wx.Panel):
629 self.ResetICP() 644 self.ResetICP()
630 self.tracker.UpdateUI(self.select_tracker_elem, self.numctrls_fiducial[3:6], self.txtctrl_fre) 645 self.tracker.UpdateUI(self.select_tracker_elem, self.numctrls_fiducial[3:6], self.txtctrl_fre)
631 646
  647 + def OnLockToTargetCheckbox(self, evt, ctrl):
  648 + value = ctrl.GetValue()
  649 + self.navigation.SetLockToTarget(value)
  650 +
632 def OnChooseTracker(self, evt, ctrl): 651 def OnChooseTracker(self, evt, ctrl):
633 Publisher.sendMessage('Update status text in GUI', 652 Publisher.sendMessage('Update status text in GUI',
634 label=_("Configuring tracker ...")) 653 label=_("Configuring tracker ..."))
@@ -1352,7 +1371,7 @@ class MarkersPanel(wx.Panel): @@ -1352,7 +1371,7 @@ class MarkersPanel(wx.Panel):
1352 1371
1353 @staticmethod 1372 @staticmethod
1354 def __list_fiducial_labels(): 1373 def __list_fiducial_labels():
1355 - """Return the list of marker labels denoting fucials.""" 1374 + """Return the list of marker labels denoting fiducials."""
1356 return list(itertools.chain(*(const.BTNS_IMG_MARKERS[i].values() for i in const.BTNS_IMG_MARKERS))) 1375 return list(itertools.chain(*(const.BTNS_IMG_MARKERS[i].values() for i in const.BTNS_IMG_MARKERS)))
1357 1376
1358 def UpdateCurrentCoord(self, position): 1377 def UpdateCurrentCoord(self, position):
invesalius/navigation/navigation.py
@@ -174,6 +174,7 @@ class Navigation(): @@ -174,6 +174,7 @@ class Navigation():
174 self.serial_port_connection = None 174 self.serial_port_connection = None
175 175
176 # During navigation 176 # During navigation
  177 + self.lock_to_target = False
177 self.coil_at_target = False 178 self.coil_at_target = False
178 179
179 self.__bind_events() 180 self.__bind_events()
@@ -194,6 +195,9 @@ class Navigation(): @@ -194,6 +195,9 @@ class Navigation():
194 self.com_port = com_port 195 self.com_port = com_port
195 self.baud_rate = baud_rate 196 self.baud_rate = baud_rate
196 197
  198 + def SetLockToTarget(self, value):
  199 + self.lock_to_target = value
  200 +
197 def SetReferenceMode(self, value): 201 def SetReferenceMode(self, value):
198 self.ref_mode_id = value 202 self.ref_mode_id = value
199 203
@@ -220,7 +224,13 @@ class Navigation(): @@ -220,7 +224,13 @@ class Navigation():
220 return fre, fre <= const.FIDUCIAL_REGISTRATION_ERROR_THRESHOLD 224 return fre, fre <= const.FIDUCIAL_REGISTRATION_ERROR_THRESHOLD
221 225
222 def PedalStateChanged(self, state): 226 def PedalStateChanged(self, state):
223 - if state is True and self.coil_at_target and self.serial_port_in_use: 227 + if not self.serial_port_in_use:
  228 + return
  229 +
  230 + permission_to_stimulate = (self.lock_to_target and self.coil_at_target) or \
  231 + not self.lock_to_target
  232 +
  233 + if state and permission_to_stimulate:
224 self.serial_port_connection.SendPulse() 234 self.serial_port_connection.SendPulse()
225 235
226 def StartNavigation(self, tracker): 236 def StartNavigation(self, tracker):