66 lines
2.4 KiB
Python
66 lines
2.4 KiB
Python
# app/gui/dashboard.py
|
|
from __future__ import annotations
|
|
import tkinter as tk
|
|
from tkinter import ttk
|
|
|
|
class DashboardView:
|
|
def __init__(self, parent, sim, refresh_ms: int = 250):
|
|
self.sim = sim
|
|
self.frame = ttk.LabelFrame(parent, text="Dashboard", padding=6)
|
|
self.refresh_ms = refresh_ms
|
|
|
|
cols = ("label", "value", "unit", "key", "module")
|
|
self.tree = ttk.Treeview(self.frame, columns=cols, show="headings")
|
|
for c, w in zip(cols, (160, 80, 40, 160, 80)):
|
|
self.tree.heading(c, text=c.capitalize())
|
|
self.tree.column(c, width=w, anchor="w")
|
|
self.tree.grid(row=0, column=0, sticky="nsew")
|
|
sb_y = ttk.Scrollbar(self.frame, orient="vertical", command=self.tree.yview)
|
|
self.tree.configure(yscrollcommand=sb_y.set); sb_y.grid(row=0, column=1, sticky="ns")
|
|
|
|
self.frame.columnconfigure(0, weight=1)
|
|
self.frame.rowconfigure(0, weight=1)
|
|
|
|
self._rows = {}
|
|
self._tick()
|
|
|
|
def _tick(self):
|
|
snap = self.sim.v.dashboard_snapshot()
|
|
specs = snap.get("specs", {})
|
|
values = snap.get("values", {})
|
|
# sort by priority then label
|
|
ordered = sorted(specs.values(), key=lambda s: (s.get("priority", 100), s.get("label", s["key"])))
|
|
|
|
seen_keys = set()
|
|
for spec in ordered:
|
|
k = spec["key"]; seen_keys.add(k)
|
|
label = spec.get("label", k)
|
|
unit = spec.get("unit", "") or ""
|
|
fmt = spec.get("fmt")
|
|
src = spec.get("source", "")
|
|
val = values.get(k, "")
|
|
if fmt and isinstance(val, (int, float)):
|
|
try:
|
|
val = format(val, fmt)
|
|
except Exception:
|
|
pass
|
|
row_id = self._rows.get(k)
|
|
row_vals = (label, val, unit, k, src)
|
|
if row_id is None:
|
|
row_id = self.tree.insert("", "end", values=row_vals)
|
|
self._rows[k] = row_id
|
|
else:
|
|
self.tree.item(row_id, values=row_vals)
|
|
|
|
# delete rows that disappeared
|
|
for k, rid in list(self._rows.items()):
|
|
if k not in seen_keys:
|
|
try: self.tree.delete(rid)
|
|
except Exception: pass
|
|
self._rows.pop(k, None)
|
|
|
|
try:
|
|
self.frame.after(self.refresh_ms, self._tick)
|
|
except tk.TclError:
|
|
pass
|