""" ui/device_panel.py Left sidebar showing all registered devices with status indicators. """ from PyQt6.QtWidgets import ( QWidget, QVBoxLayout, QLabel, QPushButton, QHBoxLayout, QScrollArea, QFrame ) from PyQt6.QtCore import Qt, pyqtSignal from devices.base_device import DeviceStatus from devices.device_registry import DeviceRegistry from core.acquisition import AcquisitionEngine class DeviceCard(QFrame): config_clicked = pyqtSignal(str) toggle_clicked = pyqtSignal(str, bool) STATUS_COLORS = { DeviceStatus.CONNECTED: "#3fb950", DeviceStatus.SIMULATED: "#58a6ff", DeviceStatus.DISCONNECTED: "#8b949e", DeviceStatus.CONNECTING: "#d29922", DeviceStatus.ERROR: "#f85149", } def __init__(self, device, parent=None): super().__init__(parent) self.device = device self.setObjectName("deviceCard") self._build() def _build(self): layout = QVBoxLayout(self) layout.setContentsMargins(8, 8, 8, 8) layout.setSpacing(4) # Header row hdr = QHBoxLayout() icon = QLabel(self.device.info.icon) icon.setObjectName("deviceIcon") hdr.addWidget(icon) name = QLabel(self.device.info.name) name.setObjectName("deviceName") hdr.addWidget(name) hdr.addStretch() color = self.STATUS_COLORS.get(self.device.status, "#8b949e") self.status_dot = QLabel("●") self.status_dot.setStyleSheet(f"color: {color}; font-size: 10px;") hdr.addWidget(self.status_dot) layout.addLayout(hdr) # ID + type sub = QLabel(f"{self.device.info.device_id} · {self.device.info.device_type}") sub.setObjectName("deviceSub") layout.addWidget(sub) # Channel count ch_count = sum(1 for ch in self.device.info.channels if ch.enabled) ch_lbl = QLabel(f"{ch_count} channel{'s' if ch_count != 1 else ''}") ch_lbl.setObjectName("deviceChannelCount") layout.addWidget(ch_lbl) # Config button cfg_btn = QPushButton("Configure") cfg_btn.setObjectName("configButton") cfg_btn.clicked.connect(lambda: self.config_clicked.emit(self.device.info.device_id)) layout.addWidget(cfg_btn) def refresh_status(self): color = self.STATUS_COLORS.get(self.device.status, "#8b949e") self.status_dot.setStyleSheet(f"color: {color}; font-size: 10px;") class DevicePanel(QWidget): config_requested = pyqtSignal(str) def __init__(self, registry: DeviceRegistry, engine: AcquisitionEngine): super().__init__() self.registry = registry self.engine = engine self._cards = {} self._build_ui() self.refresh() def _build_ui(self): layout = QVBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(0) header = QLabel(" DEVICES") header.setObjectName("panelHeader") layout.addWidget(header) scroll = QScrollArea() scroll.setWidgetResizable(True) scroll.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) scroll.setObjectName("deviceScroll") self._container = QWidget() self._inner = QVBoxLayout(self._container) self._inner.setContentsMargins(8, 8, 8, 8) self._inner.setSpacing(8) self._inner.addStretch() scroll.setWidget(self._container) layout.addWidget(scroll) def refresh(self): # Remove old cards for card in self._cards.values(): self._inner.removeWidget(card) card.deleteLater() self._cards.clear() for dev in self.registry.all_instances(): card = DeviceCard(dev) card.config_clicked.connect(self.config_requested.emit) # Insert before the stretch self._inner.insertWidget(self._inner.count() - 1, card) self._cards[dev.info.device_id] = card