gui_client: highlight changed rows on each new record

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-29 21:15:59 +02:00
co-authored by Claude Sonnet 4.6
parent 74379e55d6
commit 73f6b85c05
+38 -3
View File
@@ -180,6 +180,23 @@ def _is_phys(v) -> bool:
return isinstance(v, dict) and "value" in v and "unit" in v
_CHANGED_BG = QColor("#1e3a00")
def _flatten_values(data: dict, prefix: str = "") -> dict[str, object]:
"""Flat dict of path → value for every physical leaf, using '/' separator."""
result = {}
for k, v in data.items():
if k == "ts":
continue
path = f"{prefix}/{k}" if prefix else k
if _is_phys(v):
result[path] = v.get("value")
elif isinstance(v, dict):
result.update(_flatten_values(v, path))
return result
def _extract_phys(data: dict, prefix: str = "") -> dict[str, dict]:
"""Return a flat dict of path → {value, unit} for every physical leaf."""
result = {}
@@ -302,10 +319,15 @@ class DashboardTab(QWidget):
self._tree.setAlternatingRowColors(True)
self._tree.setRootIsDecorated(True)
layout.addWidget(self._tree)
self._prev_flat: dict = {}
def update(self, snapshot: dict):
self._ts.setText(f"Last update: {snapshot.get('ts', '')}")
curr_flat = _flatten_values(snapshot)
changed = {p for p, v in curr_flat.items() if v != self._prev_flat.get(p)}
self._prev_flat = curr_flat
expanded = self._expanded_paths()
self._tree.clear()
@@ -320,38 +342,51 @@ class DashboardTab(QWidget):
d_item.setFont(0, bold)
d_item.setForeground(0, domain_color)
for obj_name, fields in domain_data.items():
path = f"{domain}/{obj_name}"
if _is_phys(fields):
item = QTreeWidgetItem([obj_name, str(fields["value"]), fields["unit"], fields.get("str_state", "")])
item.setTextAlignment(1, Qt.AlignRight | Qt.AlignVCenter)
if path in changed:
for col in range(4):
item.setBackground(col, _CHANGED_BG)
d_item.addChild(item)
elif isinstance(fields, dict):
obj_item = QTreeWidgetItem([obj_name])
obj_item.setFont(0, bold)
self._add_fields(obj_item, fields, f"{domain}/{obj_name}")
self._add_fields(obj_item, fields, path, changed)
d_item.addChild(obj_item)
else:
item = QTreeWidgetItem([obj_name, str(fields), "", ""])
item.setTextAlignment(1, Qt.AlignRight | Qt.AlignVCenter)
if path in changed:
for col in range(4):
item.setBackground(col, _CHANGED_BG)
d_item.addChild(item)
self._tree.addTopLevelItem(d_item)
self._tree.expandAll()
self._restore_expanded(expanded)
def _add_fields(self, parent: QTreeWidgetItem, data: dict, path: str):
def _add_fields(self, parent: QTreeWidgetItem, data: dict, path: str, changed: set):
for k, v in data.items():
child_path = f"{path}/{k}"
if _is_phys(v):
item = QTreeWidgetItem([k, str(v["value"]), v["unit"], v.get("str_state", "")])
item.setTextAlignment(1, Qt.AlignRight | Qt.AlignVCenter)
if child_path in changed:
for col in range(4):
item.setBackground(col, _CHANGED_BG)
parent.addChild(item)
elif isinstance(v, dict):
node = QTreeWidgetItem([k])
self._add_fields(node, v, child_path)
self._add_fields(node, v, child_path, changed)
parent.addChild(node)
else:
item = QTreeWidgetItem([k, str(v), "", ""])
item.setTextAlignment(1, Qt.AlignRight | Qt.AlignVCenter)
if child_path in changed:
for col in range(4):
item.setBackground(col, _CHANGED_BG)
parent.addChild(item)
# ── preserve expand state across refreshes ──────────────────────────────