SettingTab #1

Merged
jack merged 2 commits from SettingTab into main 2026-04-27 21:28:54 +00:00
6 changed files with 466 additions and 101 deletions
Showing only changes of commit fbb04eb2f7 - Show all commits
+106 -45
View File
@@ -41,7 +41,8 @@ Dialog {
} }
function applyMetadataFromEditor() { function applyMetadataFromEditor() {
if (metadataEditRow < 0) return; if (metadataEditRow < 0)
return;
const rec = tableModel[metadataEditRow]; const rec = tableModel[metadataEditRow];
if (!rec) { if (!rec) {
metadataEditDialog.close(); metadataEditDialog.close();
@@ -56,14 +57,14 @@ Dialog {
readonly property var normalizedRows: (tableModel || []).map(function (row) { readonly property var normalizedRows: (tableModel || []).map(function (row) {
row = row || {}; row = row || {};
return { return {
select: row.selected ?? false, select: row.selected ?? false,
wafer: row.wafer ?? "", wafer: row.wafer ?? "",
date: row.date ?? "", date: row.date ?? "",
chamber: row.chamber ?? "", chamber: row.chamber ?? "",
edit: "", edit: "",
save: "", save: "",
notes: row.notes ?? "", notes: row.notes ?? "",
fileName: row.fileName ?? "", fileName: row.fileName ?? "",
highlight: row.highlight ?? false highlight: row.highlight ?? false
}; };
}) })
@@ -76,9 +77,7 @@ Dialog {
background: Rectangle { background: Rectangle {
radius: Theme.radiusSm radius: Theme.radiusSm
color: dialogButton.down ? Theme.buttonNeutralPressed color: dialogButton.down ? Theme.buttonNeutralPressed : dialogButton.hovered ? Theme.buttonNeutralHover : Theme.buttonNeutralBackground
: dialogButton.hovered ? Theme.buttonNeutralHover
: Theme.buttonNeutralBackground
border.width: Theme.borderThin border.width: Theme.borderThin
border.color: Theme.fieldBorder border.color: Theme.fieldBorder
} }
@@ -236,8 +235,17 @@ Dialog {
columnWidthProvider: function (col) { columnWidthProvider: function (col) {
// [select, wafer, date, chamber, notes, file (variable), edit, save] // [select, wafer, date, chamber, notes, file (variable), edit, save]
const fixed = { 0: 40, 1: 90, 2: 170, 3: 120, 4: 180, 6: 56, 7: 56 }; const fixed = {
if (fixed.hasOwnProperty(col)) return fixed[col]; 0: 40,
1: 90,
2: 170,
3: 120,
4: 180,
6: 56,
7: 56
};
if (fixed.hasOwnProperty(col))
return fixed[col];
// col 5 (File) takes remaining width // col 5 (File) takes remaining width
const w = Math.max(400, tableView.width); const w = Math.max(400, tableView.width);
const used = 40 + 90 + 170 + 120 + 180 + 56 + 56; const used = 40 + 90 + 170 + 120 + 180 + 56 + 56;
@@ -245,14 +253,30 @@ Dialog {
} }
model: TableModel { model: TableModel {
TableModelColumn { display: "select" } TableModelColumn {
TableModelColumn { display: "wafer" } display: "select"
TableModelColumn { display: "date" } }
TableModelColumn { display: "chamber" } TableModelColumn {
TableModelColumn { display: "notes" } display: "wafer"
TableModelColumn { display: "fileName" } }
TableModelColumn { display: "edit" } TableModelColumn {
TableModelColumn { display: "save" } display: "date"
}
TableModelColumn {
display: "chamber"
}
TableModelColumn {
display: "notes"
}
TableModelColumn {
display: "fileName"
}
TableModelColumn {
display: "edit"
}
TableModelColumn {
display: "save"
}
rows: root.normalizedRows rows: root.normalizedRows
} }
@@ -265,8 +289,10 @@ Dialog {
border.color: Theme.softBorder border.color: Theme.softBorder
border.width: Theme.borderThin border.width: Theme.borderThin
color: { color: {
if (root.selectedRow === row) return Theme.sideActiveBackground; if (root.selectedRow === row)
if (root.tableModel[row]?.highlight) return Theme.subtleSectionBackground; return Theme.sideActiveBackground;
if (root.tableModel[row]?.highlight)
return Theme.subtleSectionBackground;
return row % 2 === 0 ? Theme.cardBackground : Theme.tone100; return row % 2 === 0 ? Theme.cardBackground : Theme.tone100;
} }
@@ -297,9 +323,7 @@ Dialog {
background: Rectangle { background: Rectangle {
radius: Theme.radiusXs radius: Theme.radiusXs
color: parent.down ? Theme.buttonNeutralPressed color: parent.down ? Theme.buttonNeutralPressed : parent.hovered ? Theme.buttonNeutralHover : Theme.buttonNeutralBackground
: parent.hovered ? Theme.buttonNeutralHover
: Theme.buttonNeutralBackground
border.width: Theme.borderThin border.width: Theme.borderThin
border.color: Theme.fieldBorder border.color: Theme.fieldBorder
} }
@@ -325,15 +349,14 @@ Dialog {
font.pixelSize: 12 font.pixelSize: 12
onClicked: { onClicked: {
const record = root.tableModel[row]; const record = root.tableModel[row];
if (!record) return; if (!record)
return;
file_browser.saveMetadata(record.fileName ?? "", record.wafer ?? "", record.date ?? "", record.chamber ?? "", record.notes ?? "", record.selected ?? false); file_browser.saveMetadata(record.fileName ?? "", record.wafer ?? "", record.date ?? "", record.chamber ?? "", record.notes ?? "", record.selected ?? false);
} }
background: Rectangle { background: Rectangle {
radius: Theme.radiusXs radius: Theme.radiusXs
color: parent.down ? Theme.buttonNeutralPressed color: parent.down ? Theme.buttonNeutralPressed : parent.hovered ? Theme.buttonNeutralHover : Theme.buttonNeutralBackground
: parent.hovered ? Theme.buttonNeutralHover
: Theme.buttonNeutralBackground
border.width: Theme.borderThin border.width: Theme.borderThin
border.color: Theme.fieldBorder border.color: Theme.fieldBorder
} }
@@ -359,9 +382,12 @@ Dialog {
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
text: { text: {
const record = root.tableModel[row] || {}; const record = root.tableModel[row] || {};
if (column === 1) return record.wafer ?? ""; if (column === 1)
if (column === 2) return record.date ?? ""; return record.wafer ?? "";
if (column === 5) return record.fileName ?? ""; if (column === 2)
return record.date ?? "";
if (column === 5)
return record.fileName ?? "";
return ""; return "";
} }
elide: Text.ElideRight elide: Text.ElideRight
@@ -381,7 +407,8 @@ Dialog {
verticalAlignment: Text.AlignVCenter verticalAlignment: Text.AlignVCenter
text: { text: {
const record = root.tableModel[row] || {}; const record = root.tableModel[row] || {};
if (column === 3) return record.chamber ?? ""; if (column === 3)
return record.chamber ?? "";
return record.notes ?? ""; return record.notes ?? "";
} }
elide: Text.ElideRight elide: Text.ElideRight
@@ -496,7 +523,11 @@ Dialog {
anchors.fill: parent anchors.fill: parent
spacing: Theme.settingsGridSpacing spacing: Theme.settingsGridSpacing
Label { text: qsTr("File"); font.bold: true; color: Theme.headingColor } Label {
text: qsTr("File")
font.bold: true
color: Theme.headingColor
}
Text { Text {
id: filePathLabel id: filePathLabel
@@ -509,17 +540,45 @@ Dialog {
font.pixelSize: 12 font.pixelSize: 12
} }
Label { text: qsTr("Wafer"); color: Theme.headingColor } Label {
DialogTextInput { id: waferField; Layout.fillWidth: true; Layout.preferredHeight: 36 } text: qsTr("Wafer")
color: Theme.headingColor
}
DialogTextInput {
id: waferField
Layout.fillWidth: true
Layout.preferredHeight: 36
}
Label { text: qsTr("Date"); color: Theme.headingColor } Label {
DialogTextInput { id: dateField; Layout.fillWidth: true; Layout.preferredHeight: 36 } text: qsTr("Date")
color: Theme.headingColor
}
DialogTextInput {
id: dateField
Layout.fillWidth: true
Layout.preferredHeight: 36
}
Label { text: qsTr("Chamber"); color: Theme.headingColor } Label {
DialogTextInput { id: chamberField; Layout.fillWidth: true; Layout.preferredHeight: 36 } text: qsTr("Chamber")
color: Theme.headingColor
}
DialogTextInput {
id: chamberField
Layout.fillWidth: true
Layout.preferredHeight: 36
}
Label { text: qsTr("Notes"); color: Theme.headingColor } Label {
DialogTextInput { id: notesField; Layout.fillWidth: true; Layout.preferredHeight: 36 } text: qsTr("Notes")
color: Theme.headingColor
}
DialogTextInput {
id: notesField
Layout.fillWidth: true
Layout.preferredHeight: 36
}
CheckBox { CheckBox {
id: selectedField id: selectedField
@@ -531,7 +590,9 @@ Dialog {
Layout.topMargin: 8 Layout.topMargin: 8
spacing: 8 spacing: 8
Item { Layout.fillWidth: true } Item {
Layout.fillWidth: true
}
DialogActionButton { DialogActionButton {
text: qsTr("Cancel") text: qsTr("Cancel")
+3 -2
View File
@@ -419,7 +419,9 @@ Item {
opacity: (settingsScroll.ScrollBar.vertical.position <= 0 && settingsScroll.contentHeight > settingsScroll.height) ? (scrollHintTimer.running ? 1 : 0) : 0 opacity: (settingsScroll.ScrollBar.vertical.position <= 0 && settingsScroll.contentHeight > settingsScroll.height) ? (scrollHintTimer.running ? 1 : 0) : 0
Behavior on opacity { Behavior on opacity {
NumberAnimation { duration: 400 } NumberAnimation {
duration: 400
}
} }
} }
} }
@@ -432,5 +434,4 @@ Item {
repeat: false repeat: false
onTriggered: scrollHint.visible = false onTriggered: scrollHint.visible = false
} }
} }
+104 -52
View File
@@ -2,44 +2,120 @@ pragma Singleton
import QtQuick import QtQuick
// ===== Shared Theme Tokens ===== // =============================================================================
// Theme — single source of truth for colors and sizing
// =============================================================================
// Sections:
// 1. Mode flag
// 2. Tone palette (raw tones; everything else derives from these)
// 3. Surfaces (page / card / panel / workspace backgrounds)
// 4. Borders
// 5. Text (headings, body, panel titles)
// 6. Controls (fields, buttons, checkbox)
// 7. Tracks (pill toggle + scrollbar)
// 8. Tabs (footer tab bar)
// 9. Side rail
// 10. Status (success / warning / error)
// 11. Geometry (radius / border width / spacing / sizing)
// =============================================================================
QtObject { QtObject {
// ===== Theme Mode ===== // ── 1. Mode ──────────────────────────────────────────────────────────────
// Toggle this to preview the same shell in light mode. property bool isDarkMode: false
property bool isDarkMode: true
// ===== Surface Colors ===== // ── 2. Tone palette (base values; consume via semantic tokens below) ─────
// Core application surfaces. readonly property color tone100: isDarkMode ? "#111111" : "#FAFAFA"
readonly property color pageBackground: isDarkMode ? "#11161c" : "#eef4f7" readonly property color tone200: isDarkMode ? "#1A1A1A" : "#F2F2F2"
readonly property color cardBackground: isDarkMode ? "#1b232d" : "#ffffff" readonly property color tone300: isDarkMode ? "#242424" : "#E8E8E8"
readonly property color workspaceBackground: isDarkMode ? "#121923" : "#ffffff" readonly property color toneText: isDarkMode ? "#F2F2F2" : "#111111"
readonly property color sideRailBackground: isDarkMode ? "#18202a" : "#f5f7fa" readonly property color toneMute: isDarkMode ? "#A8A8A8" : "#8A8A8A"
readonly property color responseBackground: isDarkMode ? "#18212a" : "#f9fbfc" readonly property color toneBorder: isDarkMode ? "#2B2B2B" : "#D8D8D8"
// Shared border and text colors. // ── 3. Surfaces ──────────────────────────────────────────────────────────
readonly property color cardBorder: isDarkMode ? "#334155" : "#cbd5e1" readonly property color pageBackground: tone100
readonly property color workspaceBorder: isDarkMode ? "#334155" : "#d7dde5" readonly property color cardBackground: tone200
readonly property color responseBorder: isDarkMode ? "#334155" : "#d5e0e8" readonly property color panelBackground: tone200
readonly property color workspaceBackground: tone200
readonly property color responseBackground: tone200
readonly property color subtleSectionBackground: tone300
readonly property color headingColor: isDarkMode ? "#e5eef7" : "#12324a" // ── 4. Borders ───────────────────────────────────────────────────────────
readonly property color bodyColor: isDarkMode ? "#afbdca" : "#415a6b" readonly property color cardBorder: toneBorder
readonly property color buttonPressed: isDarkMode ? "#214d60" : "#17576c" readonly property color responseBorder: toneBorder
readonly property color statusSuccessColor: isDarkMode ? "#7cd67e" : "#1f7a33" readonly property color workspaceBorder: toneBorder
readonly property color statusErrorColor: isDarkMode ? "#ff8a80" : "#c62828" readonly property color innerFrameBorder: toneBorder
readonly property color statusWarningColor: isDarkMode ? "#f3c677" : "#9a6700" readonly property color outerFrameBorder: isDarkMode ? "#343434" : "#CECECE"
readonly property color softBorder: isDarkMode ? "#222222" : "#E2E2E2"
// ===== Side Rail Sizing ===== // ── 5. Text ──────────────────────────────────────────────────────────────
readonly property color headingColor: toneText
readonly property color bodyColor: toneMute
readonly property color panelTitleText: toneMute
// ── 6. Controls ──────────────────────────────────────────────────────────
// Fields
readonly property color fieldBackground: tone100
readonly property color fieldText: toneText
readonly property color fieldPlaceholder: toneMute
readonly property color fieldBorder: toneBorder
readonly property color fieldBorderFocus: toneText
// Neutral buttons
readonly property color buttonNeutralBackground: tone300
readonly property color buttonNeutralHover: isDarkMode ? "#303030" : "#DDDDDD"
readonly property color buttonNeutralPressed: tone100
readonly property color buttonNeutralText: toneText
readonly property color buttonPressed: tone300
readonly property color primaryAccent: toneText
// Checkbox
readonly property color checkboxText: toneText
// ── 7. Tracks (pill toggle, scrollbar thumb) ─────────────────────────────
readonly property color trackBackground: isDarkMode ? "#25252B" : "#D1D5DB"
// ── 8. Tabs (footer tab bar) ─────────────────────────────────────────────
readonly property color tabBarBackground: tone200
readonly property color tabBackground: "transparent"
readonly property color tabActiveBackground: "transparent"
readonly property color tabHoverBackground: tone300
readonly property color tabBorder: "transparent"
readonly property color tabText: toneMute
readonly property color tabActiveText: toneText
// ── 9. Side rail ─────────────────────────────────────────────────────────
readonly property color sideRailBackground: tone200
readonly property color sideActiveBackground: tone300
// ── 10. Status ───────────────────────────────────────────────────────────
readonly property color statusSuccessColor: isDarkMode ? "#63D471" : "#2E9E44"
readonly property color statusWarningColor: isDarkMode ? "#F5C15C" : "#C88A18"
readonly property color statusErrorColor: isDarkMode ? "#FF6B6B" : "#D64545"
// ── 11. Geometry ─────────────────────────────────────────────────────────
// Radius
readonly property int radiusXs: 4 // fields, tight elements
readonly property int radiusSm: 6 // buttons
readonly property int radiusMd: 10 // cards, group boxes
readonly property int radiusLg: 14 // large panels / dialogs
// Border width
readonly property int borderThin: 1
readonly property int borderStrong: 2
// Main panel layout
readonly property int mainAreaPadding: 10
readonly property int rightPaneGap: 6
readonly property int panelPadding: 12
// Side rail layout
readonly property int sideRailWidth: 190 readonly property int sideRailWidth: 190
readonly property int sideRailSpacing: 14 readonly property int sideRailSpacing: 14
readonly property int sideButtonHeight: 146 readonly property int sideButtonHeight: 146
readonly property int sideButtonMinHeight: 88 readonly property int sideButtonMinHeight: 88
readonly property int panelPadding: 12 // Tab bar layout
// ===== Main Panel Spacing =====
// Main content spacing.
readonly property int mainAreaPadding: 10
readonly property int rightPaneGap: 6
readonly property int tabBarHeight: 34 readonly property int tabBarHeight: 34
readonly property int tabBarPadding: 6
readonly property int tabSpacing: 2
readonly property int tabRadius: 2
readonly property int tabFontSize: 12
readonly property int tabButtonMinWidth: 80
// Settings page layout
readonly property int settingsPanelMaxWidth: 760 readonly property int settingsPanelMaxWidth: 760
readonly property int settingsOuterMargin: 40 readonly property int settingsOuterMargin: 40
readonly property int settingsSectionSpacing: 20 readonly property int settingsSectionSpacing: 20
@@ -48,28 +124,4 @@ QtObject {
readonly property int settingsButtonHeight: 40 readonly property int settingsButtonHeight: 40
readonly property int settingsActionWidth: 200 readonly property int settingsActionWidth: 200
readonly property int settingsFieldMinWidth: 160 readonly property int settingsFieldMinWidth: 160
// ===== Footer Tab Theme =====
// Bottom tab styling.
readonly property color tabBarBackground: isDarkMode ? "#161d26" : "#f2f2f2"
readonly property color tabBackground: isDarkMode ? "#202833" : "#ececec"
readonly property color tabActiveBackground: isDarkMode ? "#2a3440" : "#ffffff"
readonly property color tabHoverBackground: isDarkMode ? "#263140" : "#f7f7f7"
readonly property color tabBorder: isDarkMode ? "#3a4758" : "#cfcfcf"
readonly property color tabText: isDarkMode ? "#c6d0da" : "#222222"
readonly property color tabActiveText: isDarkMode ? "#ffffff" : "#111111"
readonly property int tabButtonMinWidth: 80
readonly property int tabHorizontalPadding: 14
readonly property int tabBarPadding: 6
readonly property int tabSpacing: 2
readonly property int tabRadius: 2
readonly property int tabFontSize: 12
// ===== Form Control Theme =====
readonly property color inputBackground: isDarkMode ? "#202833" : "#ffffff"
readonly property color inputBorder: isDarkMode ? "#4a596d" : "#cfcfcf"
readonly property color buttonBackground: isDarkMode ? "#202833" : "#ffffff"
readonly property color buttonText: isDarkMode ? "#e5eef7" : "#111111"
readonly property color checkboxText: isDarkMode ? "#d6dfeb" : "#111111"
} }
+230
View File
@@ -0,0 +1,230 @@
from __future__ import annotations
import json
import re
from datetime import datetime
from pathlib import Path
from PySide6.QtCore import QObject, Property, QStandardPaths, Signal, Slot
from PySide6.QtWidgets import QFileDialog, QMessageBox
from backend.csv_file_metadata import CSVFileMetadata
from backend.zwafer_parser import ZWaferParser
# ===== File Browser Model =====
class FileBrowser(QObject):
# ===== Change Signals =====
filesChanged = Signal()
currentDirectoryChanged = Signal()
# ===== Lifecycle =====
def __init__(self, parent: QObject | None = None) -> None:
super().__init__(parent)
self._parser = ZWaferParser()
self._files: list[dict[str, object]] = []
self._current_directory = self._resolve_default_directory()
self._refresh_files(show_empty_message=False)
# ===== Exposed Properties =====
@Property("QVariantList", notify=filesChanged)
def files(self) -> list[dict[str, object]]:
return [dict(row) for row in self._files]
@Property(str, notify=currentDirectoryChanged)
def currentDirectory(self) -> str:
return str(self._current_directory)
# ===== Directory Selection =====
@Slot()
def chooseDirectory(self) -> None:
selected_dir = QFileDialog.getExistingDirectory(
None,
"Select CSV Folder",
self.currentDirectory,
)
if not selected_dir:
return
self._set_current_directory(Path(selected_dir))
self._refresh_files(show_empty_message=True)
@Slot()
def refreshFiles(self) -> None:
self._refresh_files(show_empty_message=False)
# ===== Metadata Persistence =====
@Slot(str, str, str, str, str, bool)
def saveMetadata(
self,
file_path: str,
wafer: str,
date: str,
chamber: str,
notes: str,
selected: bool,
) -> None:
csv_path = Path(file_path)
if not csv_path.exists():
self._show_error(f"CSV file was not found:\n{file_path}")
return
row = self._find_row(file_path)
if row is None:
self._show_error("The selected CSV row is no longer available.")
return
row["wafer"] = wafer
row["date"] = date
row["chamber"] = chamber
row["notes"] = notes
row["selected"] = selected
payload = {
"wafer": wafer,
"date": date,
"chamber": chamber,
"notes": notes,
}
try:
sidecar_path = self._sidecar_path(csv_path)
sidecar_path.write_text(json.dumps(payload, indent=4), encoding="utf-8")
except Exception as exc: # pragma: no cover - defensive UI path
self._show_error(f"Failed to save metadata:\n{exc}")
return
self.filesChanged.emit()
self._show_info(f"Saved metadata for:\n{csv_path.name}")
# ===== Internal Data Loading =====
def _refresh_files(self, show_empty_message: bool) -> None:
selected_state = {
str(row.get("fileName", "")): bool(row.get("selected", False))
for row in self._files
}
self._files = []
if not self._current_directory.exists():
self.filesChanged.emit()
return
for csv_path in sorted(self._current_directory.glob("*.csv")):
try:
metadata = self._load_metadata(csv_path)
self._files.append(
{
"selected": selected_state.get(str(csv_path), False),
"wafer": metadata.wafer,
"date": metadata.string_date_format(),
"chamber": metadata.chamber,
"notes": metadata.notes,
"fileName": str(csv_path),
"highlight": metadata.get_wafer_type() in {"A", "B", "C"},
}
)
except Exception:
self._files.append(
{
"selected": selected_state.get(str(csv_path), False),
"wafer": csv_path.stem,
"date": "",
"chamber": "",
"notes": "Unable to parse metadata",
"fileName": str(csv_path),
"highlight": False,
}
)
self.filesChanged.emit()
if show_empty_message and not self._files:
self._show_info("No CSV files were found in the selected folder.")
def _load_metadata(self, csv_path: Path) -> CSVFileMetadata:
sidecar_path = self._sidecar_path(csv_path)
if sidecar_path.exists():
try:
payload = json.loads(sidecar_path.read_text(encoding="utf-8"))
return CSVFileMetadata(
wafer=str(payload.get("wafer", "")),
date=str(payload.get("date", "")),
chamber=str(payload.get("chamber", "")),
notes=str(payload.get("notes", "")),
filename=str(csv_path),
)
except Exception:
# Fall back to CSV/header parsing if sidecar is malformed.
pass
parser_data, _ = self._parser.parse(str(csv_path))
if parser_data is not None:
wafer = parser_data.serial or ""
date_text = ""
if parser_data.date != datetime.min:
date_text = CSVFileMetadata.format_date(parser_data.date)
return CSVFileMetadata(
wafer=wafer,
date=date_text,
chamber="",
notes="",
filename=str(csv_path),
)
return self._metadata_from_filename(csv_path)
def _metadata_from_filename(self, csv_path: Path) -> CSVFileMetadata:
match = re.match(
r"^(?P<wafer>[^-]+)-(?P<date>\d{8})(?:_(?P<run>\d+))?$",
csv_path.stem,
)
if not match:
return CSVFileMetadata(filename=str(csv_path))
date_text = ""
try:
parsed = datetime.strptime(match.group("date"), "%Y%m%d")
date_text = CSVFileMetadata.format_date(parsed)
except ValueError:
date_text = ""
return CSVFileMetadata(
wafer=match.group("wafer"),
date=date_text,
filename=str(csv_path),
)
# ===== Internal Helpers =====
def _resolve_default_directory(self) -> Path:
documents_dir = QStandardPaths.writableLocation(
QStandardPaths.DocumentsLocation
)
base_dir = Path(documents_dir) if documents_dir else (Path.home() / "Documents")
return base_dir / "isc_data"
def _set_current_directory(self, directory: Path) -> None:
normalized = Path(directory)
if normalized == self._current_directory:
return
self._current_directory = normalized
self.currentDirectoryChanged.emit()
def _find_row(self, file_path: str) -> dict[str, object] | None:
for row in self._files:
if str(row.get("fileName", "")) == file_path:
return row
return None
def _sidecar_path(self, csv_path: Path) -> Path:
return csv_path.with_suffix(f"{csv_path.suffix}.json")
def _show_error(self, message: str) -> None:
QMessageBox.critical(None, "iSenseCloud", message)
def _show_info(self, message: str) -> None:
QMessageBox.information(None, "iSenseCloud", message)
# Backward-compatible alias while the QML dialog is still named SelectFileDialog.
SelectFileDialogModel = FileBrowser
+22 -2
View File
@@ -1,16 +1,36 @@
import sys import sys
from pathlib import Path from pathlib import Path
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine from PySide6.QtQml import QQmlApplicationEngine
from PySide6.QtQuickControls2 import QQuickStyle
from PySide6.QtWidgets import QApplication
from backend.local_settings_model import LocalSettingsModel
from backend.file_browser import FileBrowser
# ===== Application Entry Point =====
if __name__ == "__main__": if __name__ == "__main__":
# ===== UI Style Setup =====
# Use a non-native controls style so our custom QML button backgrounds are supported.
QQuickStyle.setStyle("Basic")
# ===== Qt Bootstrap =====
# Minimal Qt bootstrap: load the ISC QML module and exit if it fails. # Minimal Qt bootstrap: load the ISC QML module and exit if it fails.
app = QGuiApplication(sys.argv) app = QApplication(sys.argv)
engine = QQmlApplicationEngine() engine = QQmlApplicationEngine()
# ===== Shared Models =====
settings_model = LocalSettingsModel()
select_file_dialog_model = FileBrowser()
settings_model.loadSettings()
engine.rootContext().setContextProperty("settingsModel", settings_model)
engine.rootContext().setContextProperty("file_browser", select_file_dialog_model)
# ===== QML Startup =====
engine.addImportPath(Path(__file__).parent) engine.addImportPath(Path(__file__).parent)
engine.loadFromModule("ISC", "Main") engine.loadFromModule("ISC", "Main")
# ===== Exit Handling =====
if not engine.rootObjects(): if not engine.rootObjects():
sys.exit(-1) sys.exit(-1)
+1
View File
@@ -5,3 +5,4 @@ name = "PySide QtQuick Project"
# ===== PySide Build Inputs ===== # ===== PySide Build Inputs =====
[tool.pyside6-project] [tool.pyside6-project]
files = ["ISC/HomePage.qml", "ISC/Main.qml", "ISC/Tabs/SelectFileDialog.qml", "ISC/Tabs/SettingsTab.qml", "ISC/Tabs/qmldir", "ISC/Theme.qml", "ISC/qmldir", "backend/file_browser.py", "backend/local_settings.py", "backend/local_settings_model.py", "main.py"] files = ["ISC/HomePage.qml", "ISC/Main.qml", "ISC/Tabs/SelectFileDialog.qml", "ISC/Tabs/SettingsTab.qml", "ISC/Tabs/qmldir", "ISC/Theme.qml", "ISC/qmldir", "backend/file_browser.py", "backend/local_settings.py", "backend/local_settings_model.py", "main.py"]