feat(map): add batch export and adjust layout alignment

- Implement synchronous offscreen wafer map PNG rendering and CSV summary.
- Add "Batch Export" button to file browser sidebar.
- Adjust trend pane bottom spacer to align with About button when visible.
- Fix type check errors and add pytest coverage for batch export.
This commit is contained in:
jack
2026-07-10 15:22:36 -07:00
parent 278a48a2e4
commit 69753e35f9
12 changed files with 422 additions and 187 deletions
+25 -10
View File
@@ -10,15 +10,20 @@ Item {
id: root
anchors.fill: parent
// Wire streamController.trendData (Signal(str) carrying JSON list of floats)
// into the trend chart's data property. The slot parseJsonToData() is defined
// on the Python TrendChartItem; we use it so malformed payloads are logged
// there instead of crashing the QML binding.
// Wire streamController.trendDelta (Signal(str) carrying one new
// [elapsed_s, value] pair per live frame) into the trend chart, which
// accumulates its own bounded 60s window. trendReset clears it when a
// new stream starts. Parsing happens on the Python TrendChartItem side
// so malformed payloads are logged there instead of crashing the QML
// binding.
property string loadErrorMessage: ""
Connections {
target: streamController
function onTrendData(avgsJson) {
trendChart.setDataFromJson(avgsJson);
function onTrendDelta(deltaJson) {
trendChart.appendDelta(deltaJson);
}
function onTrendReset() {
trendChart.clearTrend();
}
function onLoadedFileChanged() {
root.loadErrorMessage = "";
@@ -145,6 +150,8 @@ Item {
id: waferView
Layout.fillWidth: true
Layout.fillHeight: true
Layout.preferredHeight: 400
Layout.minimumHeight: 280
blend: readoutPanel.heatmapBlend
showLabels: readoutPanel.showLabels
showThickness: readoutPanel.showThickness
@@ -152,23 +159,31 @@ Item {
Rectangle {
id: trendPane
Layout.fillWidth: true
implicitHeight: 160
Layout.fillHeight: true
Layout.preferredHeight: 220
Layout.minimumHeight: 160
visible: trendChart.hasData && streamController.mode === "live"
color: Theme.cardBackground
border.color: Theme.cardBorder
border.width: 1
radius: Theme.radiusMd
TrendChartItem {
id: trendChart
ColumnLayout {
anchors.fill: parent
anchors.margins: 8
spacing: 10
TrendChartItem {
id: trendChart
Layout.fillWidth: true
Layout.fillHeight: true
}
}
}
Item {
Layout.fillWidth: true
implicitHeight: 16
implicitHeight: trendPane.visible ? 52 : 16
}
TransportBar {
Layout.fillWidth: true
@@ -275,8 +275,15 @@ Rectangle {
Layout.fillWidth: true
implicitHeight: 28
text: "Export"
// Nothing to export until a file is loaded or a stream has played
enabled: streamController.sensorValues.length > 0
opacity: enabled ? 1 : 0.4
onClicked: exportDialog.open()
onClicked: {
// Default into <saveDataDir>/Export (created on demand)
exportDialog.selectedFile = "file://" + deviceController.exportDir() + "/wafer_map.png"
exportDialog.open()
}
background: Rectangle {
radius: Theme.radiusSm
@@ -299,6 +306,7 @@ Rectangle {
id: exportDialog
title: "Export Wafer Map"
fileMode: FileDialog.SaveFile
defaultSuffix: "png"
nameFilters: ["PNG files (*.png)"]
onAccepted: {
var path = String(selectedFile).replace(/^file:\/\//, "");