From 39df648d6b8885782db3351b83866364d8acbad6 Mon Sep 17 00:00:00 2001 From: v-malheiro Date: Mon, 23 Sep 2024 16:31:15 -0300 Subject: [PATCH 1/5] ADD: message to enable free drive button --- invesalius/navigation/robot.py | 1 + 1 file changed, 1 insertion(+) diff --git a/invesalius/navigation/robot.py b/invesalius/navigation/robot.py index cc3551b04..96c60686d 100644 --- a/invesalius/navigation/robot.py +++ b/invesalius/navigation/robot.py @@ -108,6 +108,7 @@ def OnRobotConnectionStatus(self, data): self.is_robot_connected = data if self.is_robot_connected: Publisher.sendMessage("Enable move away button", enabled=True) + Publisher.sendMessage("Enable free drive button", enabled=True) def RegisterRobot(self): Publisher.sendMessage("End busy cursor") From f7a5442a8c97d06889e5cfe449737e77da96a6a6 Mon Sep 17 00:00:00 2001 From: v-malheiro Date: Mon, 23 Sep 2024 16:32:27 -0300 Subject: [PATCH 2/5] ADD: free drive button settings --- invesalius/gui/task_navigator.py | 40 +++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index fcb103725..5b32e01e6 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -1320,6 +1320,25 @@ def __init__(self, parent, nav_hub): ) self.robot_move_away_button = robot_move_away_button + # Toggle button for enable/disable free drive robot mode + tooltip = _("Free drive robot") + BMP_FREE_DRIVE = wx.Bitmap( + str(inv_paths.ICON_DIR.joinpath("robot_free_drive.png")), wx.BITMAP_TYPE_PNG + ) + robot_free_drive_button = wx.ToggleButton( + self, -1, "", style=pbtn.PB_STYLE_SQUARE, size=ICON_SIZE + ) + robot_free_drive_button.SetBackgroundColour(GREY_COLOR) + robot_free_drive_button.SetBitmap(BMP_FREE_DRIVE) + robot_free_drive_button.SetToolTip(tooltip) + robot_free_drive_button.SetValue(False) + robot_free_drive_button.Enable(False) + robot_free_drive_button.Bind( + wx.EVT_TOGGLEBUTTON, + partial(self.OnRobotFreeDriveButton, ctrl=robot_free_drive_button), + ) + self.robot_free_drive_button = robot_free_drive_button + # Sizers start_navigation_button_sizer = wx.BoxSizer(wx.VERTICAL) start_navigation_button_sizer.AddMany( @@ -1341,11 +1360,12 @@ def __init__(self, parent, nav_hub): ] ) - robot_buttons_sizer = wx.FlexGridSizer(2, 5, 5) + robot_buttons_sizer = wx.FlexGridSizer(4, 5, 5) robot_buttons_sizer.AddMany( [ (robot_track_target_button), (robot_move_away_button), + (robot_free_drive_button), ] ) @@ -1391,6 +1411,8 @@ def __bind_events(self): Publisher.subscribe(self.PressRobotMoveAwayButton, "Press move away button") Publisher.subscribe(self.EnableRobotMoveAwayButton, "Enable move away button") + Publisher.subscribe(self.EnableRobotFreeDriveButton, "Enable free drive button") + Publisher.subscribe(self.ShowTargetButton, "Show target button") Publisher.subscribe(self.HideTargetButton, "Hide target button") Publisher.subscribe(self.PressTargetModeButton, "Press target mode button") @@ -1572,6 +1594,10 @@ def UpdateRobotButtons(self): move_away_button_enabled = self.robot.IsConnected() self.EnableRobotMoveAwayButton(enabled=move_away_button_enabled) + # Enable 'free drive' robot button if robot is connected. + free_drive_button_enabled = self.robot.IsConnected() + self.EnableRobotFreeDriveButton(enabled=free_drive_button_enabled) + def SetTargetMode(self, enabled=False): self.target_mode = enabled @@ -1774,6 +1800,18 @@ def OnRobotMoveAwayButton(self, evt=None, ctrl=None): if self.robot.objective == RobotObjective.MOVE_AWAY_FROM_HEAD: self.robot.SetObjective(RobotObjective.NONE) + # 'Free drive' button + def EnableRobotFreeDriveButton(self, enabled=False): + self.EnableToggleButton(self.robot_free_drive_button, enabled) + self.UpdateToggleButton(self.robot_free_drive_button) + + def OnRobotFreeDriveButton(self, evt=None, ctrl=None): + self.UpdateToggleButton(self.robot_free_drive_button) + pressed = self.robot_free_drive_button.GetValue() + if pressed: + Publisher.sendMessage("Neuronavigation to Robot: Set free drive", set=True) + else: + Publisher.sendMessage("Neuronavigation to Robot: Set free drive", set=False) class MarkersPanel(wx.Panel, ColumnSorterMixin): def __init__(self, parent, nav_hub): From d2f07339604638a5ffa7530e788adff25af07cb1 Mon Sep 17 00:00:00 2001 From: v-malheiro Date: Mon, 23 Sep 2024 21:25:19 -0300 Subject: [PATCH 3/5] ADD: free drive icon button --- icons/robot_free_drive.png | Bin 0 -> 6611 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 icons/robot_free_drive.png diff --git a/icons/robot_free_drive.png b/icons/robot_free_drive.png new file mode 100644 index 0000000000000000000000000000000000000000..4a4517743bcd0c9c83a9e09e5a62b668326e2751 GIT binary patch literal 6611 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 Date: Mon, 23 Sep 2024 23:37:30 -0300 Subject: [PATCH 4/5] Command line for automatic export of skull and implant by command line. (#850) --- app.py | 35 ++++++++++++++++++++++-- invesalius/data/slice_.py | 1 + invesalius/data/surface.py | 56 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 2 deletions(-) diff --git a/app.py b/app.py index 0b22ba047..33db1413a 100644 --- a/app.py +++ b/app.py @@ -361,7 +361,7 @@ def use_cmd_optargs(args): Publisher.sendMessage("Save project", filepath=os.path.abspath(args.save)) exit(0) - check_for_segmentation(args) + check_for_cranioplasty(args) check_for_export(args) return True @@ -411,10 +411,41 @@ def use_cmd_optargs(args): return False -def check_for_segmentation(args): +def check_for_cranioplasty(args): + import invesalius.constants as const + from invesalius.i18n import tr as _ + + surface_options = { + "method": { + "algorithm": "Default", + "options": {}, + }, + "options": { + "index": 0, + "name": "", + "quality": _("Optimal *"), + "fill": False, + "keep_largest": False, + "overwrite": False, + }, + } + if args.cranioplasty: + from invesalius.data import slice_ + from invesalius.project import Project + + # create cranium mask + Publisher.sendMessage("Update threshold limits", threshold_range=(226, 3071)) + Publisher.sendMessage("Appy threshold all slices") + + # create implant mask Publisher.sendMessage("Create implant for cranioplasty") + # convert masks to surfaces and exports them. + Publisher.sendMessage( + "Export all surfaces separately", folder="./", filetype=const.FILETYPE_STL + ) + def sanitize(text): text = str(text).strip().replace(" ", "_") diff --git a/invesalius/data/slice_.py b/invesalius/data/slice_.py index bcf9fa4ea..eeea274ef 100644 --- a/invesalius/data/slice_.py +++ b/invesalius/data/slice_.py @@ -254,6 +254,7 @@ def __bind_events(self) -> None: Publisher.subscribe(self._fill_holes_auto, "Fill holes automatically") Publisher.subscribe(self._set_interpolation_method, "Set interpolation method") + Publisher.subscribe(self.do_threshold_to_all_slices, "Appy threshold all slices") def GetMaxSliceNumber(self, orientation: str) -> int: shape: Tuple[int, int, int] = self.matrix.shape diff --git a/invesalius/data/surface.py b/invesalius/data/surface.py index a3670ff52..d6a0ec7f7 100644 --- a/invesalius/data/surface.py +++ b/invesalius/data/surface.py @@ -228,6 +228,7 @@ def __bind_events(self): Publisher.subscribe(self.UpdateConvertToInvFlag, "Update convert_to_inv flag") Publisher.subscribe(self.CreateSurfaceFromPolydata, "Create surface from polydata") + Publisher.subscribe(self.export_all_surfaces_separately, "Export all surfaces separately") def OnDuplicate(self, surface_indexes): proj = prj.Project() @@ -1206,6 +1207,61 @@ def OnExportSurface(self, filename, filetype, convert_to_world=False): dlg.Destroy() os.remove(temp_file) + def export_all_surfaces_separately(self, folder, filetype): + import invesalius.data.slice_ as slc + + if filetype in ( + const.FILETYPE_STL, + const.FILETYPE_VTP, + const.FILETYPE_PLY, + const.FILETYPE_STL_ASCII, + ): + proj = prj.Project() + + for index in list(proj.mask_dict.keys()): + if index == 1: + algorithm = "Context aware smoothing" + else: + algorithm = "Default" + surface_parameters = { + "method": { + "algorithm": algorithm, + "options": {}, + }, + "options": { + "index": index, + "name": "", + "quality": _("Optimal *"), + "fill": False, + "keep_largest": True, + "overwrite": False, + }, + } + + mask = proj.mask_dict[index] + print(mask.matrix.min(), mask.matrix.max(), mask.name) + slice_ = slc.Slice() + + Publisher.sendMessage( + "Create surface", + slice_=slice_, + mask=mask, + surface_parameters=surface_parameters, + ) + + # hide all surfaces + for index in proj.surface_dict: + proj.surface_dict[index].is_shown = False + + # Displays one surface at a time and export + for index in proj.surface_dict.keys(): + print(proj.surface_dict[index].name) + proj.surface_dict[index].is_shown = True + self._export_surface( + os.path.join(folder, str(index) + ".stl"), const.FILETYPE_STL, False + ) + proj.surface_dict[index].is_shown = False + def _export_surface(self, filename, filetype, convert_to_world): if filetype in ( const.FILETYPE_STL, From ecf9bbebd5b0032138887651de9def72cd8e271b Mon Sep 17 00:00:00 2001 From: v-malheiro Date: Tue, 24 Sep 2024 11:23:31 -0300 Subject: [PATCH 5/5] MOD: ajust pre-commit on changed files --- invesalius/gui/task_navigator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/invesalius/gui/task_navigator.py b/invesalius/gui/task_navigator.py index 5b32e01e6..de8dacd65 100644 --- a/invesalius/gui/task_navigator.py +++ b/invesalius/gui/task_navigator.py @@ -1813,6 +1813,7 @@ def OnRobotFreeDriveButton(self, evt=None, ctrl=None): else: Publisher.sendMessage("Neuronavigation to Robot: Set free drive", set=False) + class MarkersPanel(wx.Panel, ColumnSorterMixin): def __init__(self, parent, nav_hub): wx.Panel.__init__(self, parent)