Commit 29ce17eb290f1a613260249cec833455c19bfa74
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).
Showing
3 changed files
with
104 additions
and
43 deletions
Show diff stats
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() | ... | ... |