From a042b58e427ac48e1e6b4d1ec1bb5d9b3c71172a Mon Sep 17 00:00:00 2001 From: Renan Date: Fri, 26 Apr 2019 14:53:21 -0300 Subject: [PATCH] Dialog "go to" scanner coordinate system (#180) --- invesalius/constants.py | 1 + invesalius/control.py | 17 +++++++++++------ invesalius/data/surface.py | 13 ++++++++++++- invesalius/gui/dialogs.py | 91 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ invesalius/gui/frame.py | 21 +++++++++++++++++++++ invesalius/gui/task_navigator.py | 6 +----- 6 files changed, 137 insertions(+), 12 deletions(-) diff --git a/invesalius/constants.py b/invesalius/constants.py index cee3897..575130b 100644 --- a/invesalius/constants.py +++ b/invesalius/constants.py @@ -559,6 +559,7 @@ ID_CREATE_SURFACE = wx.NewId() ID_CREATE_MASK = wx.NewId() ID_GOTO_SLICE = wx.NewId() +ID_GOTO_COORD = wx.NewId() #--------------------------------------------------------- STATE_DEFAULT = 1000 diff --git a/invesalius/control.py b/invesalius/control.py index 6c4bd72..ef641ca 100644 --- a/invesalius/control.py +++ b/invesalius/control.py @@ -58,7 +58,8 @@ class Controller(): #None, others and opened Project = 0 #DICOM = 1 #TIFF uCT = 2 - self.img_type = 0 + self.img_type = 0 + self.affine = None #Init session session = ses.Session() @@ -108,6 +109,8 @@ class Controller(): Publisher.subscribe(self.OnSaveProject, 'Save project') + Publisher.subscribe(self.Send_affine, 'Get affine matrix') + def SetBitmapSpacing(self, spacing): proj = prj.Project() proj.spacing = spacing @@ -368,6 +371,7 @@ class Controller(): session = ses.Session() session.CloseProject() + Publisher.sendMessage('Update status text in GUI', label=_("Ready")) ########################### def StartImportBitmapPanel(self, path): @@ -862,11 +866,7 @@ class Controller(): hdr = group.header if group.affine.any(): - from numpy import hstack - from numpy.linalg import inv - affine = inv(group.affine) - affine[1, 3] = -affine[1, 3] - self.affine = hstack(affine) + self.affine = group.affine Publisher.sendMessage('Update affine matrix', affine=self.affine, status=True) hdr.set_data_dtype('int16') @@ -889,6 +889,11 @@ class Controller(): threshold_range=scalar_range) return self.matrix, self.filename + def Send_affine(self): + if self.affine is not None: + Publisher.sendMessage('Update affine matrix', + affine=self.affine, status=True) + def LoadImagedataInfo(self): proj = prj.Project() diff --git a/invesalius/data/surface.py b/invesalius/data/surface.py index 613bafc..4a7179b 100644 --- a/invesalius/data/surface.py +++ b/invesalius/data/surface.py @@ -337,7 +337,18 @@ class SurfaceManager(): self.CreateSurfaceFromPolydata(polydata, name=name, scalar=scalar) def UpdateAffineMatrix(self, affine, status): - self.affine = affine + try: + if status: + from numpy import hstack + from numpy.linalg import inv + affine = inv(affine) + affine[1, 3] = -affine[1, 3] + self.affine = hstack(affine) + else: + self.affine = None + except: + Publisher.sendMessage('Update affine matrix', + affine=None, status=False) def UpdateconverttoInVflag(self, converttoInV): self.converttoInV = converttoInV diff --git a/invesalius/gui/dialogs.py b/invesalius/gui/dialogs.py index 1c912c5..48d7c24 100644 --- a/invesalius/gui/dialogs.py +++ b/invesalius/gui/dialogs.py @@ -3685,3 +3685,94 @@ class GoToDialog(wx.Dialog): def Close(self): wx.Dialog.Close(self) self.Destroy() + + +class GoToDialogScannerCoord(wx.Dialog): + def __init__(self, title=_("Go to scanner coord...")): + wx.Dialog.__init__(self, wx.GetApp().GetTopWindow(), -1, title, style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT|wx.STAY_ON_TOP) + self._init_gui() + + def _init_gui(self): + self.goto_sagital = wx.TextCtrl(self, size=(50,-1)) + self.goto_coronal = wx.TextCtrl(self, size=(50,-1)) + self.goto_axial = wx.TextCtrl(self, size=(50,-1)) + + btn_ok = wx.Button(self, wx.ID_OK) + btn_ok.SetHelpText("") + btn_ok.SetDefault() + + btn_cancel = wx.Button(self, wx.ID_CANCEL) + btn_cancel.SetHelpText("") + + btnsizer = wx.StdDialogButtonSizer() + btnsizer.AddButton(btn_ok) + btnsizer.AddButton(btn_cancel) + btnsizer.Realize() + + sizer_create = wx.FlexGridSizer(3, 2, 10, 10) + sizer_create.AddMany([(wx.StaticText(self, 1, _("Sagital coordinate:")), 1, wx.LEFT, 10), (self.goto_sagital, 1, wx.RIGHT, 10), + (wx.StaticText(self, 1, _("Coronal coordinate:")), 1, wx.LEFT, 10), (self.goto_coronal, 1, wx.RIGHT, 10), + (wx.StaticText(self, 1, _("Axial coordinate:")), 1, wx.LEFT, 10), (self.goto_axial, 1, wx.RIGHT, 10)]) + + main_sizer = wx.BoxSizer(wx.VERTICAL) + + main_sizer.Add((5, 5)) + main_sizer.Add(sizer_create, proportion=3, flag=wx.CENTER, border=20) + main_sizer.Add(btnsizer, proportion=1, flag=wx.CENTER|wx.TOP, border=5) + main_sizer.Add((5, 5)) + + self.SetSizer(main_sizer) + main_sizer.Fit(self) + + self.orientation = None + self.affine = None + + self.__bind_events() + + btn_ok.Bind(wx.EVT_BUTTON, self.OnOk) + Publisher.sendMessage('Get affine matrix') + + def __bind_events(self): + Publisher.subscribe(self.SetNewFocalPoint, 'Cross focal point') + Publisher.subscribe(self.UpdateAffineMatrix, 'Update affine matrix') + + def UpdateAffineMatrix(self, affine, status): + self.affine = affine + + def SetNewFocalPoint(self, coord, spacing): + Publisher.sendMessage('Update cross pos', coord=self.result*spacing) + + def OnOk(self, evt): + from numpy.linalg import inv + import invesalius.data.slice_ as slc + try: + #get affine from image import + if self.affine is not None: + affine = self.affine + #get affine from project + else: + from invesalius.project import Project + affine = Project().affine + + point = [float(self.goto_sagital.GetValue()), + float(self.goto_coronal.GetValue()), + float(self.goto_axial.GetValue())] + + # transformation from scanner coordinates to inv coord system + affine = inv(affine) + self.result = np.dot(affine[:3, :3], np.transpose(point[0:3])) + affine[:3, 3] + self.result[1] = slc.Slice().GetMaxSliceNumber(const.CORONAL_STR) - self.result[1] + + Publisher.sendMessage('Update status text in GUI', label=_("Calculating the transformation ...")) + + Publisher.sendMessage('Set Update cross pos') + Publisher.sendMessage("Toggle Cross", id=const.SLICE_STATE_CROSS) + + Publisher.sendMessage('Update status text in GUI', label=_("Ready")) + except ValueError: + pass + self.Close() + + def Close(self): + wx.Dialog.Close(self) + self.Destroy() diff --git a/invesalius/gui/frame.py b/invesalius/gui/frame.py index c0761ae..5159267 100644 --- a/invesalius/gui/frame.py +++ b/invesalius/gui/frame.py @@ -463,6 +463,8 @@ class Frame(wx.Frame): self.OnRedo() elif id == const.ID_GOTO_SLICE: self.OnGotoSlice() + elif id == const.ID_GOTO_COORD: + self.GoToDialogScannerCoord() elif id == const.ID_BOOLEAN_MASK: self.OnMaskBoolean() @@ -700,6 +702,12 @@ class Frame(wx.Frame): gt_dialog.ShowModal() self.Refresh() + def GoToDialogScannerCoord(self): + gts_dialog = dlg.GoToDialogScannerCoord() + gts_dialog.CenterOnParent() + gts_dialog.ShowModal() + self.Refresh() + def OnMaskBoolean(self): Publisher.sendMessage('Show boolean dialog') @@ -792,6 +800,7 @@ class MenuBar(wx.MenuBar): sub(self.OnEnableState, "Enable state project") sub(self.OnEnableUndo, "Enable undo") sub(self.OnEnableRedo, "Enable redo") + sub(self.OnEnableGotoCoord, "Update affine matrix") sub(self.OnEnableNavigation, "Navigation status") sub(self.OnAddMask, "Add mask") @@ -859,6 +868,8 @@ class MenuBar(wx.MenuBar): file_edit.Append(wx.ID_UNDO, _("Undo\tCtrl+Z")).Enable(False) file_edit.Append(wx.ID_REDO, _("Redo\tCtrl+Y")).Enable(False) file_edit.Append(const.ID_GOTO_SLICE, _("Go to slice ...\tCtrl+G")) + file_edit.Append(const.ID_GOTO_COORD, _("Go to scanner coord ...\t")).Enable(False) + #app(const.ID_EDIT_LIST, "Show Undo List...") ################################################################# @@ -1077,6 +1088,16 @@ class MenuBar(wx.MenuBar): else: self.FindItemById(wx.ID_REDO).Enable(False) + def OnEnableGotoCoord(self, affine, status): + """ + Disable goto coord either if there is no affine matrix or affine is wrongly imported. + :param status: Affine matrix status + """ + if status: + self.FindItemById(const.ID_GOTO_COORD).Enable(True) + else: + self.FindItemById(const.ID_GOTO_COORD).Enable(False) + def OnEnableNavigation(self, status): """ Disable mode menu when navigation is on. diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 9165f81..2ae3210 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -478,8 +478,6 @@ class NeuronavigationPanel(wx.Panel): ctrl.SetSelection(0) print("Tracker not connected!") else: - Publisher.sendMessage('Update status text in GUI', - label=_("Ready")) ctrl.SetSelection(self.tracker_id) print("Tracker connected!") elif choice == 6: @@ -513,10 +511,8 @@ class NeuronavigationPanel(wx.Panel): dlg.NavigationTrackerWarning(self.tracker_id, self.trk_init[1]) self.tracker_id = 0 ctrl.SetSelection(self.tracker_id) - else: - Publisher.sendMessage('Update status text in GUI', - label=_("Ready")) + Publisher.sendMessage('Update status text in GUI', label=_("Ready")) Publisher.sendMessage('Update tracker initializer', nav_prop=(self.tracker_id, self.trk_init, self.ref_mode_id)) -- libgit2 0.21.2