diff options
| author | Christian Kolset <ckolset@colostate.edu> | 2026-08-17 10:02:16 -0600 |
|---|---|---|
| committer | Christian Kolset <ckolset@colostate.edu> | 2026-08-17 10:02:16 -0600 |
| commit | abab8f7938dada9a2abff31e719186b99934601a (patch) | |
| tree | 6f013cb54256205b3eadf467001e86a882a7f57b /ui/windows | |
| parent | e749cca30da2d02839aca736873e7e5ad45e191c (diff) | |
Fix motion capture UI freezes: throttling, async camera open/scan, stale poll
Several compounding freeze sources in plugins/motion_capture/:
- tracker.py: _run_real had no frame-rate cap independent of the camera's
configured fps (up to 120fps/2560x1440 was possible), and no bound on
consecutive failed reads — a camera that "opens" (cv2 isOpened() can lie,
seen with Windows MSMF/obsensor) but never actually delivers frames would
get hammered forever. Added a 20Hz processing cap, a real test-read before
committing to a camera, and a give-up-after-30-failures fallback to
simulation.
- tracker.py/device.py: cv2.VideoCapture() opening a real camera is a slow,
blocking call — CameraTracker.start() used to do this synchronously on
the caller's thread, which froze the whole app when ProfileManager.apply()
called it during profile load. Now runs on the background thread; status
reporting (CameraDevice) moved to an on_result callback since the caller
can no longer learn the outcome synchronously.
- window.py: the Camera View preview ran a 30Hz GUI-thread timer doing a
full-frame copy + QImage/QPixmap rebuild every tick unconditionally (no
visibility check) — dropped to 10Hz and skips entirely when hidden.
Camera enumeration (list_cameras(), up to 8 sequential cv2.VideoCapture
probes) also ran inline in the window constructor, blocking it open the
same way camera_panel.py's CameraScanThread already avoided for the Add
Device dialog — moved to the same async pattern here.
config_window.py: separately, ConfigWindow._dev_poll (device status label
refresh) was stopped in closeEvent and never restarted, since MainWindow
reuses the same ConfigWindow instance across opens — invisible before
because connect() was synchronous, but with the above change a device can
sit in CONNECTING for a while, and a stale poll made that look stuck
forever after reopening the window. Restart it in showEvent.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Diffstat (limited to 'ui/windows')
| -rw-r--r-- | ui/windows/config_window.py | 13 |
1 files changed, 11 insertions, 2 deletions
diff --git a/ui/windows/config_window.py b/ui/windows/config_window.py index 34cc789..604b6ed 100644 --- a/ui/windows/config_window.py +++ b/ui/windows/config_window.py @@ -24,7 +24,7 @@ from PyQt6.QtWidgets import ( QDoubleSpinBox, ) from PyQt6.QtCore import Qt, pyqtSignal, QTimer -from PyQt6.QtGui import QCloseEvent, QColor +from PyQt6.QtGui import QCloseEvent, QColor, QShowEvent from devices.device_registry import DeviceRegistry from core.acquisition import AcquisitionEngine @@ -535,7 +535,16 @@ class ConfigWindow(QWidget): if idx == 2: self._plot_tab.refresh_channels() - # ── Close ──────────────────────────────────────────────────────────────── + # ── Show / Close ───────────────────────────────────────────────────────── + + def showEvent(self, e: QShowEvent): + # closeEvent stops this poll; MainWindow reuses this same ConfigWindow + # instance across opens (doesn't recreate it), so without restarting + # here, reopening after a prior close leaves device status frozen at + # whatever it was at close time — e.g. stuck showing "Connecting…" + # for an async-connecting device like the motion capture camera. + self._dev_poll.start() + super().showEvent(e) def closeEvent(self, e: QCloseEvent): self._dev_poll.stop() |
