summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Kolset <ckolset@colostate.edu>2026-08-13 10:59:25 -0600
committerChristian Kolset <ckolset@colostate.edu>2026-08-13 11:04:34 -0600
commit012a1093e3cb9a1b376fc3beee4df859ccfb8da5 (patch)
treeacc261cd944187961c65bafcdea84137f87d0ead
parentb7b3a84f37cc94494e17ea2098f72d6317090c53 (diff)
Fixed loading profiles data retention and display.
-rw-r--r--core/acquisition.py18
-rw-r--r--core/profile.py6
-rw-r--r--ui/main_window.py1
-rw-r--r--ui/windows/config_window.py6
-rw-r--r--ui/windows/plot_window.py29
5 files changed, 34 insertions, 26 deletions
diff --git a/core/acquisition.py b/core/acquisition.py
index 79bdb79..f54f265 100644
--- a/core/acquisition.py
+++ b/core/acquisition.py
@@ -112,6 +112,24 @@ class AcquisitionEngine(QObject):
for ch in dev.info.channels:
self._last_units[(dev.info.device_id, ch.channel_id)] = ch.unit
+ def on_device_reconfigured(self, device_id: str):
+ """Reconcile buffers with a device's current channel list — reconfigure()
+ can add/remove channels (e.g. Signals count changed), and buffers keyed
+ by the old channel list would silently drop data for anything new."""
+ dev = next((d for d in self._devices if d.info.device_id == device_id), None)
+ if dev is None:
+ return
+ bufs = self._buffers.setdefault(device_id, {})
+ valid_ids = {ch.channel_id for ch in dev.info.channels}
+ for stale_id in [cid for cid in bufs if cid not in valid_ids]:
+ bufs.pop(stale_id, None)
+ for ch in dev.info.channels:
+ if ch.channel_id not in bufs:
+ bufs[ch.channel_id] = ChannelBuffer()
+ self._last_units[(device_id, ch.channel_id)] = ch.unit
+ for key in [k for k in self._last_units if k[0] == device_id and k[1] not in valid_ids]:
+ self._last_units.pop(key, None)
+
def remove_device(self, device_id: str):
self._devices = [d for d in self._devices if d.info.device_id != device_id]
self._buffers.pop(device_id, None)
diff --git a/core/profile.py b/core/profile.py
index 8c73b4b..1fa667a 100644
--- a/core/profile.py
+++ b/core/profile.py
@@ -306,6 +306,12 @@ class ProfileManager:
if engine is not None:
engine.remove_device(dev.info.device_id)
+ # Clear signal pipelines and derived channels — profile defines the
+ # complete set; otherwise the previous profile's leftovers stick around.
+ processor._pipelines.clear()
+ for dc in list(processor.get_derived()):
+ processor.remove_derived(dc.channel_id)
+
for dev_cfg in profile.devices:
dev_type = dev_cfg.get("device_type")
dev_id = dev_cfg.get("device_id")
diff --git a/ui/main_window.py b/ui/main_window.py
index 19d4c69..012d6f5 100644
--- a/ui/main_window.py
+++ b/ui/main_window.py
@@ -731,6 +731,7 @@ class MainWindow(QMainWindow):
self._status.setText(f"Device '{dev_id}' removed.")
def _on_device_reconfigured(self, dev_id: str):
+ self.engine.on_device_reconfigured(dev_id)
self._chart.refresh()
if self._win_config:
self._win_config.on_device_reconfigured(dev_id)
diff --git a/ui/windows/config_window.py b/ui/windows/config_window.py
index 64c669d..52612fc 100644
--- a/ui/windows/config_window.py
+++ b/ui/windows/config_window.py
@@ -347,6 +347,8 @@ class ConfigWindow(QWidget):
row.name_changed.connect(self._on_device_renamed)
self._dev_inner.insertWidget(self._dev_inner.count() - 1, row)
self._dev_rows[dev.info.device_id] = row
+ if hasattr(self, "_signals_tab"):
+ self._signals_tab.refresh()
def _refresh_device_status(self):
for row in self._dev_rows.values():
@@ -375,7 +377,7 @@ class ConfigWindow(QWidget):
dev = self.registry.get_instance(device_id)
if dev:
DeviceConfigDialog(dev, self).exec()
- self._signals_tab.refresh()
+ self.refresh_devices() # rebuilds device rows — picks up new channel count
self._pipeline_tab.on_device_reconfigured(device_id)
self.device_reconfigured.emit(device_id)
@@ -429,6 +431,8 @@ 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.refresh_derived() # load derived channels already in processor
+ # (e.g. window built fresh after profile load)
inner_splitters = self._pipeline_tab.findChildren(QSplitter)
if inner_splitters:
inner_splitters[0].widget(0).setVisible(False)
diff --git a/ui/windows/plot_window.py b/ui/windows/plot_window.py
index 119efab..dc1617b 100644
--- a/ui/windows/plot_window.py
+++ b/ui/windows/plot_window.py
@@ -1092,33 +1092,12 @@ class PlotWindow(QWidget):
QMessageBox.critical(self, "Import failed", str(e))
def refresh_channels(self):
- """Sync virtual channel panes then rebuild UI."""
- existing = {
- tr.channel_id
- for p in self.cfg.panes
- for tr in p.traces if tr.device_id == "derived"
- }
+ """Drop panes whose sole trace is a deleted virtual channel, then rebuild UI.
+ Does NOT auto-add panes for new virtual channels — the operator adds those
+ explicitly via "+ Add Pane" / "+ Add Channel", so the plot layout only ever
+ shows what was deliberately built."""
current_ids = {dc.channel_id for dc in self.processor.get_derived()}
- # Add panes for new virtual channels
- for dc in self.processor.get_derived():
- if dc.channel_id not in existing:
- new_idx = len(self.cfg.panes)
- spec = PaneSpec(title=dc.name, y_label=dc.unit)
- spec.traces.append(TraceSpec(
- device_id="derived", channel_id=dc.channel_id,
- label=dc.name, color=dc.color,
- ))
- self.cfg.panes.append(spec)
- if self.cfg.tree is None:
- self.cfg.tree = {"kind": "leaf", "pane": 0}
- else:
- self.cfg.tree = _tree_equalize_ratios({
- "kind": "vsplit", "ratio": 0.5,
- "first": self.cfg.tree,
- "second": {"kind": "leaf", "pane": new_idx},
- })
-
# Remove panes whose sole trace is a deleted virtual channel
removed = [
i for i, p in enumerate(self.cfg.panes)