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
@@ -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()