summaryrefslogtreecommitdiff
path: root/ui/windows/config_window.py
diff options
context:
space:
mode:
Diffstat (limited to 'ui/windows/config_window.py')
-rw-r--r--ui/windows/config_window.py69
1 files changed, 58 insertions, 11 deletions
diff --git a/ui/windows/config_window.py b/ui/windows/config_window.py
index 604b6ed..62789f1 100644
--- a/ui/windows/config_window.py
+++ b/ui/windows/config_window.py
@@ -28,9 +28,9 @@ from PyQt6.QtGui import QCloseEvent, QColor, QShowEvent
from devices.device_registry import DeviceRegistry
from core.acquisition import AcquisitionEngine
-from core.signal_processor import SignalProcessor, DerivedChannel
+from core.signal_processor import SignalProcessor, Channel
-from ui.windows.devices_window import DeviceRow, ChannelsTab
+from ui.windows.devices_window import DeviceRow, SignalsTab
from ui.windows.channels_window import PipelineTab, _slug_id, _dedupe_name
from ui.windows.plot_window import (
PlotWindow, LayoutConfig, LayoutCanvas, PaneBlock,
@@ -43,8 +43,8 @@ from ui.namespace_panel import NamespacePanel
# Enhanced signals table — adds Dir badge and pipeline ⚙ button
# ══════════════════════════════════════════════════════════════════════════════
-class _SignalsTab(ChannelsTab):
- """ChannelsTab with a Dir (READ/WRITE) badge. Operations (filters/calibration/math)
+class _SignalsTab(SignalsTab):
+ """SignalsTab with a Dir (READ/WRITE) badge. Operations (filters/calibration/math)
are configured per Channel in the Channels pane, not here."""
_C_DEVICE = 0
@@ -58,6 +58,7 @@ class _SignalsTab(ChannelsTab):
_C_MKCH = 8 # "+ Channel" button
channel_created = pyqtSignal()
+ channel_renamed = pyqtSignal(str, str, str, str) # old_dev, old_id, new_dev, new_id
def __init__(self, registry: DeviceRegistry, processor: SignalProcessor):
self._processor = processor
@@ -91,7 +92,7 @@ class _SignalsTab(ChannelsTab):
self._table.setRowCount(0)
row = 0
for dev in self.registry.all_instances():
- for ch in dev.info.channels:
+ for ch in dev.info.signals:
self._table.insertRow(row)
dev_item = QTableWidgetItem(f"{dev.info.icon} {dev.info.device_id}")
@@ -99,8 +100,8 @@ class _SignalsTab(ChannelsTab):
dev_item.setForeground(QColor("#64748b"))
self._table.setItem(row, self._C_DEVICE, dev_item)
- ch_item = QTableWidgetItem(ch.channel_id)
- ch_item.setFlags(ch_item.flags() & ~Qt.ItemFlag.ItemIsEditable)
+ ch_item = QTableWidgetItem(ch.signal_id)
+ ch_item.setData(Qt.ItemDataRole.UserRole, (dev.info.device_id, ch.signal_id))
ch_item.setForeground(QColor(ch.color))
self._table.setItem(row, self._C_CH_ID, ch_item)
@@ -122,7 +123,7 @@ class _SignalsTab(ChannelsTab):
chk.setEnabled(False)
else:
dev_id_cap = dev.info.device_id
- ch_id_cap = ch.channel_id
+ ch_id_cap = ch.signal_id
chk.toggled.connect(
lambda checked, d=dev_id_cap, c=ch_id_cap:
self._on_enabled(d, c, checked)
@@ -131,7 +132,7 @@ class _SignalsTab(ChannelsTab):
self._table.setCellWidget(row, self._C_ENABLED, chk_w)
name_item = QTableWidgetItem(ch.name)
- name_item.setData(Qt.ItemDataRole.UserRole, (dev.info.device_id, ch.channel_id))
+ name_item.setData(Qt.ItemDataRole.UserRole, (dev.info.device_id, ch.signal_id))
self._table.setItem(row, self._C_NAME, name_item)
self._table.setItem(row, self._C_UNIT, QTableWidgetItem(ch.unit))
@@ -148,7 +149,7 @@ class _SignalsTab(ChannelsTab):
mkch_btn.setFixedSize(24, 22)
mkch_btn.setToolTip("Create a Channel from this Signal")
d_cap, c_cap, n_cap, u_cap, col_cap = (
- dev.info.device_id, ch.channel_id, ch.name, ch.unit, ch.color
+ dev.info.device_id, ch.signal_id, ch.name, ch.unit, ch.color
)
mkch_btn.clicked.connect(
lambda _=False, d=d_cap, c=c_cap, n=n_cap, u=u_cap, col=col_cap:
@@ -161,6 +162,49 @@ class _SignalsTab(ChannelsTab):
self._table.blockSignals(False)
+ def _on_cell_changed(self, row: int, col: int):
+ if col == self._C_CH_ID:
+ self._on_id_edited(row)
+ return
+ super()._on_cell_changed(row, col)
+
+ def _on_id_edited(self, row: int):
+ """Signal IDs are stable/backend-facing by default (see
+ devices/base_device.py::SignalConfig) but user-editable here, same
+ as a derived Channel's ID — renaming propagates through
+ reference_registry.propagate_rename() (wired via channel_renamed)
+ so plot traces / controls / pipelines pointing at the old id follow
+ it instead of going dangling (the bug that motivated this feature:
+ an Arduino analog pin remap used to change the id outright)."""
+ id_item = self._table.item(row, self._C_CH_ID)
+ if id_item is None:
+ return
+ ref = id_item.data(Qt.ItemDataRole.UserRole)
+ if ref is None:
+ return
+ dev_id, old_id = ref
+ new_id = id_item.text().strip()
+ if not new_id or new_id == old_id:
+ self._table.blockSignals(True)
+ id_item.setText(old_id)
+ self._table.blockSignals(False)
+ return
+ dev = self.registry.get_instance(dev_id)
+ if not dev or any(s.signal_id == new_id for s in dev.info.signals if s.signal_id != old_id):
+ self._table.blockSignals(True)
+ id_item.setText(old_id)
+ self._table.blockSignals(False)
+ return
+ ch = dev.get_signal(old_id)
+ if not ch:
+ return
+ ch.signal_id = new_id
+ id_item.setData(Qt.ItemDataRole.UserRole, (dev_id, new_id))
+ name_item = self._table.item(row, self._C_NAME)
+ if name_item is not None:
+ name_item.setData(Qt.ItemDataRole.UserRole, (dev_id, new_id))
+ self.channel_renamed.emit(dev_id, old_id, dev_id, new_id)
+
def _on_create_channel(self, dev_id: str, ch_id: str, ch_name: str, unit: str, color: str):
"""Create a passthrough Channel (kind="none") from this Signal,
named after it — the "1 Signal -> 1 Channel" quick path. No-ops if
@@ -173,7 +217,7 @@ class _SignalsTab(ChannelsTab):
existing_ids = {d.channel_id for d in existing}
name = _dedupe_name(ch_name, existing_names)
channel_id = _slug_id(name, existing_ids)
- dc = DerivedChannel(channel_id=channel_id, name=name, kind="none",
+ dc = Channel(channel_id=channel_id, name=name, kind="none",
sources=[(dev_id, ch_id)], source_names=[ch_name],
unit=unit, color=color or "#00d4ff")
err = self._processor.add_derived(dc)
@@ -275,6 +319,7 @@ class ConfigWindow(QWidget):
channel_unit_changed = pyqtSignal(str, str, str)
pipeline_changed = pyqtSignal()
derived_changed = pyqtSignal()
+ channel_renamed = pyqtSignal(str, str, str, str) # old_dev, old_id, new_dev, new_id
layout_applied = pyqtSignal(object)
closed = pyqtSignal()
@@ -360,6 +405,7 @@ class ConfigWindow(QWidget):
self._signals_tab.channel_visibility_changed.connect(self._on_sig_visibility)
self._signals_tab.channel_name_changed.connect(self._on_sig_name)
self._signals_tab.channel_unit_changed.connect(self._on_sig_unit)
+ self._signals_tab.channel_renamed.connect(self.channel_renamed)
sig_l.addWidget(self._signals_tab, 1)
splitter.addWidget(sig_w)
@@ -468,6 +514,7 @@ class ConfigWindow(QWidget):
self._pipeline_tab = PipelineTab(self.registry, self.processor)
self._pipeline_tab.changed.connect(self.pipeline_changed)
self._pipeline_tab.derived_changed.connect(self.derived_changed)
+ self._pipeline_tab.channel_renamed.connect(self.channel_renamed)
self._pipeline_tab.refresh_derived() # load derived channels already in processor
# (e.g. window built fresh after profile load)
inner_splitters = self._pipeline_tab.findChildren(QSplitter)