feat(export): batch wafer-map PNG export from file list

- BatchExport worker + BatchExportController (threaded, busy flag)
- SourcePanel: Export Maps button → FolderDialog → batch export
  with progress/summary banner (dismissible)
- StreamControlPanel: name single export after loaded CSV + timestamp
This commit is contained in:
jack
2026-07-11 23:53:45 -07:00
parent c26de492d7
commit 7d98759b43
9 changed files with 448 additions and 8 deletions
+68
View File
@@ -0,0 +1,68 @@
from __future__ import annotations
from dataclasses import dataclass
from datetime import datetime
from pathlib import Path
from pygui.backend.data.data_records import (
is_official_csv,
read_data_records,
read_official_csv,
)
from pygui.backend.models.frame_player import frames_from_wafer_data
from pygui.backend.visualization.wafer_map_item import WaferMapItem
from pygui.backend.wafer.zwafer_parser import ZWaferParser
_EXPORT_SIZE = 600
@dataclass
class BatchExportResult:
file_path: str
output_path: str
success: bool
error: str = ""
def export_batch(
file_paths: list[str], output_dir: str, timestamp: str | None = None
) -> list[BatchExportResult]:
"""Render the last frame of each file to a PNG in output_dir.
Skips files that fail to parse or render, continuing with the rest.
"""
ts = timestamp or datetime.now().strftime("%Y%m%d_%H%M%S")
results = []
for file_path in file_paths:
results.append(_export_one(file_path, output_dir, ts))
return results
def _export_one(file_path: str, output_dir: str, ts: str) -> BatchExportResult:
base_name = Path(file_path).stem
output_path = str(Path(output_dir) / f"{base_name}_{ts}.png")
try:
if is_official_csv(file_path):
sensors, records = read_official_csv(file_path)
else:
data, _ = ZWaferParser().parse(file_path)
if data is None or not data.sensors:
return BatchExportResult(file_path, output_path, False, "Invalid layout or missing sensors")
sensors = data.sensors
records = read_data_records(file_path)
frames = frames_from_wafer_data(None, records)
if not sensors or not frames:
return BatchExportResult(file_path, output_path, False, "No sensors or frames found")
item = WaferMapItem()
item.setWidth(_EXPORT_SIZE)
item.setHeight(_EXPORT_SIZE)
item.sensors = [{"label": s.label, "x": s.x, "y": s.y} for s in sensors] # type: ignore[method-assign]
item.values = frames[-1].values # type: ignore[method-assign]
if not item.export_image(output_path):
return BatchExportResult(file_path, output_path, False, "Export render failed")
return BatchExportResult(file_path, output_path, True)
except Exception as exc:
return BatchExportResult(file_path, output_path, False, str(exc))