feat: add interactive folder selection for CSV saves and stabilize cross-platform dependency locking

This commit is contained in:
jack
2026-06-18 10:30:51 -07:00
parent e545c245d7
commit 1119733929
5 changed files with 201 additions and 36 deletions
+34 -1
View File
@@ -24,8 +24,15 @@ Rectangle {
property int selectedTabIndex: 0
property int selectedSideActionIndex: -1 // nothing active on startup
property bool memoryRead: false
property bool waferDetected: false
Connections {
target: deviceController
function onDetectResult(result){
//result is a waferInfo dict on success, None on failure
root.waferDetected = (result != null && result!= undefined)
}
function onParsedDataReady(result) {
if (result.success && result.csv_path) {
// Load the freshly read CSV into the player
@@ -36,8 +43,23 @@ Rectangle {
}
}
}
Connections {
target: deviceController
function onReadResult(result) {
// result has "success" key on success, "error" key on failure
root.memoryRead = (result !== null &&
result !== undefined &&
result.success === true)
// ===== Main Two-Column Layout =====
if (root.memoryRead) {
console.log(`[P1.1] Memory read complete: ${result.bytes} bytes`)
} else {
console.log(`[P1.1] Read failed: ${result.error || "unknown"}`)
}
}
}
// ===== Main Two-Column Layout ======
RowLayout {
anchors.fill: parent
spacing: 0
@@ -66,6 +88,16 @@ Rectangle {
Button {
id: control
text: modelData
enabled: {
switch (index) {
case 0: return true // DETECT WAFER
case 1: return root.waferDetected // READ MEMORY
case 2: return root.memoryRead // OPEN CSV IN EXCEL
case 3: return root.waferDetected // ERASE MEMORY
case 4: return root.memoryRead // IMPORT DATA
default: return true // STORED DATA
}
}
property bool isActive: index === root.selectedSideActionIndex
Layout.fillWidth: true
Layout.preferredHeight: Math.max(Theme.sideButtonMinHeight, sideRail.computedButtonHeight)
@@ -74,6 +106,7 @@ Rectangle {
root.selectedSideActionIndex = index
root.selectedTabIndex = 0 // always jump to Status tab
if (index === 0) {
root.memoryRead = false
streamController.setMode("review")
streamController.stopStream()
deviceController.detectWafer()
+26
View File
@@ -1,6 +1,7 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import QtQuick.Dialogs
import ISC
// ===== Status Tab =====
@@ -29,6 +30,28 @@ ColumnLayout {
property string csvPath: ""
property bool dataParsed: false
function cleanFolderUrl(url) {
return decodeURIComponent(String(url).replace(/^file:\/\//, ""))
}
function currentFamilyCode() {
var info = deviceController.lastWaferInfo
return info && info.length > 0 ? (info[0] || "") : ""
}
function parseAndSavePendingRead() {
deviceController.parseAndSaveData(root.currentFamilyCode(), deviceController.selectedPort || "")
}
FolderDialog {
id: saveDirDialog
title: "Choose CSV Save Folder"
onAccepted: {
deviceController.setSaveDataDir(root.cleanFolderUrl(selectedFolder))
root.parseAndSavePendingRead()
}
}
// ===== Empty state — nothing shown until detect fires =====
Item {
Layout.fillWidth: true
@@ -345,6 +368,9 @@ ColumnLayout {
root.dataRows = 0
root.dataCols = 0
root.csvPath = ""
if (result && result.success === true)
saveDirDialog.open()
}
}
@@ -303,7 +303,8 @@ class DeviceController(QObject):
"""Read wafer memory in a background thread.
Uses the family code and port from the last detect.
Emits readResult on completion, then auto-chains to parseAndSaveData.
Emits readResult on completion; QML prompts for the save directory
before calling parseAndSaveData.
"""
if self._operation_in_progress:
self._append_log("Already busy — ignoring read request")
@@ -350,9 +351,7 @@ class DeviceController(QObject):
self._raw_bytes = data
self.readResult.emit({"success": True, "bytes": len(data)})
self._set_operation_progress(False)
# Auto-chain: read → parse → save (matches C# behavior).
# Parse runs on main thread — it's CPU-bound but bounded (~1s).
self.parseAndSaveData(family_code, port)
self._append_log("Memory read complete - choose save directory to save CSV")
self._save_status()
return
self._connection_status = "Disconnected"
@@ -489,6 +488,7 @@ class DeviceController(QObject):
return
self._append_log(f"Saved CSV: {csv_path}")
self._last_csv_path = csv_path
# Load data into the QAbstractTableModel
self._data_model.load_data(temp_data, cols)