Commit a042b58e427ac48e1e6b4d1ec1bb5d9b3c71172a
Committed by
Thiago Franco de Moraes
1 parent
eac24f4f
Exists in
master
Dialog "go to" scanner coordinate system (#180)
* Created dialog goto sacnner coord * Get affine matrix from project or imported image -deals with wrongly affine shape and old inv projects * Update status GUI * Remove redundant sendmessage and show cross * cleaning...
Showing
6 changed files
with
137 additions
and
12 deletions
Show diff stats
invesalius/constants.py
@@ -559,6 +559,7 @@ ID_CREATE_SURFACE = wx.NewId() | @@ -559,6 +559,7 @@ ID_CREATE_SURFACE = wx.NewId() | ||
559 | ID_CREATE_MASK = wx.NewId() | 559 | ID_CREATE_MASK = wx.NewId() |
560 | 560 | ||
561 | ID_GOTO_SLICE = wx.NewId() | 561 | ID_GOTO_SLICE = wx.NewId() |
562 | +ID_GOTO_COORD = wx.NewId() | ||
562 | 563 | ||
563 | #--------------------------------------------------------- | 564 | #--------------------------------------------------------- |
564 | STATE_DEFAULT = 1000 | 565 | STATE_DEFAULT = 1000 |
invesalius/control.py
@@ -58,7 +58,8 @@ class Controller(): | @@ -58,7 +58,8 @@ class Controller(): | ||
58 | #None, others and opened Project = 0 | 58 | #None, others and opened Project = 0 |
59 | #DICOM = 1 | 59 | #DICOM = 1 |
60 | #TIFF uCT = 2 | 60 | #TIFF uCT = 2 |
61 | - self.img_type = 0 | 61 | + self.img_type = 0 |
62 | + self.affine = None | ||
62 | 63 | ||
63 | #Init session | 64 | #Init session |
64 | session = ses.Session() | 65 | session = ses.Session() |
@@ -108,6 +109,8 @@ class Controller(): | @@ -108,6 +109,8 @@ class Controller(): | ||
108 | 109 | ||
109 | Publisher.subscribe(self.OnSaveProject, 'Save project') | 110 | Publisher.subscribe(self.OnSaveProject, 'Save project') |
110 | 111 | ||
112 | + Publisher.subscribe(self.Send_affine, 'Get affine matrix') | ||
113 | + | ||
111 | def SetBitmapSpacing(self, spacing): | 114 | def SetBitmapSpacing(self, spacing): |
112 | proj = prj.Project() | 115 | proj = prj.Project() |
113 | proj.spacing = spacing | 116 | proj.spacing = spacing |
@@ -368,6 +371,7 @@ class Controller(): | @@ -368,6 +371,7 @@ class Controller(): | ||
368 | session = ses.Session() | 371 | session = ses.Session() |
369 | session.CloseProject() | 372 | session.CloseProject() |
370 | 373 | ||
374 | + Publisher.sendMessage('Update status text in GUI', label=_("Ready")) | ||
371 | ########################### | 375 | ########################### |
372 | 376 | ||
373 | def StartImportBitmapPanel(self, path): | 377 | def StartImportBitmapPanel(self, path): |
@@ -862,11 +866,7 @@ class Controller(): | @@ -862,11 +866,7 @@ class Controller(): | ||
862 | 866 | ||
863 | hdr = group.header | 867 | hdr = group.header |
864 | if group.affine.any(): | 868 | if group.affine.any(): |
865 | - from numpy import hstack | ||
866 | - from numpy.linalg import inv | ||
867 | - affine = inv(group.affine) | ||
868 | - affine[1, 3] = -affine[1, 3] | ||
869 | - self.affine = hstack(affine) | 869 | + self.affine = group.affine |
870 | Publisher.sendMessage('Update affine matrix', | 870 | Publisher.sendMessage('Update affine matrix', |
871 | affine=self.affine, status=True) | 871 | affine=self.affine, status=True) |
872 | hdr.set_data_dtype('int16') | 872 | hdr.set_data_dtype('int16') |
@@ -889,6 +889,11 @@ class Controller(): | @@ -889,6 +889,11 @@ class Controller(): | ||
889 | threshold_range=scalar_range) | 889 | threshold_range=scalar_range) |
890 | return self.matrix, self.filename | 890 | return self.matrix, self.filename |
891 | 891 | ||
892 | + def Send_affine(self): | ||
893 | + if self.affine is not None: | ||
894 | + Publisher.sendMessage('Update affine matrix', | ||
895 | + affine=self.affine, status=True) | ||
896 | + | ||
892 | def LoadImagedataInfo(self): | 897 | def LoadImagedataInfo(self): |
893 | proj = prj.Project() | 898 | proj = prj.Project() |
894 | 899 |
invesalius/data/surface.py
@@ -337,7 +337,18 @@ class SurfaceManager(): | @@ -337,7 +337,18 @@ class SurfaceManager(): | ||
337 | self.CreateSurfaceFromPolydata(polydata, name=name, scalar=scalar) | 337 | self.CreateSurfaceFromPolydata(polydata, name=name, scalar=scalar) |
338 | 338 | ||
339 | def UpdateAffineMatrix(self, affine, status): | 339 | def UpdateAffineMatrix(self, affine, status): |
340 | - self.affine = affine | 340 | + try: |
341 | + if status: | ||
342 | + from numpy import hstack | ||
343 | + from numpy.linalg import inv | ||
344 | + affine = inv(affine) | ||
345 | + affine[1, 3] = -affine[1, 3] | ||
346 | + self.affine = hstack(affine) | ||
347 | + else: | ||
348 | + self.affine = None | ||
349 | + except: | ||
350 | + Publisher.sendMessage('Update affine matrix', | ||
351 | + affine=None, status=False) | ||
341 | 352 | ||
342 | def UpdateconverttoInVflag(self, converttoInV): | 353 | def UpdateconverttoInVflag(self, converttoInV): |
343 | self.converttoInV = converttoInV | 354 | self.converttoInV = converttoInV |
invesalius/gui/dialogs.py
@@ -3685,3 +3685,94 @@ class GoToDialog(wx.Dialog): | @@ -3685,3 +3685,94 @@ class GoToDialog(wx.Dialog): | ||
3685 | def Close(self): | 3685 | def Close(self): |
3686 | wx.Dialog.Close(self) | 3686 | wx.Dialog.Close(self) |
3687 | self.Destroy() | 3687 | self.Destroy() |
3688 | + | ||
3689 | + | ||
3690 | +class GoToDialogScannerCoord(wx.Dialog): | ||
3691 | + def __init__(self, title=_("Go to scanner coord...")): | ||
3692 | + wx.Dialog.__init__(self, wx.GetApp().GetTopWindow(), -1, title, style=wx.DEFAULT_DIALOG_STYLE|wx.FRAME_FLOAT_ON_PARENT|wx.STAY_ON_TOP) | ||
3693 | + self._init_gui() | ||
3694 | + | ||
3695 | + def _init_gui(self): | ||
3696 | + self.goto_sagital = wx.TextCtrl(self, size=(50,-1)) | ||
3697 | + self.goto_coronal = wx.TextCtrl(self, size=(50,-1)) | ||
3698 | + self.goto_axial = wx.TextCtrl(self, size=(50,-1)) | ||
3699 | + | ||
3700 | + btn_ok = wx.Button(self, wx.ID_OK) | ||
3701 | + btn_ok.SetHelpText("") | ||
3702 | + btn_ok.SetDefault() | ||
3703 | + | ||
3704 | + btn_cancel = wx.Button(self, wx.ID_CANCEL) | ||
3705 | + btn_cancel.SetHelpText("") | ||
3706 | + | ||
3707 | + btnsizer = wx.StdDialogButtonSizer() | ||
3708 | + btnsizer.AddButton(btn_ok) | ||
3709 | + btnsizer.AddButton(btn_cancel) | ||
3710 | + btnsizer.Realize() | ||
3711 | + | ||
3712 | + sizer_create = wx.FlexGridSizer(3, 2, 10, 10) | ||
3713 | + sizer_create.AddMany([(wx.StaticText(self, 1, _("Sagital coordinate:")), 1, wx.LEFT, 10), (self.goto_sagital, 1, wx.RIGHT, 10), | ||
3714 | + (wx.StaticText(self, 1, _("Coronal coordinate:")), 1, wx.LEFT, 10), (self.goto_coronal, 1, wx.RIGHT, 10), | ||
3715 | + (wx.StaticText(self, 1, _("Axial coordinate:")), 1, wx.LEFT, 10), (self.goto_axial, 1, wx.RIGHT, 10)]) | ||
3716 | + | ||
3717 | + main_sizer = wx.BoxSizer(wx.VERTICAL) | ||
3718 | + | ||
3719 | + main_sizer.Add((5, 5)) | ||
3720 | + main_sizer.Add(sizer_create, proportion=3, flag=wx.CENTER, border=20) | ||
3721 | + main_sizer.Add(btnsizer, proportion=1, flag=wx.CENTER|wx.TOP, border=5) | ||
3722 | + main_sizer.Add((5, 5)) | ||
3723 | + | ||
3724 | + self.SetSizer(main_sizer) | ||
3725 | + main_sizer.Fit(self) | ||
3726 | + | ||
3727 | + self.orientation = None | ||
3728 | + self.affine = None | ||
3729 | + | ||
3730 | + self.__bind_events() | ||
3731 | + | ||
3732 | + btn_ok.Bind(wx.EVT_BUTTON, self.OnOk) | ||
3733 | + Publisher.sendMessage('Get affine matrix') | ||
3734 | + | ||
3735 | + def __bind_events(self): | ||
3736 | + Publisher.subscribe(self.SetNewFocalPoint, 'Cross focal point') | ||
3737 | + Publisher.subscribe(self.UpdateAffineMatrix, 'Update affine matrix') | ||
3738 | + | ||
3739 | + def UpdateAffineMatrix(self, affine, status): | ||
3740 | + self.affine = affine | ||
3741 | + | ||
3742 | + def SetNewFocalPoint(self, coord, spacing): | ||
3743 | + Publisher.sendMessage('Update cross pos', coord=self.result*spacing) | ||
3744 | + | ||
3745 | + def OnOk(self, evt): | ||
3746 | + from numpy.linalg import inv | ||
3747 | + import invesalius.data.slice_ as slc | ||
3748 | + try: | ||
3749 | + #get affine from image import | ||
3750 | + if self.affine is not None: | ||
3751 | + affine = self.affine | ||
3752 | + #get affine from project | ||
3753 | + else: | ||
3754 | + from invesalius.project import Project | ||
3755 | + affine = Project().affine | ||
3756 | + | ||
3757 | + point = [float(self.goto_sagital.GetValue()), | ||
3758 | + float(self.goto_coronal.GetValue()), | ||
3759 | + float(self.goto_axial.GetValue())] | ||
3760 | + | ||
3761 | + # transformation from scanner coordinates to inv coord system | ||
3762 | + affine = inv(affine) | ||
3763 | + self.result = np.dot(affine[:3, :3], np.transpose(point[0:3])) + affine[:3, 3] | ||
3764 | + self.result[1] = slc.Slice().GetMaxSliceNumber(const.CORONAL_STR) - self.result[1] | ||
3765 | + | ||
3766 | + Publisher.sendMessage('Update status text in GUI', label=_("Calculating the transformation ...")) | ||
3767 | + | ||
3768 | + Publisher.sendMessage('Set Update cross pos') | ||
3769 | + Publisher.sendMessage("Toggle Cross", id=const.SLICE_STATE_CROSS) | ||
3770 | + | ||
3771 | + Publisher.sendMessage('Update status text in GUI', label=_("Ready")) | ||
3772 | + except ValueError: | ||
3773 | + pass | ||
3774 | + self.Close() | ||
3775 | + | ||
3776 | + def Close(self): | ||
3777 | + wx.Dialog.Close(self) | ||
3778 | + self.Destroy() |
invesalius/gui/frame.py
@@ -463,6 +463,8 @@ class Frame(wx.Frame): | @@ -463,6 +463,8 @@ class Frame(wx.Frame): | ||
463 | self.OnRedo() | 463 | self.OnRedo() |
464 | elif id == const.ID_GOTO_SLICE: | 464 | elif id == const.ID_GOTO_SLICE: |
465 | self.OnGotoSlice() | 465 | self.OnGotoSlice() |
466 | + elif id == const.ID_GOTO_COORD: | ||
467 | + self.GoToDialogScannerCoord() | ||
466 | 468 | ||
467 | elif id == const.ID_BOOLEAN_MASK: | 469 | elif id == const.ID_BOOLEAN_MASK: |
468 | self.OnMaskBoolean() | 470 | self.OnMaskBoolean() |
@@ -700,6 +702,12 @@ class Frame(wx.Frame): | @@ -700,6 +702,12 @@ class Frame(wx.Frame): | ||
700 | gt_dialog.ShowModal() | 702 | gt_dialog.ShowModal() |
701 | self.Refresh() | 703 | self.Refresh() |
702 | 704 | ||
705 | + def GoToDialogScannerCoord(self): | ||
706 | + gts_dialog = dlg.GoToDialogScannerCoord() | ||
707 | + gts_dialog.CenterOnParent() | ||
708 | + gts_dialog.ShowModal() | ||
709 | + self.Refresh() | ||
710 | + | ||
703 | def OnMaskBoolean(self): | 711 | def OnMaskBoolean(self): |
704 | Publisher.sendMessage('Show boolean dialog') | 712 | Publisher.sendMessage('Show boolean dialog') |
705 | 713 | ||
@@ -792,6 +800,7 @@ class MenuBar(wx.MenuBar): | @@ -792,6 +800,7 @@ class MenuBar(wx.MenuBar): | ||
792 | sub(self.OnEnableState, "Enable state project") | 800 | sub(self.OnEnableState, "Enable state project") |
793 | sub(self.OnEnableUndo, "Enable undo") | 801 | sub(self.OnEnableUndo, "Enable undo") |
794 | sub(self.OnEnableRedo, "Enable redo") | 802 | sub(self.OnEnableRedo, "Enable redo") |
803 | + sub(self.OnEnableGotoCoord, "Update affine matrix") | ||
795 | sub(self.OnEnableNavigation, "Navigation status") | 804 | sub(self.OnEnableNavigation, "Navigation status") |
796 | 805 | ||
797 | sub(self.OnAddMask, "Add mask") | 806 | sub(self.OnAddMask, "Add mask") |
@@ -859,6 +868,8 @@ class MenuBar(wx.MenuBar): | @@ -859,6 +868,8 @@ class MenuBar(wx.MenuBar): | ||
859 | file_edit.Append(wx.ID_UNDO, _("Undo\tCtrl+Z")).Enable(False) | 868 | file_edit.Append(wx.ID_UNDO, _("Undo\tCtrl+Z")).Enable(False) |
860 | file_edit.Append(wx.ID_REDO, _("Redo\tCtrl+Y")).Enable(False) | 869 | file_edit.Append(wx.ID_REDO, _("Redo\tCtrl+Y")).Enable(False) |
861 | file_edit.Append(const.ID_GOTO_SLICE, _("Go to slice ...\tCtrl+G")) | 870 | file_edit.Append(const.ID_GOTO_SLICE, _("Go to slice ...\tCtrl+G")) |
871 | + file_edit.Append(const.ID_GOTO_COORD, _("Go to scanner coord ...\t")).Enable(False) | ||
872 | + | ||
862 | #app(const.ID_EDIT_LIST, "Show Undo List...") | 873 | #app(const.ID_EDIT_LIST, "Show Undo List...") |
863 | ################################################################# | 874 | ################################################################# |
864 | 875 | ||
@@ -1077,6 +1088,16 @@ class MenuBar(wx.MenuBar): | @@ -1077,6 +1088,16 @@ class MenuBar(wx.MenuBar): | ||
1077 | else: | 1088 | else: |
1078 | self.FindItemById(wx.ID_REDO).Enable(False) | 1089 | self.FindItemById(wx.ID_REDO).Enable(False) |
1079 | 1090 | ||
1091 | + def OnEnableGotoCoord(self, affine, status): | ||
1092 | + """ | ||
1093 | + Disable goto coord either if there is no affine matrix or affine is wrongly imported. | ||
1094 | + :param status: Affine matrix status | ||
1095 | + """ | ||
1096 | + if status: | ||
1097 | + self.FindItemById(const.ID_GOTO_COORD).Enable(True) | ||
1098 | + else: | ||
1099 | + self.FindItemById(const.ID_GOTO_COORD).Enable(False) | ||
1100 | + | ||
1080 | def OnEnableNavigation(self, status): | 1101 | def OnEnableNavigation(self, status): |
1081 | """ | 1102 | """ |
1082 | Disable mode menu when navigation is on. | 1103 | Disable mode menu when navigation is on. |
invesalius/gui/task_navigator.py
@@ -478,8 +478,6 @@ class NeuronavigationPanel(wx.Panel): | @@ -478,8 +478,6 @@ class NeuronavigationPanel(wx.Panel): | ||
478 | ctrl.SetSelection(0) | 478 | ctrl.SetSelection(0) |
479 | print("Tracker not connected!") | 479 | print("Tracker not connected!") |
480 | else: | 480 | else: |
481 | - Publisher.sendMessage('Update status text in GUI', | ||
482 | - label=_("Ready")) | ||
483 | ctrl.SetSelection(self.tracker_id) | 481 | ctrl.SetSelection(self.tracker_id) |
484 | print("Tracker connected!") | 482 | print("Tracker connected!") |
485 | elif choice == 6: | 483 | elif choice == 6: |
@@ -513,10 +511,8 @@ class NeuronavigationPanel(wx.Panel): | @@ -513,10 +511,8 @@ class NeuronavigationPanel(wx.Panel): | ||
513 | dlg.NavigationTrackerWarning(self.tracker_id, self.trk_init[1]) | 511 | dlg.NavigationTrackerWarning(self.tracker_id, self.trk_init[1]) |
514 | self.tracker_id = 0 | 512 | self.tracker_id = 0 |
515 | ctrl.SetSelection(self.tracker_id) | 513 | ctrl.SetSelection(self.tracker_id) |
516 | - else: | ||
517 | - Publisher.sendMessage('Update status text in GUI', | ||
518 | - label=_("Ready")) | ||
519 | 514 | ||
515 | + Publisher.sendMessage('Update status text in GUI', label=_("Ready")) | ||
520 | Publisher.sendMessage('Update tracker initializer', | 516 | Publisher.sendMessage('Update tracker initializer', |
521 | nav_prop=(self.tracker_id, self.trk_init, self.ref_mode_id)) | 517 | nav_prop=(self.tracker_id, self.trk_init, self.ref_mode_id)) |
522 | 518 |