Skip to content

Commit

Permalink
chore: refactor and simplify menu bar creation, add workaround for ma…
Browse files Browse the repository at this point in the history
…cOS numpy problem (#1124)

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

- **Refactor**
- Improved code readability and maintainability by moving menu bar setup
logic to a dedicated method in the main window classes of the `ROI
Analysis` and `ROI Mask` modules.

- **Bug Fixes**
- Added conditional logic to manage thread usage based on the numpy
version and macOS arm64 architecture in the napari image view.

- **Chores**
- Simplified dependencies in `pyproject.toml` by combining them into a
single string `"PartSeg[accelerate,pyqt5]"`.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
  • Loading branch information
Czaki authored Jul 5, 2024
1 parent 3d42678 commit af44d19
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 57 deletions.
55 changes: 29 additions & 26 deletions package/PartSeg/_roi_analysis/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -577,35 +577,11 @@ def __init__( # noqa: PLR0915
elif initial_image is not False:
self.settings.image = initial_image

self._setup_menu_bar()

icon = QIcon(os.path.join(PartSegData.icons_dir, "icon.png"))
self.setWindowIcon(icon)

menu_bar = self.menuBar()
file_menu = menu_bar.addMenu("File")
file_menu.addAction("&Open").triggered.connect(self.main_menu.load_data)
file_menu.addMenu(self.recent_file_menu)
file_menu.addAction("&Save").triggered.connect(self.main_menu.save_file)
file_menu.addAction("Batch processing").triggered.connect(self.main_menu.batch_window)
view_menu = menu_bar.addMenu("View")
view_menu.addAction("Settings and Measurement").triggered.connect(self.main_menu.advanced_window_show)
view_menu.addAction("Additional output").triggered.connect(self.additional_layers_show)
view_menu.addAction("Additional output with data").triggered.connect(lambda: self.additional_layers_show(True))
view_menu.addAction("Napari viewer").triggered.connect(self.napari_viewer_show)
view_menu.addAction("Toggle Multiple Files").triggered.connect(self.toggle_multiple_files)
view_menu.addAction("Toggle left panel").triggered.connect(self.toggle_left_panel)
view_menu.addAction("Toggle console").triggered.connect(self._toggle_console)
view_menu.addAction("Toggle scale bar").triggered.connect(self._toggle_scale_bar)
action = view_menu.addAction("Screenshot right panel")
action.triggered.connect(self.screenshot(self.result_image))
action.setShortcut(QKeySequence.Print)
view_menu.addAction("Screenshot left panel").triggered.connect(self.screenshot(self.raw_image))
image_menu = menu_bar.addMenu("Image operations")
image_menu.addAction("Image adjustment").triggered.connect(self.image_adjust_exec)
image_menu.addAction("Mask manager").triggered.connect(self.main_menu.mask_manager)
help_menu = menu_bar.addMenu("Help")
help_menu.addAction("State directory").triggered.connect(self.show_settings_directory)
help_menu.addAction("About").triggered.connect(self.show_about_dialog)

layout = QGridLayout()
layout.setSpacing(0)
info_layout = QHBoxLayout()
Expand All @@ -632,6 +608,33 @@ def __init__( # noqa: PLR0915
geometry = self.settings.get_from_profile("main_window_geometry")
self.restoreGeometry(QByteArray.fromHex(bytes(geometry, "ascii")))

def _setup_menu_bar(self):
menu_bar = self.menuBar()
file_menu = menu_bar.addMenu("File")
file_menu.addAction("&Open").triggered.connect(self.main_menu.load_data)
file_menu.addMenu(self.recent_file_menu)
file_menu.addAction("&Save").triggered.connect(self.main_menu.save_file)
file_menu.addAction("Batch processing").triggered.connect(self.main_menu.batch_window)
view_menu = menu_bar.addMenu("View")
view_menu.addAction("Settings and Measurement").triggered.connect(self.main_menu.advanced_window_show)
view_menu.addAction("Additional output").triggered.connect(self.additional_layers_show)
view_menu.addAction("Additional output with data").triggered.connect(lambda: self.additional_layers_show(True))
view_menu.addAction("Napari viewer").triggered.connect(self.napari_viewer_show)
view_menu.addAction("Toggle Multiple Files").triggered.connect(self.toggle_multiple_files)
view_menu.addAction("Toggle left panel").triggered.connect(self.toggle_left_panel)
view_menu.addAction("Toggle console").triggered.connect(self._toggle_console)
view_menu.addAction("Toggle scale bar").triggered.connect(self._toggle_scale_bar)
action = view_menu.addAction("Screenshot right panel")
action.triggered.connect(self.screenshot(self.result_image))
action.setShortcut(QKeySequence.Print)
view_menu.addAction("Screenshot left panel").triggered.connect(self.screenshot(self.raw_image))
image_menu = menu_bar.addMenu("Image operations")
image_menu.addAction("Image adjustment").triggered.connect(self.image_adjust_exec)
image_menu.addAction("Mask manager").triggered.connect(self.main_menu.mask_manager)
help_menu = menu_bar.addMenu("Help")
help_menu.addAction("State directory").triggered.connect(self.show_settings_directory)
help_menu.addAction("About").triggered.connect(self.show_about_dialog)

def _toggle_scale_bar(self):
self.raw_image.toggle_scale_bar()
self.result_image.toggle_scale_bar()
Expand Down
49 changes: 26 additions & 23 deletions package/PartSeg/_roi_mask/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -891,7 +891,7 @@ def get_setting_class(cls) -> Type[StackSettings]:

initial_image_path = PartSegData.segmentation_mask_default_image

def __init__( # noqa: PLR0915
def __init__(
self, config_folder=CONFIG_FOLDER, title="PartSeg", settings=None, signal_fun=None, initial_image=None
):
super().__init__(config_folder, title, settings, io_functions.load_dict, signal_fun)
Expand All @@ -916,28 +916,7 @@ def __init__( # noqa: PLR0915
icon = QIcon(os.path.join(PartSegData.icons_dir, "icon_stack.png"))
self.setWindowIcon(icon)

menu_bar = self.menuBar()
file_menu = menu_bar.addMenu("File")
file_menu.addAction("&Open").triggered.connect(self.main_menu.load_image)
file_menu.addMenu(self.recent_file_menu)
file_menu.addAction("&Save segmentation").triggered.connect(self.main_menu.save_segmentation)
file_menu.addAction("&Save components").triggered.connect(self.main_menu.save_result)
view_menu = menu_bar.addMenu("View")
view_menu.addAction("Settings and Measurement").triggered.connect(self.main_menu.show_advanced_window)
view_menu.addAction("Additional output").triggered.connect(self.additional_layers_show)
view_menu.addAction("Additional output with data").triggered.connect(lambda: self.additional_layers_show(True))
view_menu.addAction("Napari viewer").triggered.connect(self.napari_viewer_show)
view_menu.addAction("Toggle Multiple Files").triggered.connect(self.toggle_multiple_files)
view_menu.addAction("Toggle console").triggered.connect(self._toggle_console)
view_menu.addAction("Toggle scale bar").triggered.connect(self._toggle_scale_bar)
action = view_menu.addAction("Screenshot")
action.triggered.connect(self.screenshot(self.image_view))
action.setShortcut(QKeySequence.StandardKey.Print)
image_menu = menu_bar.addMenu("Image operations")
image_menu.addAction("Image adjustment").triggered.connect(self.image_adjust_exec)
help_menu = menu_bar.addMenu("Help")
help_menu.addAction("State directory").triggered.connect(self.show_settings_directory)
help_menu.addAction("About").triggered.connect(self.show_about_dialog)
self._setup_menu_bar()

layout = QVBoxLayout()
layout.addWidget(self.main_menu)
Expand Down Expand Up @@ -968,6 +947,30 @@ def __init__( # noqa: PLR0915
geometry = self.settings.get_from_profile("main_window_geometry")
self.restoreGeometry(QByteArray.fromHex(bytes(geometry, "ascii")))

def _setup_menu_bar(self):
menu_bar = self.menuBar()
file_menu = menu_bar.addMenu("File")
file_menu.addAction("&Open").triggered.connect(self.main_menu.load_image)
file_menu.addMenu(self.recent_file_menu)
file_menu.addAction("&Save segmentation").triggered.connect(self.main_menu.save_segmentation)
file_menu.addAction("&Save components").triggered.connect(self.main_menu.save_result)
view_menu = menu_bar.addMenu("View")
view_menu.addAction("Settings and Measurement").triggered.connect(self.main_menu.show_advanced_window)
view_menu.addAction("Additional output").triggered.connect(self.additional_layers_show)
view_menu.addAction("Additional output with data").triggered.connect(lambda: self.additional_layers_show(True))
view_menu.addAction("Napari viewer").triggered.connect(self.napari_viewer_show)
view_menu.addAction("Toggle Multiple Files").triggered.connect(self.toggle_multiple_files)
view_menu.addAction("Toggle console").triggered.connect(self._toggle_console)
view_menu.addAction("Toggle scale bar").triggered.connect(self._toggle_scale_bar)
action = view_menu.addAction("Screenshot")
action.triggered.connect(self.screenshot(self.image_view))
action.setShortcut(QKeySequence.StandardKey.Print)
image_menu = menu_bar.addMenu("Image operations")
image_menu.addAction("Image adjustment").triggered.connect(self.image_adjust_exec)
help_menu = menu_bar.addMenu("Help")
help_menu.addAction("State directory").triggered.connect(self.show_settings_directory)
help_menu.addAction("About").triggered.connect(self.show_about_dialog)

def _toggle_scale_bar(self):
self.image_view.toggle_scale_bar()
super()._toggle_scale_bar()
Expand Down
34 changes: 28 additions & 6 deletions package/PartSeg/common_gui/napari_image_view.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import itertools
import logging
import platform
from contextlib import suppress
from dataclasses import dataclass, field
from enum import Enum
Expand Down Expand Up @@ -44,6 +45,20 @@
_napari_ge_4_17 = parse_version(napari.__version__) >= parse_version("0.4.17a1")
_napari_ge_5 = parse_version(napari.__version__) >= parse_version("0.5.0a1")

# if run with numpy<2 on macOS arm64 architecture compiled from pypi wheels
# then it will crash with bus error if numpy is used in different thread
if (
parse_version(np.__version__) < parse_version("2")
and platform.system() == "Darwin"
and platform.machine() == "arm64"
): # pragma: no cover
try:
USE_THREADS = "cibw-run" not in np.show_config("dicts")["Python Information"]["path"]
except (KeyError, TypeError):
USE_THREADS = True
else:
USE_THREADS = True


def get_highlight_colormap():
cmap_dict = {0: (0, 0, 0, 0), 1: "white", None: (0, 0, 0, 0)}
Expand Down Expand Up @@ -717,12 +732,19 @@ def add_image(self, image: Optional[Image], replace=False):

return image

def _prepare_layers(self, image, parameters, replace):
worker = prepare_layers(image, parameters, replace)
worker.returned.connect(self._add_image)
worker.finished.connect(self._remove_worker)
self.worker_list.append(worker)
worker.start()
if USE_THREADS: # pragma: no cover

def _prepare_layers(self, image, parameters, replace):
worker = prepare_layers(image, parameters, replace)
worker.returned.connect(self._add_image)
worker.finished.connect(self._remove_worker)
self.worker_list.append(worker)
worker.start()

else: # pragma: no cover

def _prepare_layers(self, image, parameters, replace):
self._add_image(_prepare_layers(image, parameters, replace))

def images_bounds(self) -> Tuple[List[int], List[int]]:
ranges = []
Expand Down
3 changes: 1 addition & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,7 @@ accelerate = [
"PyOpenGL-accelerate>=3.1.5 ; platform_machine != 'arm64'",
]
all = [
"PyOpenGL-accelerate>=3.1.5",
"PyQt5!=5.15.0,>=5.12.3",
"PartSeg[accelerate,pyqt5]"
]
docs = [
"autodoc-pydantic",
Expand Down

0 comments on commit af44d19

Please sign in to comment.