Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -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
Expand Down
17 changes: 16 additions & 1 deletion pyjibe/fd/__init__.py
Original file line number Diff line number Diff line change
@@ -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)
7 changes: 6 additions & 1 deletion pyjibe/fd/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@

class UiForceDistance(QtWidgets.QWidget):
_instance_counter = 0
# 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):
"""Base class for force-indentation analysis"""
Expand Down Expand Up @@ -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 = []

Expand Down Expand Up @@ -287,6 +290,8 @@ 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:
Expand Down
18 changes: 17 additions & 1 deletion pyjibe/head/__init__.py
Original file line number Diff line number Diff line change
@@ -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)
74 changes: 74 additions & 0 deletions tests/test_fd_autosave_override.py
Original file line number Diff line number Diff line change
@@ -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 `UiForceDistance.autosave` attributes
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 "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)
Loading