Skip to content

Commit 06b1fec

Browse files
committed
Fix cleanup guards and scan state transitions
Introduce explicit cleanup flags and tighten cleanup logic to avoid races and double-run teardown. Add _cleanp_requested and _cleanup_completed, replace uses of the old _cleanup_done flag, and only mark cleanup completed when no scan/preview/probe worker is active. Also always transition the scan state to IDLE when cleaning up. Update the e2e test to wait for scans to finish earlier and reduce related timeouts to reflect the more deterministic teardown behavior.
1 parent ef4e8ef commit 06b1fec

2 files changed

Lines changed: 16 additions & 7 deletions

File tree

dlclivegui/gui/camera_config/camera_config_dialog.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ def __init__(
4848
self.setWindowTitle("Configure Cameras")
4949
self.setMinimumSize(960, 720)
5050

51+
self._cleanp_requested = False
52+
self._cleanup_completed = False
53+
5154
self._dlc_camera_id: str | None = None
5255
# self.dlc_camera_id: str | None = None
5356
# Actual/working camera settings
@@ -106,7 +109,7 @@ def dlc_camera_id(self, value: str | None) -> None:
106109
def showEvent(self, event):
107110
super().showEvent(event)
108111
# Reset cleanup guard so close cleanup runs for each session
109-
self._cleanup_done = False
112+
self._cleanup_completed = False
110113

111114
# Rebuild the working copy from the latest “accepted” settings
112115
self._working_settings = self._multi_camera_settings.model_copy(deep=True)
@@ -192,9 +195,9 @@ def reject(self) -> None:
192195
def _on_close_cleanup(self) -> None:
193196
"""Stop preview, cancel workers, and reset scan UI. Safe to call multiple times."""
194197
# Guard to avoid running twice if closeEvent + reject/accept both run
195-
if getattr(self, "_cleanup_done", False):
198+
if getattr(self, "_cleanup_completed", False):
196199
return
197-
self._cleanup_done = True
200+
self._cleanp_requested = True
198201

199202
# Stop preview (loader + backend + timer)
200203
try:
@@ -256,6 +259,13 @@ def _on_close_cleanup(self) -> None:
256259
except Exception:
257260
pass
258261

262+
if (
263+
not self._is_scan_running()
264+
and not self._is_preview_live()
265+
and not (self._probe_worker and self._probe_worker.isRunning())
266+
):
267+
self._cleanup_completed = True
268+
259269
# -------------------------------
260270
# UI setup
261271
# -------------------------------
@@ -637,9 +647,7 @@ def _on_scan_thread_finished(self) -> None:
637647
return
638648

639649
self._cleanup_scan_worker()
640-
# Only transition to IDLE for the worker that was actually active.
641-
if self._scan_state not in (CameraScanState.DONE,):
642-
self._set_scan_state(CameraScanState.IDLE)
650+
self._set_scan_state(CameraScanState.IDLE)
643651

644652
def _on_scan_result(self, cams: list) -> None:
645653
if self.sender() is not self._scan_worker:

tests/gui/camera_config/test_cam_dialog_e2e.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ def dialog(qtbot, patch_detect_cameras):
120120
d = CameraConfigDialog(None, s)
121121
qtbot.addWidget(d)
122122
d.show()
123+
qtbot.waitUntil(lambda: not d._is_scan_running(), timeout=2000)
123124
qtbot.waitExposed(d)
124125

125126
yield d
@@ -136,7 +137,7 @@ def dialog(qtbot, patch_detect_cameras):
136137
d.close()
137138

138139
qtbot.waitUntil(lambda: d._preview.loader is None, timeout=2000)
139-
qtbot.waitUntil(lambda: not d._is_scan_running(), timeout=5000)
140+
qtbot.waitUntil(lambda: not d._is_scan_running(), timeout=2000)
140141
qtbot.wait(50)
141142
qtbot.waitUntil(lambda: d._preview.state == PreviewState.IDLE, timeout=2000)
142143

0 commit comments

Comments
 (0)