From 4244e5d97da0273fed70f734a4cfe822bd15c9cb Mon Sep 17 00:00:00 2001 From: Christian Kolset Date: Thu, 23 Apr 2026 10:52:37 -0600 Subject: Fixed clear signal feature to inlcude all signals --- core/__pycache__/acquisition.cpython-312.pyc | Bin 11417 -> 11456 bytes core/__pycache__/signal_processor.cpython-312.pyc | Bin 23682 -> 24525 bytes core/acquisition.py | 2 +- core/signal_processor.py | 13 ++++ ui/__pycache__/main_window.cpython-312.pyc | Bin 27500 -> 28733 bytes ui/main_window.py | 19 +++++- ui/style_dark.qss | 72 +++++++++++++++++++++- ui/style_light.qss | 57 +++++++++++++++++ 8 files changed, 160 insertions(+), 3 deletions(-) diff --git a/core/__pycache__/acquisition.cpython-312.pyc b/core/__pycache__/acquisition.cpython-312.pyc index c593432..cb5f3f3 100644 Binary files a/core/__pycache__/acquisition.cpython-312.pyc and b/core/__pycache__/acquisition.cpython-312.pyc differ diff --git a/core/__pycache__/signal_processor.cpython-312.pyc b/core/__pycache__/signal_processor.cpython-312.pyc index e914490..4572e55 100644 Binary files a/core/__pycache__/signal_processor.cpython-312.pyc and b/core/__pycache__/signal_processor.cpython-312.pyc differ diff --git a/core/acquisition.py b/core/acquisition.py index 57fda7e..a984c71 100644 --- a/core/acquisition.py +++ b/core/acquisition.py @@ -176,7 +176,7 @@ class AcquisitionEngine(QObject): for ch in dev.info.channels: val = readings.get(ch.channel_id) - if val is None: + if val is None or not ch.enabled: log_row.append("") continue buf = self._buffers.get(dev.info.device_id, {}).get(ch.channel_id) diff --git a/core/signal_processor.py b/core/signal_processor.py index 0d4ed39..417d142 100644 --- a/core/signal_processor.py +++ b/core/signal_processor.py @@ -359,6 +359,14 @@ class SignalProcessor(QObject): def all_virtual_channel_ids(self) -> List[str]: return list(self._derived_bufs.keys()) + def clear_history(self): + """Clear derived channel buffers and latest-value cache.""" + with self._lock: + for t_buf, v_buf in self._derived_bufs.values(): + t_buf.clear() + v_buf.clear() + self._latest.clear() + # ── Main data path ──────────────────────────────────────────────────── def on_raw_data(self, device_id: str, channel_id: str, @@ -401,6 +409,11 @@ class SignalProcessor(QObject): try: result = self._compute_derived(dc, inputs, timestamp, latest) if result is not None: + # Write back into local snapshot so downstream derived + # channels that depend on this one can see it this pass + latest[("derived", dc.channel_id)] = (timestamp, result) + with self._lock: + self._latest[("derived", dc.channel_id)] = (timestamp, result) bufs = self._derived_bufs.get(dc.channel_id) if bufs: bufs[0].append(timestamp) diff --git a/ui/__pycache__/main_window.cpython-312.pyc b/ui/__pycache__/main_window.cpython-312.pyc index ceb1a97..054aa5b 100644 Binary files a/ui/__pycache__/main_window.cpython-312.pyc and b/ui/__pycache__/main_window.cpython-312.pyc differ diff --git a/ui/main_window.py b/ui/main_window.py index ec0f39d..483ca49 100644 --- a/ui/main_window.py +++ b/ui/main_window.py @@ -10,10 +10,23 @@ from PyQt6.QtWidgets import ( QMainWindow, QWidget, QHBoxLayout, QVBoxLayout, QSplitter, QStatusBar, QLabel, QPushButton, QToolBar, QSizePolicy, QApplication, QFrame, + QAbstractSpinBox, QComboBox, ) -from PyQt6.QtCore import Qt, QTimer, pyqtSlot +from PyQt6.QtCore import Qt, QTimer, QObject, QEvent, pyqtSlot import os + +class _WheelBlocker(QObject): + """App-level event filter: blocks accidental scroll-wheel changes on + spin boxes, combo boxes and sliders that don't have keyboard focus.""" + def eventFilter(self, obj, event): + if event.type() == QEvent.Type.Wheel: + if isinstance(obj, (QAbstractSpinBox, QComboBox)): + if not obj.hasFocus(): + event.ignore() + return True + return super().eventFilter(obj, event) + from devices.analog_input import AnalogInputDevice from devices.digital_io import DigitalIODevice from devices.serial_device import SerialDevice @@ -52,6 +65,9 @@ class MainWindow(QMainWindow): self._win_plot = None self._win_settings = None + self._wheel_blocker = _WheelBlocker(self) + QApplication.instance().installEventFilter(self._wheel_blocker) + self._init_demo_devices() self._build_ui() self._connect_signals() @@ -345,6 +361,7 @@ class MainWindow(QMainWindow): def _clear_history(self): self.engine.clear_history() + self.processor.clear_history() self._chart.refresh() self._status.setText("History cleared.") diff --git a/ui/style_dark.qss b/ui/style_dark.qss index df48330..2c24d1c 100644 --- a/ui/style_dark.qss +++ b/ui/style_dark.qss @@ -18,6 +18,18 @@ QMainWindow, QDialog { background-color: #0b0e13; } +QWidget { + color: #e2e8f0; +} + +/* Containers inside scroll areas inherit dark bg */ +QAbstractScrollArea > QWidget { + background-color: #111620; +} +QAbstractScrollArea > QWidget > QWidget { + background-color: #111620; +} + /* ── Toolbar ─────────────────────────────────────────────────────── */ QToolBar#mainToolbar { background-color: #0b0e13; @@ -388,7 +400,6 @@ QPushButton#applyButton:hover { background-color: #1d4ed8; color: #eff6ff; } /* ── Control widgets ─────────────────────────────────────────────── */ QFrame#controlWidget { background-color: #161d2e; border: 1px solid #2a3558; border-radius: 6px; } -QFrame#controlWidget:hover { border-color: #3b82f6; } QWidget#controlWidgetHeader { background-color: #0f1521; border-radius: 5px 5px 0 0; border-bottom: 1px solid #2a3558; } QLabel#controlWidgetIcon { font-size: 14px; color: #3b82f6; padding-right: 4px; } QLabel#controlWidgetTitle { font-family: "IBM Plex Mono", monospace; font-size: 11px; font-weight: 700; letter-spacing: 1.5px; color: #8b9dc3; } @@ -465,6 +476,65 @@ QLabel#traceSource { font-size: 12px; } +/* ── File dialog ─────────────────────────────────────────────────── */ +QFileDialog { + background-color: #0b0e13; +} +QFileDialog QWidget { + background-color: #0b0e13; + color: #e2e8f0; +} +QFileDialog QTreeView, +QFileDialog QListView { + background-color: #111620; + border: 1px solid #2a3558; + color: #e2e8f0; + alternate-background-color: #161d2e; +} +QFileDialog QTreeView::item:selected, +QFileDialog QListView::item:selected { + background-color: #1e3a5f; + color: #e0f2fe; +} +QFileDialog QTreeView::item:hover, +QFileDialog QListView::item:hover { + background-color: #1c2540; +} +QFileDialog QHeaderView::section { + background-color: #161d2e; + color: #8b9dc3; + border: none; + border-bottom: 1px solid #2a3558; + padding: 4px 8px; +} +QFileDialog QSplitter { + background-color: #0b0e13; +} +QFileDialog QSplitter::handle { + background-color: #2a3558; +} +QFileDialog QToolBar { + background-color: #0b0e13; + border-bottom: 1px solid #2a3558; +} +QFileDialog QToolButton { + background-color: transparent; + color: #8b9dc3; + border: 1px solid transparent; + border-radius: 3px; + padding: 2px; +} +QFileDialog QToolButton:hover { + background-color: #1c2540; + border-color: #2a3558; + color: #e2e8f0; +} +QFileDialog QComboBox { + background-color: #0b0e13; + border: 1px solid #2a3558; + color: #e2e8f0; +} + /* ── Profile / File menu ─────────────────────────────────────────── */ QMenu#profileMenu { background-color: #111620; diff --git a/ui/style_light.qss b/ui/style_light.qss index 8b5c8da..38b91f8 100644 --- a/ui/style_light.qss +++ b/ui/style_light.qss @@ -67,3 +67,60 @@ QListWidget#portList { QListWidget#portList::item { padding:5px 8px; border-bottom:1px solid #f1f5f9; } QListWidget#portList::item:hover { background:#f0f9ff; color:#1d4ed8; } QListWidget#portList::item:selected { background:#dbeafe; color:#1d4ed8; border-left:3px solid #3b82f6; } + +/* ── Control panel widgets ───────────────────────────────────────── */ +QFrame#controlWidget { background:#ffffff; border:1px solid #cbd5e1; border-radius:6px; } +QWidget#controlWidgetHeader { background:#f1f5f9; border-radius:5px 5px 0 0; border-bottom:1px solid #e2e8f0; } +QLabel#controlWidgetIcon { font-size:14px; color:#3b82f6; padding-right:4px; } +QLabel#controlWidgetTitle { font-family:"IBM Plex Mono",monospace; font-size:11px; font-weight:700; letter-spacing:1.5px; color:#64748b; } +QWidget#controlWidgetBody { background:#ffffff; border-radius:0 0 5px 5px; } +QFrame#controlWidgetWrapper { background:transparent; border:none; } + +/* Motor */ +QLabel#motorSpeedLbl { font-family:"IBM Plex Mono",monospace; font-size:28px; font-weight:700; color:#1d4ed8; } +QLabel#motorUnitLbl { font-family:"IBM Plex Mono",monospace; font-size:12px; color:#94a3b8; padding-top:12px; } +QLabel#motorPctLbl { font-family:"IBM Plex Mono",monospace; font-size:12px; color:#94a3b8; } +QSlider#motorSlider::groove:horizontal, QSlider#pwmSlider::groove:horizontal { background:#e2e8f0; height:6px; border-radius:3px; } +QSlider#motorSlider::handle:horizontal, QSlider#pwmSlider::handle:horizontal { background:#3b82f6; border:2px solid #1d4ed8; width:16px; height:16px; margin:-5px 0; border-radius:8px; } +QSlider#motorSlider::sub-page:horizontal { background:#3b82f6; border-radius:3px; } +QSlider#pwmSlider::sub-page:horizontal { background:#7c3aed; border-radius:3px; } +QPushButton#motorDirBtn { background:#f1f5f9; color:#64748b; border:1px solid #cbd5e1; border-radius:4px; padding:5px 10px; font-family:"IBM Plex Mono",monospace; font-size:12px; } +QPushButton#motorDirBtnRev { background:#ede9fe; color:#6d28d9; border:1px solid #7c3aed; border-radius:4px; padding:5px 10px; font-family:"IBM Plex Mono",monospace; font-size:12px; } +QPushButton#motorRunBtnOff { background:#f1f5f9; color:#94a3b8; border:1px solid #cbd5e1; border-radius:4px; padding:5px 12px; font-family:"IBM Plex Mono",monospace; font-weight:700; } +QPushButton#motorRunBtnOn { background:#dcfce7; color:#166534; border:1px solid #22c55e; border-radius:4px; padding:5px 12px; font-family:"IBM Plex Mono",monospace; font-weight:700; } + +/* Switch */ +QPushButton#switchBtnOff, QPushButton#switchBtn { background:#f1f5f9; color:#94a3b8; border:2px solid #cbd5e1; border-radius:6px; font-family:"IBM Plex Mono",monospace; font-size:18px; font-weight:800; letter-spacing:4px; min-height:52px; } +QPushButton#switchBtnOn { background:#dcfce7; color:#166534; border:2px solid #22c55e; border-radius:6px; font-family:"IBM Plex Mono",monospace; font-size:18px; font-weight:800; letter-spacing:4px; min-height:52px; } +QLabel#switchIndicatorOff { font-size:18px; color:#cbd5e1; } +QLabel#switchIndicatorOn { font-size:18px; color:#22c55e; } +QLabel#switchStateLbl { font-family:"IBM Plex Mono",monospace; font-size:10px; font-weight:700; letter-spacing:1px; color:#94a3b8; } + +/* Setpoint */ +QLabel#spLabel { font-family:"IBM Plex Mono",monospace; font-size:11px; font-weight:700; color:#3b82f6; letter-spacing:0.5px; } +QLabel#pvLabel { font-family:"IBM Plex Mono",monospace; font-size:11px; font-weight:700; color:#16a34a; letter-spacing:0.5px; } +QLabel#errLabel { font-family:"IBM Plex Mono",monospace; font-size:11px; font-weight:700; color:#d97706; letter-spacing:0.5px; } +QLabel#pvValue, QLabel#errValue { font-family:"IBM Plex Mono",monospace; font-size:12px; color:#64748b; } +QDoubleSpinBox#spSpinbox { font-family:"IBM Plex Mono",monospace; font-size:13px; font-weight:600; color:#1e293b; background:#ffffff; border:1px solid #3b82f6; border-radius:3px; padding:3px 6px; } +QPushButton#spStepBtn { background:#dbeafe; color:#1d4ed8; border:1px solid #3b82f6; border-radius:3px; font-size:16px; font-weight:700; padding:0; } +QPushButton#spStepBtn:hover { background:#bfdbfe; } + +/* PWM */ +QLabel#pwmLabel { font-family:"IBM Plex Mono",monospace; font-size:11px; color:#64748b; } +QLabel#pwmValue { font-family:"IBM Plex Mono",monospace; font-size:13px; font-weight:700; color:#6d28d9; } +QSpinBox#pwmFreqSpin { font-family:"IBM Plex Mono",monospace; font-size:12px; background:#ffffff; border:1px solid #cbd5e1; border-radius:3px; color:#1e293b; padding:3px 6px; } +QPushButton#digitalOutBtn { background:#f1f5f9; color:#94a3b8; border:1px solid #cbd5e1; border-radius:4px; padding:4px 14px; font-family:"IBM Plex Mono",monospace; min-width:50px; } +QPushButton#digitalOutBtn:checked { background:#dcfce7; border-color:#22c55e; color:#166534; } + +/* Misc window controls */ +QWidget#controlPanelHeader { background:#f1f5f9; border-bottom:1px solid #e2e8f0; } +QPushButton#devicesSmallBtn { background:transparent; color:#94a3b8; border:1px solid #e2e8f0; border-radius:3px; font-size:13px; padding:0; } +QPushButton#devicesSmallBtn:hover { background:#f0f9ff; color:#3b82f6; border-color:#3b82f6; } +QPushButton#plotCfgButton { background:#f0f9ff; color:#1d4ed8; border:1px solid #93c5fd; border-radius:4px; padding:5px 14px; font-weight:600; } +QPushButton#plotCfgButton:hover { background:#dbeafe; border-color:#3b82f6; } +QPushButton#plotCfgButton:checked { background:#dbeafe; border-color:#3b82f6; } +QComboBox#channelPickerCb { font-family:"IBM Plex Mono",monospace; font-size:12px; background:#ffffff; border:1px solid #cbd5e1; border-radius:3px; color:#1e293b; padding:4px 8px; } +QMenu#profileMenu { background:#ffffff; border:1px solid #e2e8f0; border-radius:4px; padding:4px 0; color:#1e293b; } +QMenu#profileMenu::item { padding:7px 20px; color:#334155; } +QMenu#profileMenu::item:selected { background:#dbeafe; color:#1d4ed8; } +QMenu#profileMenu::separator { height:1px; background:#e2e8f0; margin:3px 8px; } -- cgit v1.2.3