From 34c12b2b5b280206890cd03153b1c95116acc85e Mon Sep 17 00:00:00 2001 From: Christian Kolset Date: Thu, 13 Aug 2026 13:39:25 -0600 Subject: Channels tab drag-to-reorder, Namespace absolute references, fix stale Channel ID renames Generalized the Plot pane drag-to-reorder (_PaneDragHandle/_PlotDropContainer -> _DragHandle/_DragReorderContainer) and reused it for the Channels tab's derived-channel list; reordering also updates SignalProcessor's evaluation order (list order matters for chained Channels) via new reorder_derived(). Namespace panel: added an ID column (bare channel_id, matching the actual channels.get(...) script convention) and fixed the Source column for Channels to show resolved absolute references (dev_id/ch_id for a Signal, bare id for a chained Channel) instead of the operation kind, so a chain is traceable by eye. Fixed a real bug this surfaced: renaming a Channel's ID never propagated to other Channels' sources, leaving dangling ("derived", old_id) references that silently stopped updating. Added SignalProcessor.rename_derived() to fix up sources plus carry the ring buffer/latest-value cache to the new key, wired to DerivedBlock's Apply. Co-Authored-By: Claude Sonnet 5 --- ui/windows/plot_window.py | 40 +++++++++++++++++++++++----------------- 1 file changed, 23 insertions(+), 17 deletions(-) (limited to 'ui/windows/plot_window.py') diff --git a/ui/windows/plot_window.py b/ui/windows/plot_window.py index 93ce776..e810365 100644 --- a/ui/windows/plot_window.py +++ b/ui/windows/plot_window.py @@ -377,42 +377,45 @@ class TraceChannelPickerDialog(QDialog): # ══════════════════════════════════════════════════════════════════════════════ # Drag-to-reorder — same mechanics as ControlPanel (ui/control_panel.py): # a small drag handle starts a QDrag carrying the source index, the config -# list container accepts the drop and reorders. The pane title here is an -# editable QLineEdit (unlike ControlPanel's static label), so the whole -# header can't be made transparent-for-mouse — a dedicated grip is used -# instead of the whole header. +# list container accepts the drop and reorders. Reused by PipelineTab +# (ui/windows/channels_window.py) for the Channels list — owner just needs +# to implement _index_of / _drop_index / _indicator_y_for / _reorder. +# A dedicated grip is used instead of making the whole header a drag +# source (unlike ControlPanel) because these headers hold an editable +# QLineEdit title, which needs real mouse focus to edit. # ══════════════════════════════════════════════════════════════════════════════ -class _PaneDragHandle(QLabel): +class _DragHandle(QLabel): def __init__(self): super().__init__("⠿") self._owner = None - self._block = None + self._item = None self.setObjectName("paneDragHandle") self.setFixedWidth(18) self.setAlignment(Qt.AlignmentFlag.AlignCenter) self.setCursor(Qt.CursorShape.OpenHandCursor) self.setToolTip("Drag to reorder") - def set_drag_source(self, owner: "PlotWindow", block: "PaneBlock"): + def set_drag_source(self, owner, item): self._owner = owner - self._block = block + self._item = item def mousePressEvent(self, event): if event.button() == Qt.MouseButton.LeftButton and self._owner is not None: drag = QDrag(self) mime = QMimeData() - mime.setText(str(self._owner._blocks.index(self._block))) + mime.setText(str(self._owner._index_of(self._item))) drag.setMimeData(mime) drag.exec(Qt.DropAction.MoveAction) super().mousePressEvent(event) -class _PlotDropContainer(QWidget): - """Holds the pane config blocks; drops reorder them — same drag-to-reorder - mechanics as ControlPanel's _DropContainer.""" +class _DragReorderContainer(QWidget): + """Holds a vertical list of config blocks; drops reorder them — same + drag-to-reorder mechanics as ControlPanel's _DropContainer. Owner must + implement _drop_index(y), _indicator_y_for(idx), _reorder(src, dst).""" - def __init__(self, owner: "PlotWindow"): + def __init__(self, owner): super().__init__() self._owner = owner self._indicator_y = None @@ -438,7 +441,7 @@ class _PlotDropContainer(QWidget): src_idx = int(event.mimeData().text()) except ValueError: return - self._owner._reorder_panes(src_idx, self._owner._drop_index(event.position().y())) + self._owner._reorder(src_idx, self._owner._drop_index(event.position().y())) event.acceptProposedAction() def _show_indicator(self, y: float): @@ -474,7 +477,7 @@ class PaneBlock(QFrame): hdr = QWidget(); hdr.setObjectName("plotBlockHeader"); hdr.setFixedHeight(32) hl = QHBoxLayout(hdr); hl.setContentsMargins(8,0,6,0); hl.setSpacing(4) - self._drag_handle = _PaneDragHandle() + self._drag_handle = _DragHandle() hl.addWidget(self._drag_handle) self._title = QLineEdit(self.spec.title); self._title.setObjectName("plotBlockTitle") self._title.textChanged.connect(lambda t: ( @@ -1075,7 +1078,7 @@ class PlotWindow(QWidget): scroll = QScrollArea(); scroll.setWidgetResizable(True) scroll.setObjectName("deviceScroll") - self._cont = _PlotDropContainer(self) + self._cont = _DragReorderContainer(self) self._blay = QVBoxLayout(self._cont) self._blay.setContentsMargins(10,10,10,10); self._blay.setSpacing(12) self._blay.addStretch() @@ -1173,7 +1176,10 @@ class PlotWindow(QWidget): nxt = self._blay.itemAt(idx).widget() return (prev.y() + prev.height() + nxt.y()) // 2 - def _reorder_panes(self, src_idx: int, dst_idx: int): + def _index_of(self, block: "PaneBlock") -> int: + return self._blocks.index(block) + + def _reorder(self, src_idx: int, dst_idx: int): """Move the pane at src_idx to dst_idx (drop-target index, pre-removal) — same semantics as ControlPanel._reorder(). Also walks the BSP tree's leaf pane-index labels through the same chain of adjacent swaps so -- cgit v1.2.3