summaryrefslogtreecommitdiff
path: root/plugins/motion_capture/device.py
diff options
context:
space:
mode:
authorChristian Kolset <ckolset@colostate.edu>2026-08-17 10:02:16 -0600
committerChristian Kolset <ckolset@colostate.edu>2026-08-17 10:02:16 -0600
commitabab8f7938dada9a2abff31e719186b99934601a (patch)
tree6f013cb54256205b3eadf467001e86a882a7f57b /plugins/motion_capture/device.py
parente749cca30da2d02839aca736873e7e5ad45e191c (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 'plugins/motion_capture/device.py')
-rw-r--r--plugins/motion_capture/device.py12
1 files changed, 8 insertions, 4 deletions
diff --git a/plugins/motion_capture/device.py b/plugins/motion_capture/device.py
index 3369962..fc1c588 100644
--- a/plugins/motion_capture/device.py
+++ b/plugins/motion_capture/device.py
@@ -69,17 +69,21 @@ class CameraDevice(BaseDevice):
def connect(self) -> bool:
from tracker import CameraTracker
self._tracker = CameraTracker()
+ self.status = DeviceStatus.CONNECTING
+ # Camera opens on the tracker's background thread (cv2.VideoCapture
+ # can block for seconds) — status flips to CONNECTED/SIMULATED once
+ # the outcome is known, via this callback, instead of blocking here.
self._tracker.start(
-1 if self._simulate else self._camera_index,
resolution=self._resolution,
fps=self._fps,
+ on_result=self._on_tracker_result,
)
- if self._tracker.simulated:
- self.status = DeviceStatus.SIMULATED
- else:
- self.status = DeviceStatus.CONNECTED
return True
+ def _on_tracker_result(self, simulated: bool) -> None:
+ self.status = DeviceStatus.SIMULATED if simulated else DeviceStatus.CONNECTED
+
def disconnect(self) -> None:
if self._tracking_win is not None:
self._tracking_win.close()