feat(map): add min/max highlighting toggle and live safeguards

- Add "Highlight Min/Max" option to ReadoutPanel showing peak rings.
- Reset to review mode and blank wafer map when switching away from map tab.
- Disable hover highlights and sensor dialog triggers during live stream.
- Lower trendPane height and add top margin headroom for tick labels.
- Add test coverage for blanking states, toggle overrides, and interactions.
This commit is contained in:
jack
2026-07-10 16:28:55 -07:00
parent 034f13b717
commit 25fa7507ce
9 changed files with 103 additions and 13 deletions
+3
View File
@@ -23,6 +23,9 @@ Rectangle {
if (selectedTabIndex === 2) {
file_browser.refreshFiles();
streamController.unloadFile()
} else if (streamController.mode === "live") {
// Leaving the map tab mid-stream: stop and blank the live map.
streamController.setMode("review")
}
}
+6 -3
View File
@@ -144,7 +144,7 @@ Item {
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
spacing: 8
spacing: 16
WaferMapView {
id: waferView
@@ -154,14 +154,15 @@ Item {
Layout.minimumHeight: 280
blend: readoutPanel.heatmapBlend
showLabels: readoutPanel.showLabels
showExtremes: readoutPanel.showExtremes
showThickness: readoutPanel.showThickness
}
Rectangle {
id: trendPane
Layout.fillWidth: true
Layout.fillHeight: true
Layout.fillHeight: false
Layout.preferredHeight: 220
Layout.minimumHeight: 160
Layout.minimumHeight: 120
visible: trendChart.hasData && streamController.mode === "live"
color: Theme.cardBackground
border.color: Theme.cardBorder
@@ -183,9 +184,11 @@ Item {
Item {
Layout.fillWidth: true
visible: transportBar.hasContent
implicitHeight: trendPane.visible ? 52 : 16
}
TransportBar {
id: transportBar
Layout.fillWidth: true
}
}
@@ -12,6 +12,7 @@ ColumnLayout {
spacing: 6
property var s: streamController.stats
property alias showLabels: labelsToggle.checked
property alias showExtremes: extremesToggle.checked
property alias heatmapBlend: heatmapSlider.value
property alias showThickness: thicknessToggle.checked
@@ -353,6 +354,13 @@ ColumnLayout {
font.pixelSize: Theme.fontSm
}
PanelCheckBox {
id: extremesToggle
text: "Highlight Min/Max"
checked: true
font.pixelSize: Theme.fontSm
}
PanelCheckBox {
id: clusterAverageToggle
text: "Average Clusters"
@@ -7,6 +7,7 @@ Item {
id: root
property real blend: 0.0
property bool showLabels: true
property alias showExtremes: map.showExtremes
property alias showThickness: map.showThickness
WaferMapItem {
@@ -39,7 +40,9 @@ Item {
HoverHandler {
id: hoverHandler
onPointChanged: {
map.hoveredIndex = map.which_marker(hoverHandler.point.position.x, hoverHandler.point.position.y);
map.hoveredIndex = streamController.mode === "live"
? -1
: map.which_marker(hoverHandler.point.position.x, hoverHandler.point.position.y);
}
onHoveredChanged: if (!hoverHandler.hovered) map.hoveredIndex = -1
cursorShape: map.hoveredIndex >= 0 ? Qt.PointingHandCursor : Qt.ArrowCursor
@@ -47,6 +50,8 @@ Item {
TapHandler {
onTapped: ev => {
if (streamController.mode === "live")
return;
var idx = map.which_marker(ev.position.x, ev.position.y);
if (idx >= 0)
replaceDialog.openFor(idx);
@@ -225,6 +225,12 @@ class SessionController(QObject):
return
if mode != "live":
self.stopStream()
if self._mode == "live":
# Leaving a live session: blank the wafer map instead of
# freezing the last streamed frame on screen.
self._last = None
self._last_raw_frame = None
self.frameUpdated.emit()
self._play_timer.stop()
self._mode = mode
self.modeChanged.emit()
@@ -58,6 +58,9 @@ class TrendChartItem(QQuickPaintedItem):
# `_padding` alone — that mismatch is what clipped y-axis text before.
self._margin_left: int = 48
self._margin_bottom: int = 30
# Top tick label is drawn centered on the plot's top edge (y_px - 7),
# so the plot needs headroom or the label kisses the card border.
self._margin_top: int = 10
self._line_color = QColor("#5B9DF5")
self._grid_color = QColor("#2A3441")
self._text_color = QColor("#CBD5E1")
@@ -178,10 +181,11 @@ class TrendChartItem(QQuickPaintedItem):
return
left = self._padding + self._margin_left
top = self._padding + self._margin_top
bottom_inset = self._padding + self._margin_bottom
plot_rect = QRectF(left, self._padding,
plot_rect = QRectF(left, top,
max(1, w - left - self._padding),
max(1, h - self._padding - bottom_inset))
max(1, h - top - bottom_inset))
self._draw_grid(painter, plot_rect)
@@ -62,6 +62,7 @@ class WaferMapItem(QQuickPaintedItem):
marginChanged = Signal()
blendChanged = Signal()
showLabelsChanged = Signal()
showExtremesChanged = Signal()
colorsChanged = Signal()
shapeChanged = Signal()
sizeChanged = Signal()
@@ -80,6 +81,7 @@ class WaferMapItem(QQuickPaintedItem):
self._margin: float = 1.0
self._blend: float = 0.0
self._show_labels: bool = True
self._show_extremes: bool = True
self._shape: str = "round"
self._size: float = 300.0
self._thickness_data: list[float] = []
@@ -199,6 +201,16 @@ class WaferMapItem(QQuickPaintedItem):
self.showLabelsChanged.emit()
self.update()
@Property(bool, notify=showExtremesChanged)
def showExtremes(self) -> bool:
return self._show_extremes
@showExtremes.setter # type: ignore[no-redef]
def showExtremes(self, val: bool) -> None:
self._show_extremes = bool(val)
self.showExtremesChanged.emit()
self.update()
@Property(str, notify=shapeChanged)
def shape(self) -> str:
return self._shape
@@ -340,7 +352,6 @@ class WaferMapItem(QQuickPaintedItem):
img.fill(0) # transparent background
painter = QPainter(img)
self.paint(painter)
self._paint_extremes(painter)
if lines:
painter.fillRect(QRectF(0, h, w, footer_h), QColor("#101014"))
painter.setPen(QColor("#CBD5E1"))
@@ -357,12 +368,8 @@ class WaferMapItem(QQuickPaintedItem):
# ── internal ─────────────────────────────────────────────────────────
def _paint_extremes(self, painter: QPainter) -> None:
"""Ring the hottest (high color) and coldest (low color) markers.
Export-only overlay — paint() itself stays untouched so the live
view is unchanged.
"""
if not self._values or not self._markers:
"""Ring the hottest (high color) and coldest (low color) markers."""
if not self._show_extremes or not self._values or not self._markers:
return
s = compute_stats(self._values)
ring_r = self._marker_r + 5
@@ -606,6 +613,7 @@ class WaferMapItem(QQuickPaintedItem):
painter.setOpacity(1.0)
self._paint_markers(painter)
self._paint_extremes(painter)
def _paint_template(self, painter: QPainter, cx: int, cy: int, r_px: int) -> None:
ds = self._draw_size()