From 2ba7cdf95c84a6b727acb9b0e99c968063219a5a Mon Sep 17 00:00:00 2001 From: Paul Glover Date: Tue, 21 Jul 2026 23:26:47 -0400 Subject: [PATCH] fix: restore saved export destination mode on image open The Folder combo stores ExportPresetOutputMode members as item data, but a persisted config round-trips through JSON as a plain string, so findData() never matched and the combo fell back to index 0. The panel showed "Subfolder of source" whatever was saved, and the next edit to any export field wrote that wrong mode back over the saved destination. Normalize the row to the enum before lookup, falling back to Absolute for unknown values. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_01VcGpSk4mhUGCv7iuUejeTf --- .../desktop/view/widgets/export_settings_form.py | 13 ++++++++++++- tests/test_export_settings_form.py | 16 ++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/negpy/desktop/view/widgets/export_settings_form.py b/negpy/desktop/view/widgets/export_settings_form.py index 0f7b7d20..70ed0e15 100644 --- a/negpy/desktop/view/widgets/export_settings_form.py +++ b/negpy/desktop/view/widgets/export_settings_form.py @@ -37,6 +37,17 @@ _LABEL_WIDTH = 90 +def _coerce_output_mode(mode: Any) -> ExportPresetOutputMode: + """Persisted configs come back from JSON as plain strings. The destination + combo holds StrEnum members as item data, and findData() compares across the + QVariant boundary — a plain string never matches, so the row must be + normalized to the enum before lookup.""" + try: + return ExportPresetOutputMode(mode) + except ValueError: + return ExportPresetOutputMode.ABSOLUTE + + def constrain_combo(combo: QComboBox, min_chars: int = 6) -> None: """Stop long item text from stretching the panel: size the combo to a small minimum and elide overflow, filling its row via the layout's spare space.""" @@ -509,7 +520,7 @@ def load(self, v: Dict[str, Any]) -> None: out_path = v.get("icc_output_path") self.icc_output_combo.setCurrentText(os.path.basename(out_path) if out_path else "None") - mode = v.get("output_mode", ExportPresetOutputMode.ABSOLUTE) + mode = _coerce_output_mode(v.get("output_mode")) idx = self.output_mode_combo.findData(mode) if idx >= 0: self.output_mode_combo.setCurrentIndex(idx) diff --git a/tests/test_export_settings_form.py b/tests/test_export_settings_form.py index d1883a9f..6378c842 100644 --- a/tests/test_export_settings_form.py +++ b/tests/test_export_settings_form.py @@ -120,6 +120,22 @@ def test_destination_subfields_track_output_mode(qapp): assert form._abspath_container.isHidden() +def test_destination_mode_restored_from_persisted_plain_string(qapp): + """Saved settings come back from JSON as plain strings, not StrEnum members — + the combo must still land on the saved destination mode.""" + form = ExportSettingsForm() + for mode in ExportPresetOutputMode: + form.load(_values(output_mode=str(mode.value))) + assert form.output_mode_combo.currentData() == mode, mode + assert form.values()["output_mode"] == mode, mode + + +def test_destination_mode_falls_back_to_absolute_when_unknown(qapp): + form = ExportSettingsForm() + form.load(_values(output_mode="not_a_mode")) + assert form.values()["output_mode"] == ExportPresetOutputMode.ABSOLUTE + + def test_load_does_not_emit_changed(qapp): form = ExportSettingsForm() fired = []