9779baa468
Move all application source under src/pygui/ and rewire imports, build config, and QML module path to match. - Relocate backend/, serialcomm/, and the ISC QML module into src/pygui/; convert main.py into pygui/__main__.py with a main() entry point (run via `python -m pygui` or the new `isc` script) - Rewrite absolute imports: backend.* -> pygui.backend.*, serialcomm.* -> pygui.serialcomm.* (source + tests) - Move app icons (isc.ico/icns) into packaging/ - Update README and ISC.qmlproject to the new paths
291 lines
10 KiB
QML
291 lines
10 KiB
QML
import QtQuick
|
|
import QtQuick.Layouts
|
|
import QtQuick.Controls
|
|
import ISC
|
|
|
|
// ===== Status Tab =====
|
|
// Blank on startup. Fills in after Detect Wafer fires from the left rail.
|
|
ColumnLayout {
|
|
id: root
|
|
anchors.fill: parent
|
|
anchors.margins: Theme.panelPadding
|
|
spacing: Theme.rightPaneGap
|
|
|
|
property alias waferFamilyCode: waferInfoFamily.text
|
|
property alias waferSerialNumber: waferSerial.text
|
|
property alias waferSensorCount: waferSensors.text
|
|
property alias waferRuntime: waferRuntime.text
|
|
property alias waferCycles: waferCycles.text
|
|
property bool waferDetected: false
|
|
|
|
// Latches true once any operation starts (or a previous session is
|
|
// restored) and never resets, so the status panel stays visible even
|
|
// when a detect finds no wafer.
|
|
property bool statusActive: false
|
|
|
|
// Data summary after parse
|
|
property int dataRows: 0
|
|
property int dataCols: 0
|
|
property string csvPath: ""
|
|
property bool dataParsed: false
|
|
|
|
// ===== Empty state — nothing shown until detect fires =====
|
|
Item {
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
visible: !root.statusActive
|
|
}
|
|
|
|
// ===== Results area (shown once detect/operation runs) =====
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
visible: root.statusActive
|
|
spacing: Theme.rightPaneGap
|
|
|
|
// --- Connection Status ---
|
|
Rectangle {
|
|
Layout.fillWidth: true
|
|
Layout.preferredHeight: 86
|
|
color: {
|
|
if (deviceController.connectionStatus === "Connected") return Theme.statusSuccessColor + "22"
|
|
if (deviceController.connectionStatus === "Disconnected") return Theme.statusErrorColor + "22"
|
|
return Theme.cardBackground
|
|
}
|
|
border.color: {
|
|
if (deviceController.connectionStatus === "Connected") return Theme.statusSuccessColor
|
|
if (deviceController.connectionStatus === "Disconnected") return Theme.statusErrorColor
|
|
return Theme.cardBorder
|
|
}
|
|
radius: Theme.radiusSm
|
|
|
|
ColumnLayout {
|
|
anchors.fill: parent
|
|
anchors.margins: Theme.panelPadding
|
|
spacing: 4
|
|
|
|
Text {
|
|
text: deviceController.connectionStatus
|
|
color: {
|
|
if (deviceController.connectionStatus === "Connected") return Theme.statusSuccessColor
|
|
if (deviceController.connectionStatus === "Disconnected") return Theme.statusErrorColor
|
|
return Theme.bodyColor
|
|
}
|
|
font.bold: true
|
|
font.pixelSize: 16
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
Text {
|
|
text: "Port: " + (deviceController.selectedPort || "None selected")
|
|
color: Theme.bodyColor
|
|
font.pixelSize: 14
|
|
Layout.fillWidth: true
|
|
}
|
|
|
|
Text {
|
|
text: "Save dir: " + deviceController.saveDataDir
|
|
color: Theme.bodyColor
|
|
font.pixelSize: 12
|
|
opacity: 0.7
|
|
elide: Text.ElideMiddle
|
|
Layout.fillWidth: true
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Wafer Info ---
|
|
GroupBox {
|
|
title: "Wafer Information"
|
|
visible: root.waferDetected
|
|
Layout.fillWidth: true
|
|
|
|
ColumnLayout {
|
|
anchors.fill: parent
|
|
anchors.margins: Theme.panelPadding
|
|
spacing: 8
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
spacing: Theme.rightPaneGap
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 4
|
|
Text { text: "Family Code"; color: Theme.subheadingColor; font.pixelSize: 12 }
|
|
Text { id: waferInfoFamily; text: "—"; color: Theme.bodyColor; font.pixelSize: 16; font.bold: true }
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 4
|
|
Text { text: "Serial Number"; color: Theme.subheadingColor; font.pixelSize: 12 }
|
|
Text { id: waferSerial; text: "—"; color: Theme.bodyColor; font.pixelSize: 16; font.bold: true }
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 4
|
|
Text { text: "Sensor Count"; color: Theme.subheadingColor; font.pixelSize: 12 }
|
|
Text { id: waferSensors; text: "—"; color: Theme.bodyColor; font.pixelSize: 16; font.bold: true }
|
|
}
|
|
}
|
|
|
|
RowLayout {
|
|
Layout.fillWidth: true
|
|
spacing: Theme.rightPaneGap
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 4
|
|
Text { text: "Runtime"; color: Theme.subheadingColor; font.pixelSize: 12 }
|
|
Text { id: waferRuntime; text: "—"; color: Theme.bodyColor; font.pixelSize: 16; font.bold: true }
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 4
|
|
Text { text: "Cycles"; color: Theme.subheadingColor; font.pixelSize: 12 }
|
|
Text { id: waferCycles; text: "—"; color: Theme.bodyColor; font.pixelSize: 16; font.bold: true }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Data Summary (shown after parse) ---
|
|
GroupBox {
|
|
title: "Data Summary"
|
|
visible: root.dataParsed
|
|
Layout.fillWidth: true
|
|
|
|
RowLayout {
|
|
anchors.fill: parent
|
|
anchors.margins: Theme.panelPadding
|
|
spacing: Theme.rightPaneGap
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 4
|
|
Text { text: "Rows"; color: Theme.subheadingColor; font.pixelSize: 12 }
|
|
Text { text: String(root.dataRows); color: Theme.bodyColor; font.pixelSize: 16; font.bold: true }
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 4
|
|
Text { text: "Sensors"; color: Theme.subheadingColor; font.pixelSize: 12 }
|
|
Text { text: String(root.dataCols); color: Theme.bodyColor; font.pixelSize: 16; font.bold: true }
|
|
}
|
|
|
|
ColumnLayout {
|
|
Layout.fillWidth: true
|
|
spacing: 4
|
|
Text { text: "CSV"; color: Theme.subheadingColor; font.pixelSize: 12 }
|
|
Text { text: root.csvPath; color: Theme.bodyColor; font.pixelSize: 12; elide: Text.ElideMiddle }
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Activity Log ---
|
|
GroupBox {
|
|
title: "Activity Log"
|
|
Layout.fillWidth: true
|
|
Layout.fillHeight: true
|
|
|
|
ScrollView {
|
|
anchors.fill: parent
|
|
clip: true
|
|
|
|
TextArea {
|
|
id: activityLog
|
|
width: parent.width
|
|
text: ""
|
|
readOnly: true
|
|
font.family: "monospace"
|
|
font.pixelSize: 12
|
|
wrapMode: TextArea.WordWrap
|
|
leftPadding: 4
|
|
rightPadding: 4
|
|
color: Theme.bodyColor
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: deviceController
|
|
function onActivityLogUpdated(newLog) {
|
|
activityLog.text = newLog
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// --- Signal Handlers ---
|
|
Connections {
|
|
target: deviceController
|
|
// Any operation start (detect/read/erase/debug) latches the panel on.
|
|
function onPortsUpdated() {
|
|
if (deviceController.operationInProgress)
|
|
root.statusActive = true
|
|
}
|
|
|
|
function onDetectResult(result) {
|
|
root.statusActive = true
|
|
if (result && result.familyCode) {
|
|
root.waferDetected = true
|
|
waferInfoFamily.text = result.familyCode
|
|
waferSerial.text = result.serialNumber
|
|
waferSensors.text = String(result.sensorCount)
|
|
waferRuntime.text = result.runtime + "s"
|
|
waferCycles.text = String(result.cycleCount)
|
|
}
|
|
// On failure, keep the last displayed wafer info and do not change waferDetected.
|
|
}
|
|
|
|
function onStatusRestored() {
|
|
// Show restored wafer info if available
|
|
var info = deviceController.lastWaferInfo
|
|
if (info && info.length > 0) {
|
|
root.statusActive = true
|
|
root.waferDetected = true
|
|
waferInfoFamily.text = info[0] || "—"
|
|
waferSerial.text = info[1] || "—"
|
|
waferSensors.text = String(info[2] || 0)
|
|
waferRuntime.text = (info[3] || 0) + "s"
|
|
waferCycles.text = String(info[4] || 0)
|
|
}
|
|
// Show data summary if data was previously parsed
|
|
if (deviceController.dataRowCount > 0) {
|
|
root.statusActive = true
|
|
root.dataParsed = true
|
|
root.dataRows = deviceController.dataRowCount
|
|
root.dataCols = deviceController.dataColCount
|
|
root.csvPath = "" // CSV path not persisted as full path, just show it was parsed
|
|
}
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: deviceController
|
|
function onReadResult(result) {
|
|
root.dataParsed = false
|
|
root.dataRows = 0
|
|
root.dataCols = 0
|
|
root.csvPath = ""
|
|
}
|
|
}
|
|
|
|
Connections {
|
|
target: deviceController
|
|
function onParsedDataReady(result) {
|
|
if (result && result.success) {
|
|
root.dataParsed = true
|
|
root.dataRows = result.rows
|
|
root.dataCols = result.cols
|
|
root.csvPath = result.csv_path || ""
|
|
} else {
|
|
root.dataParsed = false
|
|
}
|
|
}
|
|
}
|
|
}
|