From b9c7dc0c881549761a1a330410f51e8e40a8e0ba Mon Sep 17 00:00:00 2001 From: Eoghan O'Connell Date: Thu, 23 Apr 2026 12:49:34 +0200 Subject: [PATCH 1/4] fix: remember the choice of the user upon curve open --- pyjibe/fd/__init__.py | 17 ++++++++++++++++- pyjibe/fd/main.py | 6 +++++- pyjibe/head/__init__.py | 18 +++++++++++++++++- 3 files changed, 38 insertions(+), 3 deletions(-) diff --git a/pyjibe/fd/__init__.py b/pyjibe/fd/__init__.py index bf0762f..43b8099 100644 --- a/pyjibe/fd/__init__.py +++ b/pyjibe/fd/__init__.py @@ -1 +1,16 @@ -from .main import UiForceDistance # noqa: F401 +"""Force-distance analysis UI package. + +This package intentionally avoids importing Qt-heavy modules at import time to +prevent circular imports with `pyjibe.registry` / `pyjibe.head`. +""" + +from __future__ import annotations + +__all__ = ["UiForceDistance"] + + +def __getattr__(name: str): + if name == "UiForceDistance": + from .main import UiForceDistance + return UiForceDistance + raise AttributeError(name) diff --git a/pyjibe/fd/main.py b/pyjibe/fd/main.py index 73a4d7b..6d8d8dd 100644 --- a/pyjibe/fd/main.py +++ b/pyjibe/fd/main.py @@ -33,6 +33,9 @@ class UiForceDistance(QtWidgets.QWidget): _instance_counter = 0 + # Remember the user's autosave overwrite choice for the current app run + # (across multiple analysis subwindows), but do not persist it across restarts. + _autosave_override_session = -1 def __init__(self, *args, **kwargs): """Base class for force-indentation analysis""" @@ -101,7 +104,7 @@ def __init__(self, *args, **kwargs): # 0: do not override existing files # 1: override existing files # 2: create additional file with date - self._autosave_override = -1 + self._autosave_override = UiForceDistance._autosave_override_session # Filenames that were created by this instance self._autosave_original_files = [] @@ -287,6 +290,7 @@ def autosave(self, fdist): oride = 2 if dlgui.cb_remember.isChecked(): self._autosave_override = oride + UiForceDistance._autosave_override_session = oride if dlgui.cb_disableauto.isChecked(): self.cb_autosave.setChecked(0) if oride == 0: diff --git a/pyjibe/head/__init__.py b/pyjibe/head/__init__.py index a709c9b..ea20be6 100644 --- a/pyjibe/head/__init__.py +++ b/pyjibe/head/__init__.py @@ -1 +1,17 @@ -from .main import PyJibe # noqa: F401 +"""Main application window package. + +Keep imports lazy to avoid circular imports when other modules import +`pyjibe.head.*` utilities (e.g. custom widgets) while `pyjibe.head.main` pulls +in the registry. +""" + +from __future__ import annotations + +__all__ = ["PyJibe"] + + +def __getattr__(name: str): + if name == "PyJibe": + from .main import PyJibe + return PyJibe + raise AttributeError(name) From 90c12402e6310e9de68cc4b75a45d56cf19addf4 Mon Sep 17 00:00:00 2001 From: Eoghan O'Connell Date: Thu, 23 Apr 2026 12:50:00 +0200 Subject: [PATCH 2/4] tests: add simple dummy test for the remember choice --- tests/test_fd_autosave_override.py | 74 ++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 tests/test_fd_autosave_override.py diff --git a/tests/test_fd_autosave_override.py b/tests/test_fd_autosave_override.py new file mode 100644 index 0000000..b65a6f4 --- /dev/null +++ b/tests/test_fd_autosave_override.py @@ -0,0 +1,74 @@ +from unittest import mock + +from PyQt6 import QtCore, QtWidgets + +import pyjibe.fd.main as fdmain + + +def test_autosave_override_remembered_across_instances(qtbot, tmp_path): + # Ensure test isolation: this is a session-level value (class attribute), + # so we must reset it explicitly at the start of the test. + fdmain.UiForceDistance._autosave_override_session = -1 + + adir = tmp_path / "data" + adir.mkdir() + existing = adir / "pyjibe_fit_results_leaf.tsv" + # this file triggers the "override existing results file?" dialog. + existing.write_text("existing\n", encoding="utf-8") + + class DummyCurve: + # Minimal stub matching the attributes used by `UiForceDistance.autosave`. + def __init__(self, path): + self.path = str(path) + self.fit_properties = {"success": True, "model_key": "dummy"} + + def build_fd(parent): + widget = fdmain.UiForceDistance(parent) + qtbot.addWidget(widget) + widget.cb_autosave.setChecked(True) + curve = DummyCurve(adir / "curve1.jpk-force") + widget.data_set = [curve] + # autosave() filters based on the "use" checkbox in column 3. + item = QtWidgets.QTreeWidgetItem(widget.list_curves, ["", "", "", ""]) + item.setCheckState(3, QtCore.Qt.CheckState.Checked) + return widget, curve + + class _Checked: + # Simple stand-in for Qt checkable widgets used by the dialog UI. + def __init__(self, checked): + self._checked = checked + + def isChecked(self): + return self._checked + + class StubDlgAutosave: + # UI stub that selects "Override" and checks "Remember this choice for now". + def setupUi(self, _dlgwin): + self.btn_nothing = _Checked(False) + self.btn_override = _Checked(True) + self.btn_new = _Checked(False) + self.cb_remember = _Checked(True) + self.cb_disableauto = _Checked(False) + + parent1 = QtWidgets.QMdiSubWindow() + fd1, curve1 = build_fd(parent1) + + with mock.patch.object(fdmain.export, "save_tsv_metadata_results"): + with mock.patch.object(fdmain, "DlgAutosave", StubDlgAutosave): + with mock.patch.object(QtWidgets.QDialog, "exec", lambda _self: 1): + fd1.autosave(curve1) + + # The remembered choice is stored session-wide (across subwindows). + assert fdmain.UiForceDistance._autosave_override_session == 1 + + parent2 = QtWidgets.QMdiSubWindow() + fd2, curve2 = build_fd(parent2) + + class RaiseDlgAutosave: + # if we remembered the choice, autosave() won't construct the dialog. + def __init__(self, *args, **kwargs): + raise AssertionError("DlgAutosave should not be constructed") + + with mock.patch.object(fdmain.export, "save_tsv_metadata_results"): + with mock.patch.object(fdmain, "DlgAutosave", RaiseDlgAutosave): + fd2.autosave(curve2) From fb4092cbdebc1dc31b2eb4cfd5898bf8c79b3a17 Mon Sep 17 00:00:00 2001 From: Eoghan O'Connell Date: Thu, 23 Apr 2026 12:52:54 +0200 Subject: [PATCH 3/4] lint codebase --- pyjibe/fd/main.py | 7 ++++--- tests/test_fd_autosave_override.py | 4 ++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/pyjibe/fd/main.py b/pyjibe/fd/main.py index 6d8d8dd..35c27b9 100644 --- a/pyjibe/fd/main.py +++ b/pyjibe/fd/main.py @@ -33,8 +33,8 @@ class UiForceDistance(QtWidgets.QWidget): _instance_counter = 0 - # Remember the user's autosave overwrite choice for the current app run - # (across multiple analysis subwindows), but do not persist it across restarts. + # remember the user's autosave overwrite choice for the current gui + # but do not persist it across restarts. _autosave_override_session = -1 def __init__(self, *args, **kwargs): @@ -290,7 +290,8 @@ def autosave(self, fdist): oride = 2 if dlgui.cb_remember.isChecked(): self._autosave_override = oride - UiForceDistance._autosave_override_session = oride + UiForceDistance._autosave_override_session = ( + oride) if dlgui.cb_disableauto.isChecked(): self.cb_autosave.setChecked(0) if oride == 0: diff --git a/tests/test_fd_autosave_override.py b/tests/test_fd_autosave_override.py index b65a6f4..1887ce9 100644 --- a/tests/test_fd_autosave_override.py +++ b/tests/test_fd_autosave_override.py @@ -17,7 +17,7 @@ def test_autosave_override_remembered_across_instances(qtbot, tmp_path): existing.write_text("existing\n", encoding="utf-8") class DummyCurve: - # Minimal stub matching the attributes used by `UiForceDistance.autosave`. + # minimal stub matching the `UiForceDistance.autosave` attributes def __init__(self, path): self.path = str(path) self.fit_properties = {"success": True, "model_key": "dummy"} @@ -42,7 +42,7 @@ def isChecked(self): return self._checked class StubDlgAutosave: - # UI stub that selects "Override" and checks "Remember this choice for now". + # UI stub that selects "Override" and "Remember this choice for now". def setupUi(self, _dlgwin): self.btn_nothing = _Checked(False) self.btn_override = _Checked(True) From 465777d7cac9b03501222c946e3beb25f73dd507 Mon Sep 17 00:00:00 2001 From: Eoghan O'Connell Date: Thu, 23 Apr 2026 14:35:45 +0200 Subject: [PATCH 4/4] update CHANGELOG --- CHANGELOG | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index c396111..ec949b3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,5 @@ +0.16.4 + - fix: remember user's choice upon curve open (#44, #45) 0.16.3 - setup: bump afmformats to 0.18.6 - setup: bump nanite to 4.2.3