Serial monitor redesign: Test box button, InfluxDB row in the checklist
- Test box: restart + start-up capture + online InfluxDB check (data newer than the restart), with a status strip; checklist and findings are the main view, live log / guided troubleshooting / box details are tabs, rarely used actions in a More menu - light palette on all systems (macOS dark mode made the colours unreadable) - new rule influx.invalid_value: -inf from a silent microphone makes InfluxDB reject every upload (HTTP 400); guide end names it - exclusive serial port on macOS/Linux: two tool windows no longer split the log between them - InfluxDB check: `since` filter, neutral wording when nothing new arrives Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
66cb804102
commit
bb933ff748
13 files changed
+422
-83
No files matched your search
+55
-19
@@ -1,9 +1,11 @@
|
||||
"""Diagnostics side panel: renders a diag.engine.Report.
|
||||
|
||||
Layout: verdict banner, expected LED (colour + pattern + meaning when the
|
||||
box reports it), firmware, the box's own self-report (KOTA lines), start-up
|
||||
milestones, a findings tree (root causes; their consequences as children;
|
||||
titles only) and a wrapped detail pane for the selected finding.
|
||||
Layout: verdict banner, the test checklist (start-up milestones plus the
|
||||
online InfluxDB check), a findings tree (root causes; their consequences as
|
||||
children; titles only) and a wrapped detail pane for the selected finding.
|
||||
`details` is a separate widget for the host to place: expected LED (colour +
|
||||
pattern + meaning when the box reports it), firmware and the box's own
|
||||
self-report (KOTA lines).
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
@@ -17,6 +19,7 @@ from PySide6.QtWidgets import (
|
||||
QListWidget,
|
||||
QListWidgetItem,
|
||||
QPushButton,
|
||||
QScrollArea,
|
||||
QSizePolicy,
|
||||
QSplitter,
|
||||
QTextBrowser,
|
||||
@@ -43,13 +46,19 @@ BANNER = {
|
||||
}
|
||||
_KEY = Qt.ItemDataRole.UserRole
|
||||
BOX_STATE = {"ok": ("#1b7a33", "✔"), "warn": ("#c77c02", "!"), "bad": ("#b03a2e", "✖"), "info": ("#2e6da4", "i")}
|
||||
# Extra checklist rows (e.g. the InfluxDB check): state -> (mark, colour)
|
||||
ROW_STATE = {"ok": ("✔", "#1b7a33"), "bad": ("✖", "#b03a2e"), "warn": ("!", "#a86400"),
|
||||
"running": ("…", "#2e6da4"), "pending": ("○", "#6b6b6b"), "off": ("–", "#6b6b6b"),
|
||||
"note": ("", "#444444")}
|
||||
|
||||
|
||||
def _heading(text: str) -> QLabel:
|
||||
lbl = QLabel(text)
|
||||
f = lbl.font()
|
||||
f.setBold(True)
|
||||
f.setPointSize(f.pointSize() + 1)
|
||||
lbl.setFont(f)
|
||||
lbl.setContentsMargins(0, 6, 0, 2)
|
||||
return lbl
|
||||
|
||||
|
||||
@@ -87,7 +96,14 @@ class DiagnosticsPanel(QWidget):
|
||||
self.milestones = QListWidget()
|
||||
self.milestones.setFrameShape(QFrame.Shape.NoFrame)
|
||||
self.milestones.setSelectionMode(QListWidget.SelectionMode.NoSelection)
|
||||
self.milestones.setFocusPolicy(Qt.FocusPolicy.NoFocus)
|
||||
self.milestones.setWordWrap(True)
|
||||
self.milestones.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff)
|
||||
self.milestones.setSizePolicy(QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Maximum)
|
||||
self.milestones.setStyleSheet("QListWidget { background: transparent; } QListWidget::item { padding: 2px; }")
|
||||
mf = self.milestones.font()
|
||||
mf.setPointSize(mf.pointSize() + 1)
|
||||
self.milestones.setFont(mf)
|
||||
|
||||
self.tree = QTreeWidget()
|
||||
self.tree.setHeaderHidden(True)
|
||||
@@ -101,22 +117,31 @@ class DiagnosticsPanel(QWidget):
|
||||
findings.setSizes([220, 260])
|
||||
|
||||
lay = QVBoxLayout(self)
|
||||
lay.setContentsMargins(4, 0, 0, 0)
|
||||
lay.setContentsMargins(4, 0, 4, 0)
|
||||
lay.addWidget(self.verdict)
|
||||
lay.addWidget(_heading("Status LED should be"))
|
||||
lay.addLayout(led_row)
|
||||
lay.addWidget(_heading("Firmware"))
|
||||
lay.addWidget(self.firmware)
|
||||
self.qr_btn = QPushButton("Matter pairing QR code...")
|
||||
self.qr_btn.setToolTip("Show the box's Matter QR code on screen to scan with the phone")
|
||||
lay.addWidget(self.qr_btn, 0, Qt.AlignmentFlag.AlignLeft)
|
||||
lay.addWidget(self.box_title)
|
||||
lay.addWidget(self.box)
|
||||
lay.addWidget(_heading("Start-up progress (current boot)"))
|
||||
lay.addWidget(_heading("Test checklist (current boot)"))
|
||||
lay.addWidget(self.milestones)
|
||||
lay.addWidget(_heading("Findings - root causes first; click one for details"))
|
||||
lay.addWidget(findings, 1)
|
||||
|
||||
# Box details: placed by the host (a separate tab).
|
||||
inner = QWidget()
|
||||
dl = QVBoxLayout(inner)
|
||||
dl.addWidget(_heading("Status LED should be"))
|
||||
dl.addLayout(led_row)
|
||||
dl.addWidget(_heading("Firmware"))
|
||||
dl.addWidget(self.firmware)
|
||||
self.qr_btn = QPushButton("Matter pairing QR code...")
|
||||
self.qr_btn.setToolTip("Show the box's Matter QR code on screen to scan with the phone")
|
||||
dl.addWidget(self.qr_btn, 0, Qt.AlignmentFlag.AlignLeft)
|
||||
dl.addWidget(self.box_title)
|
||||
dl.addWidget(self.box)
|
||||
dl.addStretch(1)
|
||||
self.details = QScrollArea()
|
||||
self.details.setWidgetResizable(True)
|
||||
self.details.setFrameShape(QFrame.Shape.NoFrame)
|
||||
self.details.setWidget(inner)
|
||||
|
||||
self._findings: dict[str, Finding] = {}
|
||||
self._selected: str | None = None
|
||||
# key -> expanded, chosen by the user; survives the rebuild on refresh.
|
||||
@@ -126,7 +151,8 @@ class DiagnosticsPanel(QWidget):
|
||||
self.tree.itemExpanded.connect(lambda it: self._remember(it, True))
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
def show_report(self, r: Report) -> None:
|
||||
def show_report(self, r: Report, extra_rows: list[tuple[str, str]] = ()) -> None:
|
||||
"""extra_rows: (state, text) appended to the checklist, state in ROW_STATE."""
|
||||
bg, fg = BANNER.get(r.verdict_severity, BANNER[Severity.WARNING])
|
||||
if r.lines == 0:
|
||||
bg, fg = "#e6e6e6", "#333333"
|
||||
@@ -161,10 +187,20 @@ class DiagnosticsPanel(QWidget):
|
||||
done = t is not None
|
||||
it = QListWidgetItem(("✔ " if done else "○ ") + label
|
||||
+ (f" ({t / 1000:.1f} s)" if done else ""))
|
||||
it.setForeground(QBrush(QColor("#1b7a33" if done else "#8a8a8a")))
|
||||
it.setForeground(QBrush(QColor("#1b7a33" if done else "#6b6b6b")))
|
||||
self.milestones.addItem(it)
|
||||
rows = min(max(self.milestones.count(), 1), 15)
|
||||
self.milestones.setFixedHeight(rows * max(self.milestones.sizeHintForRow(0), 16) + 6)
|
||||
for state, text in extra_rows:
|
||||
mark, colour = ROW_STATE[state]
|
||||
it = QListWidgetItem(f"{mark} {text}" if mark else text)
|
||||
it.setForeground(QBrush(QColor(colour)))
|
||||
if state != "note":
|
||||
f = it.font()
|
||||
f.setBold(True)
|
||||
it.setFont(f)
|
||||
it.setToolTip(text)
|
||||
self.milestones.addItem(it)
|
||||
height = sum(max(self.milestones.sizeHintForRow(i), 16) for i in range(self.milestones.count()))
|
||||
self.milestones.setFixedHeight(min(max(height, 16), 560) + 6)
|
||||
|
||||
self._fill_tree(r)
|
||||
|
||||
|
||||
Reference in new issue
Block a user