chore: apply linting fixes and expand ruff coverage to test suite
This commit is contained in:
@@ -3,6 +3,14 @@ import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import ISC
|
||||
|
||||
// TODO P3.1 + P3.2: license grid, "Load License" picker, version binding
|
||||
// THINKING: C# parity requires the dgvLicense grid (Wafer SN, Mfg Date,
|
||||
// License Level, License Date) fed from AES-128-CBC .bin files under
|
||||
// <app_data>/licenses/ — CryptoHelper (backend/crypto/crypto_helper.py)
|
||||
// already has the primitives; the missing piece is the license file model
|
||||
// (P3.1 backend). "Version 0.1.0" below is hardcoded and will drift from
|
||||
// pyproject.toml — expose importlib.metadata.version("pygui") via a context
|
||||
// property instead (P3.2). See docs/pending/alpha-release-polish-plan.md §3.
|
||||
Popup {
|
||||
id: root
|
||||
modal: true
|
||||
|
||||
@@ -603,6 +603,14 @@ class SessionController(QObject):
|
||||
return average_clusters(edited, getattr(self, "_active_clusters", []))
|
||||
return edited
|
||||
|
||||
# TODO P2.4: throttle live-frame UI updates to ~30Hz
|
||||
# THINKING: every decoded serial frame triggers frameUpdated/stateChanged,
|
||||
# which repaints WaferMapItem + trend chart; at high stream rates the GUI
|
||||
# thread saturates on paint, not decode. Buffer the latest frame and flush
|
||||
# via a 33ms QTimer (latest-wins, no queue growth). Recording must keep
|
||||
# writing every frame — only the UI emit is throttled, so place the timer
|
||||
# here rather than in StreamReader. Depends on nothing; verify with P4.3.
|
||||
# See docs/pending/alpha-release-polish-plan.md §2.4.
|
||||
def _emit_current(self) -> None:
|
||||
frame = self._player.current()
|
||||
if frame is None:
|
||||
@@ -828,4 +836,4 @@ class SessionController(QObject):
|
||||
|
||||
def _flush_trend(self) -> None:
|
||||
import json
|
||||
self.trendData.emit(json.dumps(self._trend_buffer))
|
||||
self.trendData.emit(json.dumps(self._trend_buffer))
|
||||
|
||||
@@ -18,6 +18,13 @@ _KERNEL = "thin_plate_spline"
|
||||
_SMOOTHING = 0.0
|
||||
|
||||
|
||||
# TODO P2.2: decouple grid resolution from output size
|
||||
# THINKING: callers pass widget pixel size as width/height, so RBF evaluation
|
||||
# count scales quadratically with viewport size (600px → 360k evaluations).
|
||||
# Interpolate on a fixed ~100×100 grid and let the caller scale the QImage;
|
||||
# sensor density (≤244 points) carries no more spatial information than that.
|
||||
# Called from WaferMapItem._rebuild_heatmap (see its P2.2/P2.3 TODO).
|
||||
# See docs/pending/alpha-release-polish-plan.md §2.2.
|
||||
def interpolate_field(
|
||||
xs: np.ndarray,
|
||||
ys: np.ndarray,
|
||||
|
||||
@@ -290,6 +290,12 @@ class WaferMapItem(QQuickPaintedItem):
|
||||
return idx
|
||||
return -1
|
||||
|
||||
# TODO P6.1: build batch export on top of this single-frame export
|
||||
# THINKING: export_image() already does the hard part (grabToImage → PNG);
|
||||
# batch export is just a loop over file_browser.files that loads each CSV
|
||||
# through the existing wafer-map pipeline and calls this per file into a
|
||||
# chosen output dir, plus an optional summary CSV of (file, min/max/mean).
|
||||
# No new rendering code needed — see docs/pending/alpha-release-polish-plan.md §6.1.
|
||||
@Slot(str, result=bool)
|
||||
def export_image(self, file_path: str) -> bool:
|
||||
"""Export the current wafer map rendering to a PNG file
|
||||
@@ -377,6 +383,13 @@ class WaferMapItem(QQuickPaintedItem):
|
||||
r = max(math.hypot(s.x, s.y) for s in self._sensors)
|
||||
return r * 1.05
|
||||
|
||||
# TODO P6.2: reuse this for the edge-to-center delta metric
|
||||
# THINKING: radial_metrics.py (new) needs the same "which sensors are near
|
||||
# the edge vs. near the center" bucketing this function already computes
|
||||
# for ring-line drawing — do NOT re-derive ring boundaries separately, this
|
||||
# already handles round vs. square wafer shapes correctly (see family_spec.py
|
||||
# square-wafer work). Outermost group ≈ edge sensors, innermost ≈ center.
|
||||
# See docs/pending/alpha-release-polish-plan.md §6.2.
|
||||
def _sensor_ring_radii_mm(self) -> list[float]:
|
||||
"""Distinct radial distances of sensor groups, sorted ascending, plus the outer boundary."""
|
||||
if not self._sensors:
|
||||
@@ -413,6 +426,15 @@ class WaferMapItem(QQuickPaintedItem):
|
||||
self._markers = {i: self._to_px(s.x, s.y, cx, cy, sc)
|
||||
for i, s in enumerate(self._sensors)}
|
||||
|
||||
# TODO P2.2 + P2.3: fixed-resolution RBF grid, then offload to QThreadPool
|
||||
# THINKING: width=height=ds couples RBF cost to widget pixel size — a 600px
|
||||
# viewport solves a 360k-point system per frame where a fixed 100×100 grid
|
||||
# (10k points, GPU bilinear-scales the QImage up) is visually identical
|
||||
# through the 0.4-opacity blend. Do P2.2 first: it may make P2.3 (QRunnable
|
||||
# worker emitting heatmapReady(QImage), guarding against stale results when
|
||||
# sensors change mid-flight) unnecessary for alpha. Grid width param lands
|
||||
# in interpolate_field (see matching TODO in rbf_heatmap.py).
|
||||
# See docs/pending/alpha-release-polish-plan.md §2.2/§2.3.
|
||||
def _rebuild_heatmap(self) -> None:
|
||||
if not self._sensors or not self._values or self._blend == 0.0:
|
||||
self._heatmap = None
|
||||
@@ -612,6 +634,13 @@ class WaferMapItem(QQuickPaintedItem):
|
||||
ox = getattr(s, "offset_x", 0.0) * r
|
||||
oy = getattr(s, "offset_y", 0.0) * r
|
||||
|
||||
# TODO P2.1: hoist fontMetrics() out of the sensor loop
|
||||
# THINKING: id_font/temp_font are fixed for the whole paint pass,
|
||||
# but fontMetrics() is re-queried per sensor — 65+ OS font-engine
|
||||
# round-trips per frame on a dense wafer, ~30/s in Live mode.
|
||||
# Compute id_fm/temp_fm once before `for i, s in ...` (they only
|
||||
# change when r/font_scale change). Verified by P4.3 benchmark.
|
||||
# See docs/pending/alpha-release-polish-plan.md §2.1.
|
||||
painter.setFont(id_font)
|
||||
id_fm = painter.fontMetrics()
|
||||
id_line_h = id_fm.height()
|
||||
|
||||
@@ -69,6 +69,12 @@ def _load_yaml(path: Path) -> dict:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
|
||||
# TODO P6.3: expose this list to a new LayoutSelector.qml (4-button chooser)
|
||||
# THINKING: this already returns everything the UI needs (family names) —
|
||||
# the missing piece is a context-property exposure (SessionController or a
|
||||
# thin new property) and a setLayout(family) slot that calls load_layout()
|
||||
# below. No parsing work needed; this is a wiring-only task.
|
||||
# See docs/pending/alpha-release-polish-plan.md §6.3.
|
||||
def available_families() -> list[str]:
|
||||
return [_family_name(_load_yaml(p)["name"]) for p in _LAYOUTS_DIR.glob("*.yaml")]
|
||||
|
||||
|
||||
Reference in New Issue
Block a user