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 --- core/signal_processor.py | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) (limited to 'core/signal_processor.py') diff --git a/core/signal_processor.py b/core/signal_processor.py index 8abaff8..4781e91 100644 --- a/core/signal_processor.py +++ b/core/signal_processor.py @@ -437,6 +437,47 @@ class SignalProcessor(QObject): self._derived = [d for d in self._derived if d.channel_id != channel_id] self._derived_bufs.pop(channel_id, None) + def rename_derived(self, old_id: str, new_id: str): + """Propagate a Channel ID rename to every other derived channel's + sources — otherwise anything chained from it keeps a dangling + ("derived", old_id) reference (SignalProcessor._evaluate_derived + looks sources up by exact match) and silently stops updating. + Also carries the ring buffer and latest-value cache over under the + new key so the rename doesn't reset plotted history or blank the + Value column for a poll cycle. Does NOT touch source_names or + expression/script text — those are display labels / user-authored + code, out of scope for an automatic rewrite.""" + if old_id == new_id: + return + with self._lock: + for dc in self._derived: + if dc.channel_id == new_id: + continue + new_sources = [ + ("derived", new_id) if d == "derived" and c == old_id else (d, c) + for d, c in dc.sources + ] + if new_sources != dc.sources: + dc.sources = new_sources + + buf = self._derived_bufs.pop(old_id, None) + if buf is not None: + self._derived_bufs[new_id] = buf + + entry = self._latest.pop(("derived", old_id), None) + if entry is not None: + self._latest[("derived", new_id)] = entry + + def reorder_derived(self, src_idx: int, dst_idx: int): + """Move the derived channel at src_idx to dst_idx — list order is + evaluation order (see _evaluate_derived): a Channel sourced from + another Channel needs that source evaluated earlier in the same + pass, or it reads a stale value and lags by one sample cycle.""" + with self._lock: + if 0 <= src_idx < len(self._derived) and 0 <= dst_idx < len(self._derived): + dc = self._derived.pop(src_idx) + self._derived.insert(dst_idx, dc) + def get_derived(self) -> List[DerivedChannel]: return list(self._derived) -- cgit v1.2.3