""" motion_capture/device.py BaseDevice that reads x/y position from CameraTracker. Channels: x_pos — horizontal position [px] (left=0, right=frame_width) y_pos — vertical position [px] (top=0, bottom=frame_height) Apply the pixel_to_mm filter in Signals → Channels to convert to mm. """ from __future__ import annotations import threading from typing import Any, Dict from devices.base_device import ( BaseDevice, ChannelConfig, DeviceInfo, DeviceStatus, ) class MotionCaptureDevice(BaseDevice): def __init__(self, tracker, device_id: str = "motion_capture"): super().__init__(DeviceInfo( device_id = device_id, name = "Motion Capture", device_type = "virtual", description = "Camera point-tracking — x/y position in pixels.", icon = "🎥", channels = [ ChannelConfig( channel_id = "x_pos", name = "X Position", unit = "px", min_value = 0.0, max_value = 1920.0, color = "#00d4ff", ), ChannelConfig( channel_id = "y_pos", name = "Y Position", unit = "px", min_value = 0.0, max_value = 1080.0, color = "#f72585", ), ], )) self._tracker = tracker self._lock = threading.Lock() self._last = (0.0, 0.0) # ── BaseDevice interface ────────────────────────────────────────────── def connect(self) -> bool: self.status = DeviceStatus.SIMULATED return True def disconnect(self) -> None: self.status = DeviceStatus.DISCONNECTED def read_channels(self) -> Dict[str, float]: pos = self._tracker.get_position() if pos is not None: with self._lock: self._last = pos with self._lock: x, y = self._last return {"x_pos": x, "y_pos": y} def write_channel(self, channel_id: str, value: Any) -> bool: return False def get_config_widget(self): from PyQt6.QtWidgets import QLabel lbl = QLabel( "Configure via the Motion Capture toolbar window.\n\n" "Apply 'pixel_to_mm' filter in Signals → Channels\n" "to convert pixel coordinates to millimetres." ) lbl.setObjectName("traceSource") lbl.setWordWrap(True) return lbl