feat: implement master file configuration, UI indicators, and a Run Comparison tool for the Data tab

This commit is contained in:
jack
2026-07-07 15:05:22 -07:00
parent 1e03227788
commit 92f130b3bd
8 changed files with 798 additions and 428 deletions
@@ -12,6 +12,7 @@ from __future__ import annotations
import json
import logging
import math
from typing import Optional
from PySide6.QtCore import Property, QPoint, QRectF, Qt, Signal, Slot
@@ -190,9 +191,14 @@ class TrendChartItem(QQuickPaintedItem):
lo = min(self._data)
hi = max(self._data)
if hi - lo < 1e-9:
return lo - 5.0, hi + 5.0
buf = (hi - lo) * 0.1
return lo - buf, hi + buf
lo, hi = lo - 5.0, hi + 5.0
# Snap to a "nice" tick step (1/2/5 × 10^k) so the axis holds still
# while streaming and only moves when data crosses a step boundary —
# raw min/max rescaled every frame, which read as an unstable chart.
raw_step = (hi - lo) / 4
mag = 10 ** math.floor(math.log10(raw_step))
step = next(s * mag for s in (1, 2, 5, 10) if s * mag >= raw_step)
return math.floor(lo / step) * step, math.ceil(hi / step) * step
def _x_to_px(self, i: int, n: int, plot_rect: QRectF) -> float:
if n <= 1:
@@ -234,7 +240,7 @@ class TrendChartItem(QQuickPaintedItem):
t = i / rows
y_val = y_max - t * (y_max - y_min)
y_px = plot_rect.top() + t * plot_rect.height()
label = f"{y_val:.1f}"
label = f"{y_val:g}"
painter.drawText(QRectF(0, y_px - 7, label_w, 14),
Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignVCenter,
label)
@@ -250,12 +256,17 @@ class TrendChartItem(QQuickPaintedItem):
for i in range(cols + 1):
idx = round((i / cols) * (n - 1))
x_px = self._x_to_px(idx, n, plot_rect)
align = (Qt.AlignmentFlag.AlignLeft if i == 0
else Qt.AlignmentFlag.AlignRight if i == cols
else Qt.AlignmentFlag.AlignHCenter)
painter.drawText(QRectF(x_px - 20, y_px, 40, 14),
align | Qt.AlignmentFlag.AlignTop,
str(idx))
if i == 0:
rect = QRectF(x_px, y_px, 40, 14)
align = Qt.AlignmentFlag.AlignLeft
elif i == cols:
# Right-anchor inside the plot so the label never clips off-widget
rect = QRectF(x_px - 40, y_px, 40, 14)
align = Qt.AlignmentFlag.AlignRight
else:
rect = QRectF(x_px - 20, y_px, 40, 14)
align = Qt.AlignmentFlag.AlignHCenter
painter.drawText(rect, align | Qt.AlignmentFlag.AlignTop, str(idx))
def _draw_line(
self,
@@ -347,6 +347,10 @@ class WaferMapItem(QQuickPaintedItem):
self._thickness_heatmap = None
return
field = refine_field(field)
# ponytail: RBF overshoots around outlier sensors, inventing values no
# sensor produced (e.g. negative diffs when all diffs are positive).
# Clamp to the real data range so color only reflects measured values.
field = np.clip(field, vs.min(), vs.max())
if self._shape == "round":
alpha = ellipse_alpha(*field.shape)
else:
@@ -467,6 +471,10 @@ class WaferMapItem(QQuickPaintedItem):
self._heatmap = None
return
field = refine_field(field)
# ponytail: RBF overshoots around outlier sensors, inventing values no
# sensor produced (e.g. negative diffs when all diffs are positive).
# Clamp to the real data range so color only reflects measured values.
field = np.clip(field, vs.min(), vs.max())
if self._shape == "round":
alpha = ellipse_alpha(*field.shape)
else:
@@ -609,11 +617,11 @@ class WaferMapItem(QQuickPaintedItem):
font_scale = 0.85
id_font = QFont()
id_font.setPointSize(max(4, int(r * font_scale)))
id_font.setPointSize(max(9, int(r * font_scale)))
id_font.setBold(True)
temp_font = QFont()
temp_font.setPointSize(max(3, int((r - 1) * font_scale)))
temp_font.setPointSize(max(9, int(r * font_scale)))
band_color = {
"in_range": self._in_range_color,