From b295d425a481e9e73bedc9e7b4dca5131a0a8e99 Mon Sep 17 00:00:00 2001 From: dev Date: Thu, 26 Feb 2026 10:35:09 +0000 Subject: [PATCH] feat(ui_builders_preview): implement class ScanSlicerUiPreviewMixin --- scanslicer/ui_builders_preview.py | 178 ++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) diff --git a/scanslicer/ui_builders_preview.py b/scanslicer/ui_builders_preview.py index 2bd4f80..25cf290 100644 --- a/scanslicer/ui_builders_preview.py +++ b/scanslicer/ui_builders_preview.py @@ -6,3 +6,181 @@ from .ui_builders_shared import ScanSlicerUiSharedMixin, icon_from_svg_data_url # ScanSlicerUiPreviewMixin: class definition for ScanSlicer behavior. +class ScanSlicerUiPreviewMixin(ScanSlicerUiSharedMixin): + # _create_toolbar_button: function definition for ScanSlicer behavior. + def _create_toolbar_button( + self, + parent, + text, + tooltip, + icon_data_url, + callback, + checkable=False, + enabled=True, + use_toggled=False, + ): + button = QPushButton(text, parent) + if checkable: + button.setCheckable(True) + button.setEnabled(enabled) + button.setToolTip(tooltip) + icon = icon_from_svg_data_url(icon_data_url, 20, palette=button.palette()) + if icon is not None: + button.setText("") + button.setIcon(icon) + button.setIconSize(QSize(20, 20)) + if use_toggled: + button.toggled.connect(callback) + else: + button.clicked.connect(callback) + return button + + # _build_preview_panel: function definition for ScanSlicer behavior. + def _build_preview_panel(self, root): + preview_panel = QWidget(root) + preview_layout = QGridLayout(preview_panel) + preview_layout.setContentsMargins(0, 0, 0, 0) + + preview_toolbar = QWidget(preview_panel) + preview_toolbar_layout = QHBoxLayout(preview_toolbar) + preview_toolbar_layout.setContentsMargins(0, 0, 0, 0) + self.preview_toolbar = preview_toolbar + + self.detect_button = self._create_toolbar_button( + preview_toolbar, + text="Detect", + tooltip="Detect pictures", + icon_data_url=self.DETECT_ICON_DATA_URL, + callback=lambda: self.update_preview(), + ) + preview_toolbar_layout.addWidget(self.detect_button) + + self.preview_filter_button = self._create_toolbar_button( + preview_toolbar, + text="Filter", + tooltip="Preview filter output", + icon_data_url=self.PREVIEW_FILTER_ICON_DATA_URL, + callback=self.set_preview_filter_enabled, + checkable=True, + use_toggled=True, + ) + preview_toolbar_layout.addWidget(self.preview_filter_button) + + self.reset_view_button = self._create_toolbar_button( + preview_toolbar, + text="Reset view", + tooltip="Reset and center view (95%)", + icon_data_url=self.RESET_VIEW_ICON_DATA_URL, + callback=self.reset_canvas_view, + ) + preview_toolbar_layout.addWidget(self.reset_view_button) + + self.active_box_label = QLabel("Active Box: none") + preview_toolbar_layout.addWidget(self.active_box_label) + preview_toolbar_layout.addStretch(1) + + self.active_box_lock_button = self._create_toolbar_button( + preview_toolbar, + text="🔒", + tooltip="Lock/unlock active box", + icon_data_url=self.LOCK_ICON_DATA_URL, + callback=self.toggle_active_box_lock, + checkable=True, + enabled=False, + use_toggled=True, + ) + preview_toolbar_layout.addWidget(self.active_box_lock_button) + + self.all_boxes_lock_button = self._create_toolbar_button( + preview_toolbar, + text="Lock all", + tooltip="Lock or unlock all boxes", + icon_data_url=self.LOCK_ALL_ICON_DATA_URL, + callback=self.toggle_all_boxes_lock, + checkable=True, + enabled=False, + use_toggled=True, + ) + preview_toolbar_layout.addWidget(self.all_boxes_lock_button) + + self.active_box_squarize_button = self._create_toolbar_button( + preview_toolbar, + text="▭", + tooltip="Squarize active box", + icon_data_url=self.SQUARIZE_ICON_DATA_URL, + callback=self.squarize_active_box, + enabled=False, + ) + preview_toolbar_layout.addWidget(self.active_box_squarize_button) + + self.active_box_reset_rotation_button = self._create_toolbar_button( + preview_toolbar, + text="Reset rotation", + tooltip="Reset active box rotation/tilt", + icon_data_url=self.RESET_ROTATION_ICON_DATA_URL, + callback=self.reset_active_box_rotation, + enabled=False, + ) + preview_toolbar_layout.addWidget(self.active_box_reset_rotation_button) + + self.add_box_button = self._create_toolbar_button( + preview_toolbar, + text="Add box", + tooltip="Add manual bounding box", + icon_data_url=self.ADD_BOX_ICON_DATA_URL, + callback=self.add_box, + ) + preview_toolbar_layout.addWidget(self.add_box_button) + + self.active_box_remove_button = self._create_toolbar_button( + preview_toolbar, + text="✕", + tooltip="Remove active box", + icon_data_url=self.REMOVE_ICON_DATA_URL, + callback=self.remove_active_box, + enabled=False, + ) + preview_toolbar_layout.addWidget(self.active_box_remove_button) + + self.active_box_rotate_button = self._create_toolbar_button( + preview_toolbar, + text="⟳", + tooltip="Rotate mode: drag active box corner handles to rotate", + icon_data_url=self.ROTATE_ICON_DATA_URL, + callback=self.set_rotate_handles_mode, + checkable=True, + use_toggled=True, + ) + preview_toolbar_layout.addWidget(self.active_box_rotate_button) + + self.slicing_canvas = QtSlicingCanvas(preview_panel) + self.slicing_canvas.active_slice_changed.connect(self.on_active_slice_changed) + + preview_layout.addWidget(preview_toolbar, 0, 0) + preview_layout.addWidget(self.slicing_canvas, 1, 0) + preview_layout.setRowStretch(1, 1) + return preview_panel + + # _build_status_bar: function definition for ScanSlicer behavior. + def _build_status_bar(self): + status_bar = self.statusBar() + status_bar.setMinimumHeight(30) + status_bar.setSizeGripEnabled(False) + status_bar.setStyleSheet("QStatusBar::item { border: none; }") + self.status_message_label = QLabel("Ready.") + self.status_message_label.setAlignment(Qt.AlignLeft | Qt.AlignVCenter) + status_message_container = QWidget(status_bar) + status_message_layout = QHBoxLayout(status_message_container) + status_message_layout.setContentsMargins(10, 0, 10, 6) + status_message_layout.setSpacing(0) + status_message_layout.addWidget(self.status_message_label) + status_bar.addWidget(status_message_container, 1) + + self.status_filename_label = QLabel("No file") + self.status_filename_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) + status_filename_container = QWidget(status_bar) + status_filename_layout = QHBoxLayout(status_filename_container) + status_filename_layout.setContentsMargins(10, 0, 10, 6) + status_filename_layout.setSpacing(0) + status_filename_layout.addWidget(self.status_filename_label) + status_bar.addPermanentWidget(status_filename_container)