feat(ui): decouple QML page modules and update tab layouts
Extract ReadoutPanel, WaferMapView, and TransportBar into reusable components and bind tabs to the new session controllers.
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.TableView
|
||||
import ISC
|
||||
|
||||
// ===== Data Tab =====
|
||||
@@ -74,6 +75,29 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
// --- Toolbar ---
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
spacing: 8
|
||||
|
||||
Button {
|
||||
text: "Open in Excel"
|
||||
icon.source: "icon/excel.svg"
|
||||
onClicked: deviceController.openCsvFile()
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Compare"
|
||||
onClicked: compareDialog.open()
|
||||
}
|
||||
|
||||
Button {
|
||||
text: "Split"
|
||||
onClicked: splitDialog.open()
|
||||
}
|
||||
|
||||
Item { Layout.fillWidth: true}
|
||||
}
|
||||
// --- Table container ---
|
||||
Rectangle {
|
||||
Layout.fillWidth: true
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Dialogs
|
||||
import QtQuick.Layouts
|
||||
import ISC
|
||||
import ISC.Tabs.components
|
||||
import ISC.Wafer
|
||||
|
||||
Item {
|
||||
id: root
|
||||
@@ -23,6 +25,16 @@ Item {
|
||||
var ss = s % 60
|
||||
return (m < 10 ? "0" : "") + m + ":" + (ss < 10 ? "0" : "") + ss
|
||||
}
|
||||
// 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.
|
||||
Connections {
|
||||
target: streamController
|
||||
function onTrendData(avgsJson) {
|
||||
trendChart.setDataFromJson(avgsJson)
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
@@ -37,7 +49,7 @@ Item {
|
||||
// Mode toggle
|
||||
TabBar {
|
||||
id: modeBar
|
||||
currentIndex: 1 // Default to "Review"
|
||||
currentIndex: 0 // Default to "Review"
|
||||
spacing: 2
|
||||
padding: 2
|
||||
background: Rectangle {
|
||||
@@ -46,6 +58,22 @@ Item {
|
||||
border.color: Theme.cardBorder
|
||||
border.width: Theme.borderThin
|
||||
}
|
||||
TabButton {
|
||||
text: "Review"
|
||||
implicitWidth: 64; implicitHeight: 28
|
||||
background: Rectangle {
|
||||
color: parent.checked ? Theme.tabActiveBackground : "transparent"
|
||||
radius: Theme.radiusSm - 1
|
||||
}
|
||||
contentItem: Text {
|
||||
text: parent.text
|
||||
color: parent.checked ? Theme.headingColor : Theme.bodyColor
|
||||
font.pixelSize: 11
|
||||
font.weight: parent.checked ? Font.Medium : Font.Normal
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
TabButton {
|
||||
text: "Live"
|
||||
enabled: deviceController.connectionStatus === "Connected"
|
||||
@@ -64,33 +92,20 @@ Item {
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
TabButton {
|
||||
text: "Review"
|
||||
implicitWidth: 64; implicitHeight: 28
|
||||
background: Rectangle {
|
||||
color: parent.checked ? Theme.tabActiveBackground : "transparent"
|
||||
radius: Theme.radiusSm - 1
|
||||
}
|
||||
contentItem: Text {
|
||||
text: parent.text
|
||||
color: parent.checked ? Theme.headingColor : Theme.bodyColor
|
||||
font.pixelSize: 11
|
||||
font.weight: parent.checked ? Font.Medium : Font.Normal
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
onCurrentIndexChanged:
|
||||
if (currentIndex === 0){
|
||||
// Entering Live mode, only work when wafer detected/connected
|
||||
streamController.setMode("live")
|
||||
if (deviceController.connectionStatus === "Connected") {
|
||||
var fc = ""
|
||||
if (deviceController.lastWaferInfo && deviceController.lastWaferInfo.length > 0) {
|
||||
fc = deviceController.lastWaferInfo[0]
|
||||
}
|
||||
streamController.startStream(deviceController.selectedPort, fc)
|
||||
if (currentIndex === 1){
|
||||
// Guard: revert to Review if not connected
|
||||
if (deviceController.connectionStatus !== "Connected") {
|
||||
currentIndex = 0
|
||||
return
|
||||
}
|
||||
// Entering Live mode
|
||||
streamController.setMode("live")
|
||||
var fc = ""
|
||||
if (deviceController.lastWaferInfo && deviceController.lastWaferInfo.length > 0) {
|
||||
fc = deviceController.lastWaferInfo[0]
|
||||
}
|
||||
streamController.startStream(deviceController.selectedPort, fc)
|
||||
} else {
|
||||
// Entering Review Mode (automatically calls stopStream in backend)
|
||||
streamController.setMode("review")
|
||||
@@ -172,34 +187,62 @@ Item {
|
||||
}
|
||||
|
||||
// LIVE timer
|
||||
RowLayout {
|
||||
spacing: 5
|
||||
visible: streamController.mode === "live" && streamController.state !== "idle"
|
||||
Rectangle {
|
||||
width: 7; height: 7; radius: 4
|
||||
color: Theme.liveColor
|
||||
}
|
||||
Label {
|
||||
text: "LIVE " + root.fmtTime(root._liveSecs)
|
||||
color: Theme.liveColor
|
||||
font.pixelSize: 10
|
||||
font.weight: Font.Medium
|
||||
font.letterSpacing: 0.8
|
||||
}
|
||||
}
|
||||
// RowLayout {
|
||||
// spacing: 5
|
||||
// visible: streamController.mode === "live" && streamController.state !== "idle"
|
||||
// Rectangle {
|
||||
// width: 7; height: 7; radius: 4
|
||||
// color: Theme.liveColor
|
||||
// }
|
||||
// Label {
|
||||
// text: "LIVE " + root.fmtTime(root._liveSecs)
|
||||
// color: Theme.liveColor
|
||||
// font.pixelSize: 10
|
||||
// font.weight: Font.Medium
|
||||
// font.letterSpacing: 0.8
|
||||
// }
|
||||
// }
|
||||
|
||||
// IDLE label (review / not connected)
|
||||
Label {
|
||||
visible: streamController.mode === "review" || streamController.state === "idle"
|
||||
text: streamController.state.toUpperCase()
|
||||
color: Theme.bodyColor
|
||||
font.pixelSize: 11
|
||||
font.weight: Font.Medium
|
||||
font.letterSpacing: 1.2
|
||||
// Label {
|
||||
// visible: streamController.mode === "review" || streamController.state === "idle"
|
||||
// text: streamController.state.toUpperCas`e`()
|
||||
// color: Theme.bodyColor
|
||||
// font.pixelSize: 11
|
||||
// font.weight: Font.Medium
|
||||
// font.letterSpacing: 1.2
|
||||
// }
|
||||
Button{
|
||||
text: "Export PNG"
|
||||
onClicked: exportDialog.open()
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: exportDialog
|
||||
title: "Export Wafer Map"
|
||||
fileMode: FileDialog.SaveFile
|
||||
nameFilters: ["PNG files (*.png)"]
|
||||
onAccepted: waferView.exportImage(String(selectedFile).replace(/^file:\/\//, ""))
|
||||
}
|
||||
}
|
||||
|
||||
// ── Body: 3 zones ─────────────────────────────────────────────────
|
||||
// ── Body: 3 zones (TODO P2.1: becomes 2 zones) ──────────────────────
|
||||
// -------------------------------------------------------------------
|
||||
// TODO P2.1: Delete the SourcePanel Rectangle (lines ~219-233) —
|
||||
// the file list moved to the rail (P1.3 Map context panel).
|
||||
//
|
||||
// THINKING:
|
||||
// SourcePanel shows a file list driven by file_browser — now that
|
||||
// the Map tab's context panel in the rail (P1.3) provides the same
|
||||
// file list with search/filter, SourcePanel in the wafer body is
|
||||
// redundant. The wafer map gets more horizontal space.
|
||||
//
|
||||
// After deletion: RowLayout has 2 children instead of 3.
|
||||
// Add Layout.fillWidth: true to the center ColumnLayout so it
|
||||
// fills the vacated width.
|
||||
//
|
||||
// Cross-ref: P1.3 (Map context panel), P2.1 (this task)
|
||||
// -------------------------------------------------------------------
|
||||
RowLayout {
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
@@ -233,20 +276,29 @@ Item {
|
||||
blend: readoutPanel.heatmapBlend
|
||||
showLabels: readoutPanel.showLabels
|
||||
}
|
||||
|
||||
// Horizontal separation line with vertical padding
|
||||
Item {
|
||||
Rectangle {
|
||||
id: trendPane
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: 32
|
||||
Rectangle {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: Theme.cardBorder
|
||||
implicitHeight: 160
|
||||
visible: trendChart.hasData
|
||||
color: Theme.cardBackground
|
||||
border.color: Theme.cardBorder
|
||||
border.width: 1
|
||||
radius: Theme.radiusMd
|
||||
|
||||
TrendChartItem {
|
||||
id: trendChart
|
||||
anchors.fill: parent
|
||||
anchors.margins: 8
|
||||
}
|
||||
}
|
||||
|
||||
TransportBar { Layout.fillWidth: true }
|
||||
Item { Layout.fillWidth: true; implicitHeight: 16 }
|
||||
TransportBar {
|
||||
Layout.fillWidth: true
|
||||
visible: streamController.mode !== "live" || trendChart.hasData
|
||||
height: visible ? implicitHeight : 0
|
||||
}
|
||||
}
|
||||
|
||||
// Readout panel — bordered card
|
||||
|
||||
@@ -175,6 +175,14 @@ ColumnLayout {
|
||||
bottomPadding: 8
|
||||
}
|
||||
|
||||
PanelCheckBox {
|
||||
id: thicknessToggle
|
||||
text: "Show Thickness"
|
||||
checked: false
|
||||
font.pixelSize: 11
|
||||
onCheckedChanged: waferMapItem.showThickness = checked
|
||||
}
|
||||
|
||||
PanelCheckBox {
|
||||
id: labelsToggle
|
||||
text: "Labels"
|
||||
|
||||
@@ -40,6 +40,10 @@ Item {
|
||||
|
||||
ReplaceSensorDialog { id: replaceDialog}
|
||||
|
||||
function exportImage(filePath) {
|
||||
return map.export_image(filePath)
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
||||
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.29289 1.29289C9.48043 1.10536 9.73478 1 10 1H18C19.6569 1 21 2.34315 21 4V9C21 9.55228 20.5523 10 20 10C19.4477 10 19 9.55228 19 9V4C19 3.44772 18.5523 3 18 3H11V8C11 8.55228 10.5523 9 10 9H5V20C5 20.5523 5.44772 21 6 21H7C7.55228 21 8 21.4477 8 22C8 22.5523 7.55228 23 7 23H6C4.34315 23 3 21.6569 3 20V8C3 7.73478 3.10536 7.48043 3.29289 7.29289L9.29289 1.29289ZM6.41421 7H9V4.41421L6.41421 7ZM19 12C19.5523 12 20 12.4477 20 13V19H23C23.5523 19 24 19.4477 24 20C24 20.5523 23.5523 21 23 21H19C18.4477 21 18 20.5523 18 20V13C18 12.4477 18.4477 12 19 12ZM11.8137 12.4188C11.4927 11.9693 10.8682 11.8653 10.4188 12.1863C9.96935 12.5073 9.86526 13.1318 10.1863 13.5812L12.2711 16.5L10.1863 19.4188C9.86526 19.8682 9.96935 20.4927 10.4188 20.8137C10.8682 21.1347 11.4927 21.0307 11.8137 20.5812L13.5 18.2205L15.1863 20.5812C15.5073 21.0307 16.1318 21.1347 16.5812 20.8137C17.0307 20.4927 17.1347 19.8682 16.8137 19.4188L14.7289 16.5L16.8137 13.5812C17.1347 13.1318 17.0307 12.5073 16.5812 12.1863C16.1318 11.8653 15.5073 11.9693 15.1863 12.4188L13.5 14.7795L11.8137 12.4188Z" fill="#000000"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
Reference in New Issue
Block a user