diff --git a/invesalius/constants.py b/invesalius/constants.py index fc76e5a..196675e 100644 --- a/invesalius/constants.py +++ b/invesalius/constants.py @@ -761,17 +761,38 @@ OBJA = wx.NewId() OBJC = wx.NewId() OBJF = wx.NewId() -BTNS_OBJ = {OBJL: {0: _('Left')}, - OBJR: {1: _('Right')}, - OBJA: {2: _('Anterior')}, - OBJC: {3: _('Center')}, - OBJF: {4: _('Fixed')}} - -TIPS_OBJ = [_("Select left object fiducial"), - _("Select right object fiducial"), - _("Select anterior object fiducial"), - _("Select object center"), - _("Attach sensor to object")] +OBJECT_FIDUCIALS = [ + { + 'fiducial_index': 0, + 'button_id': OBJL, + 'label': _('Left'), + 'tip': _("Select left object fiducial"), + }, + { + 'fiducial_index': 1, + 'button_id': OBJR, + 'label': _('Right'), + 'tip': _("Select right object fiducial"), + }, + { + 'fiducial_index': 2, + 'button_id': OBJA, + 'label': _('Anterior'), + 'tip': _("Select anterior object fiducial"), + }, + { + 'fiducial_index': 3, + 'button_id': OBJC, + 'label': _('Center'), + 'tip': _("Select object center"), + }, + { + 'fiducial_index': 4, + 'button_id': OBJF, + 'label': _('Fixed'), + 'tip': _("Attach sensor to object"), + }, +] MTC_PROBE_NAME = "1Probe" MTC_REF_NAME = "2Ref" diff --git a/invesalius/gui/dialogs.py b/invesalius/gui/dialogs.py index 67c5a24..8df4abe 100644 --- a/invesalius/gui/dialogs.py +++ b/invesalius/gui/dialogs.py @@ -3304,8 +3304,9 @@ class MaskDensityDialog(wx.Dialog): class ObjectCalibrationDialog(wx.Dialog): - def __init__(self, tracker): + def __init__(self, tracker, pedal_connection): self.tracker = tracker + self.pedal_connection = pedal_connection self.trk_init, self.tracker_id = tracker.GetTrackerInfo() @@ -3313,6 +3314,7 @@ class ObjectCalibrationDialog(wx.Dialog): self.obj_name = None self.polydata = None self.use_default_object = False + self.object_fiducial_being_set = None self.obj_fiducials = np.full([5, 3], np.nan) self.obj_orients = np.full([5, 3], np.nan) @@ -3323,6 +3325,11 @@ class ObjectCalibrationDialog(wx.Dialog): self._init_gui() self.LoadObject() + self.__bind_events() + + def __bind_events(self): + Publisher.subscribe(self.SetObjectFiducial, 'Set object fiducial') + def _init_gui(self): self.interactor = wxVTKRenderWindowInteractor(self, -1, size=self.GetSize()) self.interactor.Enable(1) @@ -3373,15 +3380,17 @@ class ObjectCalibrationDialog(wx.Dialog): choice_sensor]) # Push buttons for object fiducials - btns_obj = const.BTNS_OBJ - tips_obj = const.TIPS_OBJ + for object_fiducial in const.OBJECT_FIDUCIALS: + index = object_fiducial['fiducial_index'] + label = object_fiducial['label'] + button_id = object_fiducial['button_id'] + tip = object_fiducial['tip'] - for k in btns_obj: - n = list(btns_obj[k].keys())[0] - lab = list(btns_obj[k].values())[0] - self.btns_coord[n] = wx.Button(self, k, label=lab, size=wx.Size(60, 23)) - self.btns_coord[n].SetToolTip(wx.ToolTip(tips_obj[n])) - self.btns_coord[n].Bind(wx.EVT_BUTTON, self.OnGetObjectFiducials) + ctrl = wx.ToggleButton(self, button_id, label=label, size=wx.Size(60, 23)) + ctrl.SetToolTip(wx.ToolTip(tip)) + ctrl.Bind(wx.EVT_TOGGLEBUTTON, partial(self.OnObjectFiducialButton, index, ctrl=ctrl)) + + self.btns_coord[index] = ctrl for m in range(0, 5): for n in range(0, 3): @@ -3525,13 +3534,42 @@ class ObjectCalibrationDialog(wx.Dialog): self.ren.AddActor(ball_actor) return ball_actor, tactor - def OnGetObjectFiducials(self, evt): + def OnObjectFiducialButton(self, index, evt, ctrl): if not self.tracker.IsTrackerInitialized(): ShowNavigationTrackerWarning(0, 'choose') return - btn_id = list(const.BTNS_OBJ[evt.GetId()].keys())[0] + # TODO: The code below until the end of the function is essentially copy-paste from + # OnTrackerFiducials function in NeuronavigationPanel class. Probably the easiest + # way to deduplicate this would be to create a Fiducial class, which would contain + # this code just once. + # + + # Do not allow several object fiducials to be set at the same time. + if self.object_fiducial_being_set is not None and self.object_fiducial_being_set != index: + ctrl.SetValue(False) + return + + # Called when the button for setting the object fiducial is enabled and either pedal is pressed + # or the button is pressed again. + # + def set_fiducial_callback(): + Publisher.sendMessage('Set object fiducial', fiducial_index=index) + if self.pedal_connection is not None: + self.pedal_connection.remove_callback('fiducial') + + ctrl.SetValue(False) + self.object_fiducial_being_set = None + + if ctrl.GetValue(): + self.object_fiducial_being_set = index + + if self.pedal_connection is not None: + self.pedal_connection.add_callback('fiducial', set_fiducial_callback) + else: + set_fiducial_callback() + def SetObjectFiducial(self, fiducial_index): coord, coord_raw = self.tracker.GetTrackerCoordinates( # XXX: Always use static reference mode when getting the coordinates. This is what the # code did previously, as well. At some point, it should probably be thought through @@ -3549,22 +3587,22 @@ class ObjectCalibrationDialog(wx.Dialog): # mode" principle above, but it's hard to come up with a simple change to increase the consistency # and not change the function to the point of potentially breaking it.) # - if self.obj_ref_id and btn_id == 4: + if self.obj_ref_id and fiducial_index == 4: coord = coord_raw[self.obj_ref_id, :] coord[2] = -coord[2] - if btn_id == 3: + if fiducial_index == 3: coord = np.zeros([6,]) # Update text controls with tracker coordinates if coord is not None or np.sum(coord) != 0.0: - self.obj_fiducials[btn_id, :] = coord[:3] - self.obj_orients[btn_id, :] = coord[3:] - for n in [0, 1, 2]: - self.txt_coord[btn_id][n].SetLabel(str(round(coord[n], 1))) - if self.text_actors[btn_id]: - self.text_actors[btn_id].GetProperty().SetColor(0.0, 1.0, 0.0) - self.ball_actors[btn_id].GetProperty().SetColor(0.0, 1.0, 0.0) + self.obj_fiducials[fiducial_index, :] = coord[:3] + self.obj_orients[fiducial_index, :] = coord[3:] + for i in [0, 1, 2]: + self.txt_coord[fiducial_index][i].SetLabel(str(round(coord[i], 1))) + if self.text_actors[fiducial_index]: + self.text_actors[fiducial_index].GetProperty().SetColor(0.0, 1.0, 0.0) + self.ball_actors[fiducial_index].GetProperty().SetColor(0.0, 1.0, 0.0) self.Refresh() else: ShowNavigationTrackerWarning(0, 'choose') diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 0651af6..446c2b5 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -162,9 +162,10 @@ class InnerFoldPanel(wx.Panel): fold_panel = fpb.FoldPanelBar(self, -1, wx.DefaultPosition, (10, 310), 0, fpb.FPB_SINGLE_FOLD) - # Initialize Tracker object here so that it is available to several panels. + # Initialize Tracker and PedalConnection objects here so that they are available to several panels. # tracker = Tracker() + pedal_connection = PedalConnection() if HAS_PEDAL_CONNECTION else None # Fold panel style style = fpb.CaptionBarStyle() @@ -174,7 +175,7 @@ class InnerFoldPanel(wx.Panel): # Fold 1 - Navigation panel item = fold_panel.AddFoldPanel(_("Neuronavigation"), collapsed=True) - ntw = NeuronavigationPanel(item, tracker) + ntw = NeuronavigationPanel(item, tracker, pedal_connection) fold_panel.ApplyCaptionStyle(item, style) fold_panel.AddFoldPanelWindow(item, ntw, spacing=0, @@ -183,7 +184,7 @@ class InnerFoldPanel(wx.Panel): # Fold 2 - Object registration panel item = fold_panel.AddFoldPanel(_("Object registration"), collapsed=True) - otw = ObjectRegistrationPanel(item, tracker) + otw = ObjectRegistrationPanel(item, tracker, pedal_connection) fold_panel.ApplyCaptionStyle(item, style) fold_panel.AddFoldPanelWindow(item, otw, spacing=0, @@ -372,7 +373,7 @@ class ICP(): self.icp_fre = None class NeuronavigationPanel(wx.Panel): - def __init__(self, parent, tracker): + def __init__(self, parent, tracker, pedal_connection): wx.Panel.__init__(self, parent) try: default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR) @@ -385,9 +386,9 @@ class NeuronavigationPanel(wx.Panel): self.__bind_events() # Initialize global variables - self.pedal_connection = PedalConnection() if HAS_PEDAL_CONNECTION else None + self.pedal_connection = pedal_connection self.navigation = Navigation( - pedal_connection=self.pedal_connection, + pedal_connection=pedal_connection, ) self.icp = ICP() self.tracker = tracker @@ -451,7 +452,7 @@ class NeuronavigationPanel(wx.Panel): txt_fre = wx.StaticText(self, -1, _('FRE:')) txt_icp = wx.StaticText(self, -1, _('Refine:')) - if self.pedal_connection is not None and self.pedal_connection.in_use: + if pedal_connection is not None and pedal_connection.in_use: txt_pedal_pressed = wx.StaticText(self, -1, _('Pedal pressed:')) else: txt_pedal_pressed = None @@ -480,14 +481,14 @@ class NeuronavigationPanel(wx.Panel): self.checkbox_icp = checkbox_icp # An indicator for pedal trigger - if self.pedal_connection is not None and self.pedal_connection.in_use: + if pedal_connection is not None and pedal_connection.in_use: tooltip = wx.ToolTip(_(u"Is the pedal pressed")) checkbox_pedal_pressed = wx.CheckBox(self, -1, _(' ')) checkbox_pedal_pressed.SetValue(False) checkbox_pedal_pressed.Enable(False) checkbox_pedal_pressed.SetToolTip(tooltip) - self.pedal_connection.add_callback('gui', checkbox_pedal_pressed.SetValue) + pedal_connection.add_callback('gui', checkbox_pedal_pressed.SetValue) self.checkbox_pedal_pressed = checkbox_pedal_pressed else: @@ -521,7 +522,7 @@ class NeuronavigationPanel(wx.Panel): (checkbox_icp, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL)]) pedal_sizer = wx.FlexGridSizer(rows=1, cols=2, hgap=5, vgap=5) - if HAS_PEDAL_CONNECTION and self.pedal_connection.in_use: + if HAS_PEDAL_CONNECTION and pedal_connection.in_use: pedal_sizer.AddMany([(txt_pedal_pressed, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL), (checkbox_pedal_pressed, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALIGN_CENTER_VERTICAL)]) @@ -872,7 +873,7 @@ class NeuronavigationPanel(wx.Panel): class ObjectRegistrationPanel(wx.Panel): - def __init__(self, parent, tracker): + def __init__(self, parent, tracker, pedal_connection): wx.Panel.__init__(self, parent) try: default_colour = wx.SystemSettings.GetColour(wx.SYS_COLOUR_MENUBAR) @@ -883,6 +884,7 @@ class ObjectRegistrationPanel(wx.Panel): self.coil_list = const.COIL self.tracker = tracker + self.pedal_connection = pedal_connection self.nav_prop = None self.obj_fiducials = None @@ -1045,7 +1047,7 @@ class ObjectRegistrationPanel(wx.Panel): def OnLinkCreate(self, event=None): if self.tracker.IsTrackerInitialized(): - dialog = dlg.ObjectCalibrationDialog(self.tracker) + dialog = dlg.ObjectCalibrationDialog(self.tracker, self.pedal_connection) try: if dialog.ShowModal() == wx.ID_OK: self.obj_fiducials, self.obj_orients, self.obj_ref_mode, self.obj_name, polydata, use_default_object = dialog.GetValue() -- libgit2 0.21.2