diff --git a/scanslicer/ui_builders_shared.py b/scanslicer/ui_builders_shared.py index 81033e7..f2a5493 100644 --- a/scanslicer/ui_builders_shared.py +++ b/scanslicer/ui_builders_shared.py @@ -89,3 +89,155 @@ class ScrollbarWheelOnlyScrollArea(QScrollArea): return super().wheelEvent(event) + + +# ScanSlicerUiSharedMixin: class definition for ScanSlicer behavior. +class ScanSlicerUiSharedMixin: + # _build_section_title: function definition for ScanSlicer behavior. + def _build_section_title(self, text): + label = QLabel(text) + title_font = QFont(label.font()) + title_font.setPixelSize(self.SECTION_TITLE_FONT_PX) + title_font.setBold(True) + label.setFont(title_font) + label.setStyleSheet( + f"padding-top: {self.SECTION_TITLE_VPAD}px; " + f"padding-bottom: {self.SECTION_TITLE_VPAD}px;" + ) + return label + + # _add_vertical_spacer: function definition for ScanSlicer behavior. + def _add_vertical_spacer(self, target_layout, row_index): + target_layout.addItem( + QSpacerItem( + 0, + self.VERTICAL_SPACER_HEIGHT, + QSizePolicy.Policy.Minimum, + QSizePolicy.Policy.Fixed, + ), + row_index, + 0, + 1, + 2, + ) + + # _new_group_layout: function definition for ScanSlicer behavior. + def _new_group_layout(self): + group = QGroupBox() + group_layout = QGridLayout(group) + group_layout.setContentsMargins( + self.PANEL_CONTENT_MARGIN, + self.PANEL_CONTENT_MARGIN, + self.PANEL_CONTENT_MARGIN, + self.PANEL_CONTENT_MARGIN, + ) + return group, group_layout + + # _add_full_width_widget: function definition for ScanSlicer behavior. + def _add_full_width_widget(self, target_layout, row_index, widget): + target_layout.addWidget(widget, row_index, 0, 1, 2) + return row_index + 1 + + # _add_spacer_row: function definition for ScanSlicer behavior. + def _add_spacer_row(self, target_layout, row_index): + self._add_vertical_spacer(target_layout, row_index) + return row_index + 1 + + # _add_label_row: function definition for ScanSlicer behavior. + def _add_label_row(self, target_layout, row_index, text): + return self._add_full_width_widget(target_layout, row_index, QLabel(text)) + + # _create_panel_button: function definition for ScanSlicer behavior. + def _create_panel_button(self, text, callback, tooltip=None): + button = QPushButton(text) + if tooltip: + button.setToolTip(tooltip) + button.clicked.connect(callback) + return button + + # _build_parameter_block: function definition for ScanSlicer behavior. + def _build_parameter_block(self, parent, name, parameter): + param_block = QWidget(parent) + param_block_layout = QVBoxLayout(param_block) + param_block_layout.setContentsMargins(0, 0, 0, self.VERTICAL_SPACER_HEIGHT) + param_block_layout.setSpacing(3) + + param_label = QLabel(parameter.label) + param_label.setWordWrap(True) + param_block_layout.addWidget(param_label) + + if parameter.label_secondary: + param_secondary_label = QLabel(parameter.label_secondary) + param_secondary_label.setWordWrap(True) + secondary_font = QFont(param_secondary_label.font()) + secondary_info = QFontInfo(secondary_font) + if secondary_info.pixelSize() > 0: + secondary_font.setPixelSize(max(1, secondary_info.pixelSize() - 2)) + elif secondary_info.pointSizeF() > 0: + secondary_font.setPointSizeF(max(1.0, secondary_info.pointSizeF() - 1.0)) + else: + secondary_font.setPointSize(9) + param_secondary_label.setFont(secondary_font) + param_secondary_label.setStyleSheet("color: gray;") + param_block_layout.addWidget(param_secondary_label) + + if name in self.CHOICE_PARAM_OPTIONS: + control = QComboBox(param_block) + for option_label, option_value in self.CHOICE_PARAM_OPTIONS[name]: + control.addItem(option_label, option_value) + choice_index = control.findData(parameter.get()) + if choice_index >= 0: + control.setCurrentIndex(choice_index) + + # on_param_choice_changed: function definition for ScanSlicer behavior. + def on_param_choice_changed(index, p=parameter, combo=control, param_name=name): + value = combo.itemData(index) + p.set(value) + if param_name in ("bw_method", "detect_pipeline", "morph_op"): + self.update_parameter_visibility() + if not self.updating_param_controls: + self.set_preset_dropdown_custom() + + control.currentIndexChanged.connect(on_param_choice_changed) + param_block_layout.addWidget(control) + else: + slider_row = QWidget(param_block) + slider_row_layout = QHBoxLayout(slider_row) + slider_row_layout.setContentsMargins(0, 0, 0, 0) + slider_row_layout.setSpacing(6) + control = QSlider(Qt.Orientation.Horizontal) + control.setRange(parameter.min, parameter.max) + control.setSingleStep(max(1, parameter.step)) + control.setPageStep(max(1, parameter.step)) + control.setTickInterval(max(1, parameter.step)) + control.setValue(parameter.get()) + + value_label = QLabel(str(parameter.get())) + value_label.setAlignment(Qt.AlignRight | Qt.AlignVCenter) + value_label.setMinimumWidth(40) + + # on_param_slider_changed: function definition for ScanSlicer behavior. + def on_param_slider_changed(value, p=parameter, slider=control, label=value_label, param_name=name): + step = max(1, p.step) + snapped = p.min + round((value - p.min) / step) * step + snapped = min(p.max, max(p.min, snapped)) + if snapped != value: + slider.blockSignals(True) + slider.setValue(snapped) + slider.blockSignals(False) + value = snapped + p.set(value) + label.setText(str(p.get())) + if param_name in ("bw_method", "detect_pipeline", "morph_op"): + self.update_parameter_visibility() + if not self.updating_param_controls: + self.set_preset_dropdown_custom() + + control.valueChanged.connect(on_param_slider_changed) + slider_row_layout.addWidget(control, 1) + slider_row_layout.addWidget(value_label, 0) + param_block_layout.addWidget(slider_row) + + self.param_controls[name] = control + self.param_blocks[name] = param_block + return param_block