From 5111cf2786ff65a0c058c5ccace4b86252c2d862 Mon Sep 17 00:00:00 2001 From: Peter Jacobson Date: Mon, 15 Jun 2026 21:27:56 +1000 Subject: [PATCH] Expose 'Linear' row alignment in the GUI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The backend align_rows supports median, mean, and linear (linear also removes a straight slope within each row), and the Definitions help documents all three — but the GUI's "Align rows" dropdown only offered None/Median/Mean, so the documented 'linear' option was unreachable. Add "Linear" to the Align rows control (probeflow/gui/processing.py: combo items, state align_map, set_state reverse map, tooltip) and to the menu-bar mirror (probeflow/gui/dialogs/image_viewer_chrome_mixin.py). The state→op pipeline already passes the method through generically, so linear works end to end. Test: test_align_rows_exposes_linear_option. Co-Authored-By: Claude Opus 4.8 --- probeflow/gui/dialogs/image_viewer_chrome_mixin.py | 2 +- probeflow/gui/processing.py | 9 +++++---- tests/test_gui_processing_panel.py | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/probeflow/gui/dialogs/image_viewer_chrome_mixin.py b/probeflow/gui/dialogs/image_viewer_chrome_mixin.py index e0398d1..e3df784 100644 --- a/probeflow/gui/dialogs/image_viewer_chrome_mixin.py +++ b/probeflow/gui/dialogs/image_viewer_chrome_mixin.py @@ -241,7 +241,7 @@ def _build_viewer_menu_bar(self) -> None: processing_menu.addSeparator() self._add_combo_menu( processing_menu, "Align rows", self._processing_panel._align_combo, - ["None", "Median", "Mean"], + ["None", "Median", "Mean", "Linear"], ) self._add_combo_menu( processing_menu, "Bad line correction", self._processing_panel._bad_lines_combo, diff --git a/probeflow/gui/processing.py b/probeflow/gui/processing.py index b0f2d9a..9dbd438 100644 --- a/probeflow/gui/processing.py +++ b/probeflow/gui/processing.py @@ -129,10 +129,11 @@ def _col_lbl(text: str, target): line_lbl.setAlignment(Qt.AlignCenter) lay.addWidget(line_lbl) - self._align_combo = _combo_row("Align rows:", ["None", "Median", "Mean"]) + self._align_combo = _combo_row("Align rows:", ["None", "Median", "Mean", "Linear"]) self._align_combo.setToolTip( "Level each scan line by subtracting its median or mean, removing " - "row-to-row offsets and slow tilt along the slow-scan direction." + "row-to-row offsets and slow tilt along the slow-scan direction. " + "'Linear' also fits and removes a straight slope within each row." ) self._bad_lines_combo = _combo_row( @@ -340,7 +341,7 @@ def _col_lbl(text: str, target): lay.addWidget(self._filter_section) def state(self) -> dict: - align_map = {0: None, 1: "median", 2: "mean"} + align_map = {0: None, 1: "median", 2: "mean", 3: "linear"} bad_map = {0: None, 1: "step", 2: "mad"} cfg = { "align_rows": align_map[self._align_combo.currentIndex()], @@ -382,7 +383,7 @@ def set_state(self, state: dict | None) -> None: state = state or {} old_block = self._align_combo.blockSignals(True) self._align_combo.setCurrentIndex( - {None: 0, "median": 1, "mean": 2}.get(state.get("align_rows"), 0)) + {None: 0, "median": 1, "mean": 2, "linear": 3}.get(state.get("align_rows"), 0)) self._align_combo.blockSignals(old_block) self._bad_lines_combo.setCurrentIndex( {None: 0, "step": 1, "step_segments": 1, diff --git a/tests/test_gui_processing_panel.py b/tests/test_gui_processing_panel.py index 8f446ae..91a92dd 100644 --- a/tests/test_gui_processing_panel.py +++ b/tests/test_gui_processing_panel.py @@ -145,6 +145,20 @@ def test_browse_quick_panel_emits_only_thumbnail_corrections(qapp): assert panel.state() == {"align_rows": "median", "remove_bad_lines": None} +def test_align_rows_exposes_linear_option(qapp): + # The backend align_rows supports median/mean/linear; the GUI must offer all + # three so the documented 'linear' option is actually reachable. + from probeflow.gui import ProcessingControlPanel + + panel = ProcessingControlPanel("viewer_full") + items = [panel._align_combo.itemText(i) for i in range(panel._align_combo.count())] + assert items == ["None", "Median", "Mean", "Linear"] + + panel.set_state({"align_rows": "linear"}) + assert panel._align_combo.currentIndex() == 3 + assert panel.state()["align_rows"] == "linear" + + def test_viewer_full_panel_round_trips_standard_processing_state(qapp): from PySide6.QtWidgets import QCheckBox, QLabel, QPushButton from probeflow.gui import ProcessingControlPanel