summaryrefslogtreecommitdiff
path: root/plugins/motion_capture/plugin.py
diff options
context:
space:
mode:
authorChristian Kolset <christian.kolset@gmail.com>2026-06-09 19:39:35 -0600
committerChristian Kolset <christian.kolset@gmail.com>2026-06-09 19:39:35 -0600
commitb776e70b4cd94c5d1f53dde39b2e46f6447cac3e (patch)
treefade07a8ecd7a771c095a4d636fa5570e1f8ee7c /plugins/motion_capture/plugin.py
parent51f04ada503b02c598c9940c96fdc444cf5da52a (diff)
Motion capture: profile support, FPS config, persistent tracking, window state
Profile integration: - CameraDevice.get_save_config() serialises camera_index, simulate, resolution, fps — cameras now persist in .labdaq profiles - ProfileManager.register/unregister_device_factory() lets plugins register their device classes for profile restoration without core → plugin dependency - MotionCapturePlugin registers CameraDevice factory on load/unload Camera configuration: - Add Frame Rate combo to CameraPanel (Default/15/24/30/60/120 fps) defaults to 30 fps; flows through to CAP_PROP_FPS on connect - tracker.start() accepts fps param alongside existing resolution Tracking persistence: - CameraTracker.is_running() — checks thread alive state - MotionCaptureWindow initialises _connected from tracker.is_running() so reopening the window resumes the live feed without restarting the camera or losing the active tracking state Window state: - Plugin saves px_per_mm + camera_idx when window closes - Reopened window restores saved values and syncs mode/shape UI from tracker state via restore_ui_state() (signals blocked to avoid resetting tracker) - Window type changed to Qt.WindowType.Window for independent close button on all Linux WMs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'plugins/motion_capture/plugin.py')
-rw-r--r--plugins/motion_capture/plugin.py33
1 files changed, 28 insertions, 5 deletions
diff --git a/plugins/motion_capture/plugin.py b/plugins/motion_capture/plugin.py
index 0b44cb4..9cf9557 100644
--- a/plugins/motion_capture/plugin.py
+++ b/plugins/motion_capture/plugin.py
@@ -49,12 +49,16 @@ class MotionCapturePlugin(LabPlugin):
# ── Lifecycle ─────────────────────────────────────────────────────────
def on_load(self, context: PluginContext) -> None:
- self._ctx = context
- self._btn = None # toolbar button reference
- self._windows: dict = {} # device_id → MotionCaptureWindow
+ self._ctx = context
+ self._btn = None # toolbar button reference
+ self._windows: dict = {} # device_id → MotionCaptureWindow
+ self._window_state: dict = {} # device_id → {px_per_mm, camera_idx}
from ui.add_device_dialog import _PANELS
from camera_panel import CameraPanel
_PANELS["Camera"] = (CameraPanel, "cam")
+ from core.profile import ProfileManager
+ from device import CameraDevice
+ ProfileManager.register_device_factory(CameraDevice.DEVICE_TYPE, CameraDevice)
def on_unload(self) -> None:
for win in list(self._windows.values()):
@@ -62,6 +66,9 @@ class MotionCapturePlugin(LabPlugin):
self._windows.clear()
from ui.add_device_dialog import _PANELS
_PANELS.pop("Camera", None)
+ from core.profile import ProfileManager
+ from device import CameraDevice
+ ProfileManager.unregister_device_factory(CameraDevice.DEVICE_TYPE)
# ── Integration hooks ─────────────────────────────────────────────────
@@ -133,11 +140,27 @@ class MotionCapturePlugin(LabPlugin):
self._btn.setChecked(False)
return
win = MotionCaptureWindow(device._tracker)
- win.closed.connect(lambda: self._close_window(dev_id))
+
+ # Restore mode/shape from live tracker state
+ win.restore_ui_state(
+ mode=device._tracker.track_mode,
+ shape=device._tracker.template_shape,
+ )
+ # Restore px/mm and camera index from last session
+ if dev_id in self._window_state:
+ state = self._window_state[dev_id]
+ win.set_px_per_mm(state["px_per_mm"])
+ win.set_camera_index(state["camera_idx"])
+
+ win.closed.connect(lambda d=dev_id, w=win: self._on_window_closed(d, w))
self._windows[dev_id] = win
win.show()
- def _close_window(self, dev_id: str) -> None:
+ def _on_window_closed(self, dev_id: str, win) -> None:
+ self._window_state[dev_id] = {
+ "px_per_mm": win.get_px_per_mm(),
+ "camera_idx": win.get_camera_index(),
+ }
self._windows.pop(dev_id, None)
if not self._windows and self._btn is not None:
self._btn.setChecked(False)