Commit 29ce17eb290f1a613260249cec833455c19bfa74

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

MOD: Allow setting object fiducials using pedal (#355)

* Add OBJECT_FIDUCIALS dict to constants.py, replacing BTNS_OBJ
  and TIPS_OBJ, and facilitating cleaning up the code.

* Pass PedalConnection object to ObjectCalibrationDialog so that
  the pedal can be used for object calibration.

* Copy and slightly adapt the code that was used to set tracker
  fiducials using pedal (in OnTrackerFiducials in NeuronavigationPanel
  class) to do the same for object fiducials. Mark code duplication with
  TODO comment for later clean-up.

* Rename a function (OnGetObjectFiducials -> OnObjectFiducialButton),
  and rename a few variables (e.g., btn_id -> fiducial_index).
invesalius/constants.py
... ... @@ -761,17 +761,38 @@ OBJA = wx.NewId()
761 761 OBJC = wx.NewId()
762 762 OBJF = wx.NewId()
763 763  
764   -BTNS_OBJ = {OBJL: {0: _('Left')},
765   - OBJR: {1: _('Right')},
766   - OBJA: {2: _('Anterior')},
767   - OBJC: {3: _('Center')},
768   - OBJF: {4: _('Fixed')}}
769   -
770   -TIPS_OBJ = [_("Select left object fiducial"),
771   - _("Select right object fiducial"),
772   - _("Select anterior object fiducial"),
773   - _("Select object center"),
774   - _("Attach sensor to object")]
  764 +OBJECT_FIDUCIALS = [
  765 + {
  766 + 'fiducial_index': 0,
  767 + 'button_id': OBJL,
  768 + 'label': _('Left'),
  769 + 'tip': _("Select left object fiducial"),
  770 + },
  771 + {
  772 + 'fiducial_index': 1,
  773 + 'button_id': OBJR,
  774 + 'label': _('Right'),
  775 + 'tip': _("Select right object fiducial"),
  776 + },
  777 + {
  778 + 'fiducial_index': 2,
  779 + 'button_id': OBJA,
  780 + 'label': _('Anterior'),
  781 + 'tip': _("Select anterior object fiducial"),
  782 + },
  783 + {
  784 + 'fiducial_index': 3,
  785 + 'button_id': OBJC,
  786 + 'label': _('Center'),
  787 + 'tip': _("Select object center"),
  788 + },
  789 + {
  790 + 'fiducial_index': 4,
  791 + 'button_id': OBJF,
  792 + 'label': _('Fixed'),
  793 + 'tip': _("Attach sensor to object"),
  794 + },
  795 +]
775 796  
776 797 MTC_PROBE_NAME = "1Probe"
777 798 MTC_REF_NAME = "2Ref"
... ...
invesalius/gui/dialogs.py
... ... @@ -3304,8 +3304,9 @@ class MaskDensityDialog(wx.Dialog):
3304 3304  
3305 3305 class ObjectCalibrationDialog(wx.Dialog):
3306 3306  
3307   - def __init__(self, tracker):
  3307 + def __init__(self, tracker, pedal_connection):
3308 3308 self.tracker = tracker
  3309 + self.pedal_connection = pedal_connection
3309 3310  
3310 3311 self.trk_init, self.tracker_id = tracker.GetTrackerInfo()
3311 3312  
... ... @@ -3313,6 +3314,7 @@ class ObjectCalibrationDialog(wx.Dialog):
3313 3314 self.obj_name = None
3314 3315 self.polydata = None
3315 3316 self.use_default_object = False
  3317 + self.object_fiducial_being_set = None
3316 3318  
3317 3319 self.obj_fiducials = np.full([5, 3], np.nan)
3318 3320 self.obj_orients = np.full([5, 3], np.nan)
... ... @@ -3323,6 +3325,11 @@ class ObjectCalibrationDialog(wx.Dialog):
3323 3325 self._init_gui()
3324 3326 self.LoadObject()
3325 3327  
  3328 + self.__bind_events()
  3329 +
  3330 + def __bind_events(self):
  3331 + Publisher.subscribe(self.SetObjectFiducial, 'Set object fiducial')
  3332 +
3326 3333 def _init_gui(self):
3327 3334 self.interactor = wxVTKRenderWindowInteractor(self, -1, size=self.GetSize())
3328 3335 self.interactor.Enable(1)
... ... @@ -3373,15 +3380,17 @@ class ObjectCalibrationDialog(wx.Dialog):
3373 3380 choice_sensor])
3374 3381  
3375 3382 # Push buttons for object fiducials
3376   - btns_obj = const.BTNS_OBJ
3377   - tips_obj = const.TIPS_OBJ
  3383 + for object_fiducial in const.OBJECT_FIDUCIALS:
  3384 + index = object_fiducial['fiducial_index']
  3385 + label = object_fiducial['label']
  3386 + button_id = object_fiducial['button_id']
  3387 + tip = object_fiducial['tip']
3378 3388  
3379   - for k in btns_obj:
3380   - n = list(btns_obj[k].keys())[0]
3381   - lab = list(btns_obj[k].values())[0]
3382   - self.btns_coord[n] = wx.Button(self, k, label=lab, size=wx.Size(60, 23))
3383   - self.btns_coord[n].SetToolTip(wx.ToolTip(tips_obj[n]))
3384   - self.btns_coord[n].Bind(wx.EVT_BUTTON, self.OnGetObjectFiducials)
  3389 + ctrl = wx.ToggleButton(self, button_id, label=label, size=wx.Size(60, 23))
  3390 + ctrl.SetToolTip(wx.ToolTip(tip))
  3391 + ctrl.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnObjectFiducialButton, index, ctrl=ctrl))
  3392 +
  3393 + self.btns_coord[index] = ctrl
3385 3394  
3386 3395 for m in range(0, 5):
3387 3396 for n in range(0, 3):
... ... @@ -3525,13 +3534,42 @@ class ObjectCalibrationDialog(wx.Dialog):
3525 3534 self.ren.AddActor(ball_actor)
3526 3535 return ball_actor, tactor
3527 3536  
3528   - def OnGetObjectFiducials(self, evt):
  3537 + def OnObjectFiducialButton(self, index, evt, ctrl):
3529 3538 if not self.tracker.IsTrackerInitialized():
3530 3539 ShowNavigationTrackerWarning(0, 'choose')
3531 3540 return
3532 3541  
3533   - btn_id = list(const.BTNS_OBJ[evt.GetId()].keys())[0]
  3542 + # TODO: The code below until the end of the function is essentially copy-paste from
  3543 + # OnTrackerFiducials function in NeuronavigationPanel class. Probably the easiest
  3544 + # way to deduplicate this would be to create a Fiducial class, which would contain
  3545 + # this code just once.
  3546 + #
  3547 +
  3548 + # Do not allow several object fiducials to be set at the same time.
  3549 + if self.object_fiducial_being_set is not None and self.object_fiducial_being_set != index:
  3550 + ctrl.SetValue(False)
  3551 + return
  3552 +
  3553 + # Called when the button for setting the object fiducial is enabled and either pedal is pressed
  3554 + # or the button is pressed again.
  3555 + #
  3556 + def set_fiducial_callback():
  3557 + Publisher.sendMessage('Set object fiducial', fiducial_index=index)
  3558 + if self.pedal_connection is not None:
  3559 + self.pedal_connection.remove_callback('fiducial')
  3560 +
  3561 + ctrl.SetValue(False)
  3562 + self.object_fiducial_being_set = None
  3563 +
  3564 + if ctrl.GetValue():
  3565 + self.object_fiducial_being_set = index
  3566 +
  3567 + if self.pedal_connection is not None:
  3568 + self.pedal_connection.add_callback('fiducial', set_fiducial_callback)
  3569 + else:
  3570 + set_fiducial_callback()
3534 3571  
  3572 + def SetObjectFiducial(self, fiducial_index):
3535 3573 coord, coord_raw = self.tracker.GetTrackerCoordinates(
3536 3574 # XXX: Always use static reference mode when getting the coordinates. This is what the
3537 3575 # code did previously, as well. At some point, it should probably be thought through
... ... @@ -3549,22 +3587,22 @@ class ObjectCalibrationDialog(wx.Dialog):
3549 3587 # mode" principle above, but it's hard to come up with a simple change to increase the consistency
3550 3588 # and not change the function to the point of potentially breaking it.)
3551 3589 #
3552   - if self.obj_ref_id and btn_id == 4:
  3590 + if self.obj_ref_id and fiducial_index == 4:
3553 3591 coord = coord_raw[self.obj_ref_id, :]
3554 3592 coord[2] = -coord[2]
3555 3593  
3556   - if btn_id == 3:
  3594 + if fiducial_index == 3:
3557 3595 coord = np.zeros([6,])
3558 3596  
3559 3597 # Update text controls with tracker coordinates
3560 3598 if coord is not None or np.sum(coord) != 0.0:
3561   - self.obj_fiducials[btn_id, :] = coord[:3]
3562   - self.obj_orients[btn_id, :] = coord[3:]
3563   - for n in [0, 1, 2]:
3564   - self.txt_coord[btn_id][n].SetLabel(str(round(coord[n], 1)))
3565   - if self.text_actors[btn_id]:
3566   - self.text_actors[btn_id].GetProperty().SetColor(0.0, 1.0, 0.0)
3567   - self.ball_actors[btn_id].GetProperty().SetColor(0.0, 1.0, 0.0)
  3599 + self.obj_fiducials[fiducial_index, :] = coord[:3]
  3600 + self.obj_orients[fiducial_index, :] = coord[3:]
  3601 + for i in [0, 1, 2]:
  3602 + self.txt_coord[fiducial_index][i].SetLabel(str(round(coord[i], 1)))
  3603 + if self.text_actors[fiducial_index]:
  3604 + self.text_actors[fiducial_index].GetProperty().SetColor(0.0, 1.0, 0.0)
  3605 + self.ball_actors[fiducial_index].GetProperty().SetColor(0.0, 1.0, 0.0)
3568 3606 self.Refresh()
3569 3607 else:
3570 3608 ShowNavigationTrackerWarning(0, 'choose')
... ...
invesalius/gui/task_navigator.py
... ... @@ -162,9 +162,10 @@ class InnerFoldPanel(wx.Panel):
162 162 fold_panel = fpb.FoldPanelBar(self, -1, wx.DefaultPosition,
163 163 (10, 310), 0, fpb.FPB_SINGLE_FOLD)
164 164  
165   - # Initialize Tracker object here so that it is available to several panels.
  165 + # Initialize Tracker and PedalConnection objects here so that they are available to several panels.
166 166 #
167 167 tracker = Tracker()
  168 + pedal_connection = PedalConnection() if HAS_PEDAL_CONNECTION else None
168 169  
169 170 # Fold panel style
170 171 style = fpb.CaptionBarStyle()
... ... @@ -174,7 +175,7 @@ class InnerFoldPanel(wx.Panel):
174 175  
175 176 # Fold 1 - Navigation panel
176 177 item = fold_panel.AddFoldPanel(_("Neuronavigation"), collapsed=True)
177   - ntw = NeuronavigationPanel(item, tracker)
  178 + ntw = NeuronavigationPanel(item, tracker, pedal_connection)
178 179  
179 180 fold_panel.ApplyCaptionStyle(item, style)
180 181 fold_panel.AddFoldPanelWindow(item, ntw, spacing=0,
... ... @@ -183,7 +184,7 @@ class InnerFoldPanel(wx.Panel):
183 184  
184 185 # Fold 2 - Object registration panel
185 186 item = fold_panel.AddFoldPanel(_("Object registration"), collapsed=True)
186   - otw = ObjectRegistrationPanel(item, tracker)
  187 + otw = ObjectRegistrationPanel(item, tracker, pedal_connection)
187 188  
188 189 fold_panel.ApplyCaptionStyle(item, style)
189 190 fold_panel.AddFoldPanelWindow(item, otw, spacing=0,
... ... @@ -372,7 +373,7 @@ class ICP():
372 373 self.icp_fre = None
373 374  
374 375 class NeuronavigationPanel(wx.Panel):
375   - def __init__(self, parent, tracker):
  376 + def __init__(self, parent, tracker, pedal_connection):
376 377 wx.Panel.__init__(self, parent)
377 378 try:
378 379 default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR)
... ... @@ -385,9 +386,9 @@ class NeuronavigationPanel(wx.Panel):
385 386 self.__bind_events()
386 387  
387 388 # Initialize global variables
388   - self.pedal_connection = PedalConnection() if HAS_PEDAL_CONNECTION else None
  389 + self.pedal_connection = pedal_connection
389 390 self.navigation = Navigation(
390   - pedal_connection=self.pedal_connection,
  391 + pedal_connection=pedal_connection,
391 392 )
392 393 self.icp = ICP()
393 394 self.tracker = tracker
... ... @@ -451,7 +452,7 @@ class NeuronavigationPanel(wx.Panel):
451 452 txt_fre = wx.StaticText(self, -1, _('FRE:'))
452 453 txt_icp = wx.StaticText(self, -1, _('Refine:'))
453 454  
454   - if self.pedal_connection is not None and self.pedal_connection.in_use:
  455 + if pedal_connection is not None and pedal_connection.in_use:
455 456 txt_pedal_pressed = wx.StaticText(self, -1, _('Pedal pressed:'))
456 457 else:
457 458 txt_pedal_pressed = None
... ... @@ -480,14 +481,14 @@ class NeuronavigationPanel(wx.Panel):
480 481 self.checkbox_icp = checkbox_icp
481 482  
482 483 # An indicator for pedal trigger
483   - if self.pedal_connection is not None and self.pedal_connection.in_use:
  484 + if pedal_connection is not None and pedal_connection.in_use:
484 485 tooltip = wx.ToolTip(_(u"Is the pedal pressed"))
485 486 checkbox_pedal_pressed = wx.CheckBox(self, -1, _(' '))
486 487 checkbox_pedal_pressed.SetValue(False)
487 488 checkbox_pedal_pressed.Enable(False)
488 489 checkbox_pedal_pressed.SetToolTip(tooltip)
489 490  
490   - self.pedal_connection.add_callback('gui', checkbox_pedal_pressed.SetValue)
  491 + pedal_connection.add_callback('gui', checkbox_pedal_pressed.SetValue)
491 492  
492 493 self.checkbox_pedal_pressed = checkbox_pedal_pressed
493 494 else:
... ... @@ -521,7 +522,7 @@ class NeuronavigationPanel(wx.Panel):
521 522 (checkbox_icp, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL)])
522 523  
523 524 pedal_sizer = wx.FlexGridSizer(rows=1, cols=2, hgap=5, vgap=5)
524   - if HAS_PEDAL_CONNECTION and self.pedal_connection.in_use:
  525 + if HAS_PEDAL_CONNECTION and pedal_connection.in_use:
525 526 pedal_sizer.AddMany([(txt_pedal_pressed, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL),
526 527 (checkbox_pedal_pressed, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL)])
527 528  
... ... @@ -872,7 +873,7 @@ class NeuronavigationPanel(wx.Panel):
872 873  
873 874  
874 875 class ObjectRegistrationPanel(wx.Panel):
875   - def __init__(self, parent, tracker):
  876 + def __init__(self, parent, tracker, pedal_connection):
876 877 wx.Panel.__init__(self, parent)
877 878 try:
878 879 default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR)
... ... @@ -883,6 +884,7 @@ class ObjectRegistrationPanel(wx.Panel):
883 884 self.coil_list = const.COIL
884 885  
885 886 self.tracker = tracker
  887 + self.pedal_connection = pedal_connection
886 888  
887 889 self.nav_prop = None
888 890 self.obj_fiducials = None
... ... @@ -1045,7 +1047,7 @@ class ObjectRegistrationPanel(wx.Panel):
1045 1047 def OnLinkCreate(self, event=None):
1046 1048  
1047 1049 if self.tracker.IsTrackerInitialized():
1048   - dialog = dlg.ObjectCalibrationDialog(self.tracker)
  1050 + dialog = dlg.ObjectCalibrationDialog(self.tracker, self.pedal_connection)
1049 1051 try:
1050 1052 if dialog.ShowModal() == wx.ID_OK:
1051 1053 self.obj_fiducials, self.obj_orients, self.obj_ref_mode, self.obj_name, polydata, use_default_object = dialog.GetValue()
... ...