《PySide6 GUI开发指南:QML核心与实践》 第五篇:Python与QML深度融合——数据绑定与交互

前言:跨越语言的桥梁

在前四篇中,我们分别学习了QML语法基础、响应式UI设计、动画系统以及组件化架构。现在,我们来到了本系列最核心、最强大的部分------Python与QML的深度融合。真正的全栈GUI开发,不仅仅是让界面看起来漂亮,更重要的是如何将强大的Python后端逻辑与灵活的QML前端界面无缝结合。

在现代企业级应用中,数据是核心,交互是灵魂。Python凭借其丰富的数据处理库和成熟的生态系统,成为数据处理的不二选择;而QML则以其声明式语法和流畅的动画效果,为用户提供卓越的交互体验。两者的结合,就像大脑与感官的完美协作。

通过本篇学习,你将掌握如何构建真正的数据驱动应用,实现前后端的无缝通信,打造既美观又强大的桌面应用程序。

本篇学习目标

完成本篇学习后,你将能够:

  • 深入理解PySide6与QML的完整通信机制

  • 掌握多种数据绑定策略及其适用场景

  • 实现Python与QML之间的双向实时数据同步

  • 构建复杂的Model-View架构数据处理系统

  • 处理异步操作和多线程环境下的数据交互

  • 设计可扩展的前后端分离架构

  • 优化大数据量场景下的性能表现

  • 构建完整的企业级数据管理系统

  • 实现错误处理和调试机制

  • 掌握组件间复杂的数据流管理

知识地图:Python-QML集成全景

1. 深度集成架构解析

1.1 集成架构总览

Python与QML的集成不是简单的函数调用,而是一个完整的系统架构。让我们先通过架构图理解整个集成体系:

1.2 通信机制对比

在深入代码之前,让我们先理解Python与QML之间的各种通信方式及其适用场景:

通信机制详细对比表:

2. 核心集成技术详解

2.1 上下文属性:全局数据共享

上下文属性是最基础的集成方式,适合传递全局配置和共享数据。让我们通过一个完整的示例来理解:

python 复制代码
# backend.py - Python后端实现
import sys
import os
from datetime import datetime
from typing import Dict, Any, List, Optional
from dataclasses import dataclass, field, asdict
from enum import Enum
import json

from PySide6.QtCore import (
    QObject, 
    Slot, 
    Property, 
    Signal, 
    QUrl,
    QTimer,
    QThread,
    QThreadPool,
    QRunnable,
    QMetaObject,
    Qt
)
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine, QmlElement


# 注册QML类型
QML_IMPORT_NAME = "Dashboard.Backend"
QML_IMPORT_MAJOR_VERSION = 1


@dataclass
class UserProfile:
    """用户信息数据类"""
    username: str
    email: str
    role: str
    avatar_url: str = ""
    last_login: Optional[datetime] = None
    preferences: Dict[str, Any] = field(default_factory=dict)
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        data = asdict(self)
        if self.last_login:
            data['last_login'] = self.last_login.isoformat()
        return data


class ThemeMode(Enum):
    """主题模式枚举"""
    LIGHT = "light"
    DARK = "dark"
    AUTO = "auto"


@QmlElement
class ApplicationConfig(QObject):
    """应用配置管理器"""
    
    # 信号定义
    themeChanged = Signal(str)
    languageChanged = Signal(str)
    configChanged = Signal(dict)
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self._theme = ThemeMode.AUTO.value
        self._language = "zh_CN"
        self._config = {
            "auto_save": True,
            "check_updates": True,
            "notifications": True,
            "animation_enabled": True,
            "font_size": 14
        }
        
    # 主题属性
    @Property(str, notify=themeChanged)
    def theme(self) -> str:
        return self._theme
    
    @theme.setter
    def theme(self, value: str):
        if self._theme != value and value in [t.value for t in ThemeMode]:
            self._theme = value
            self.themeChanged.emit(value)
            self._save_config()
    
    # 语言属性
    @Property(str, notify=languageChanged)
    def language(self) -> str:
        return self._language
    
    @language.setter
    def language(self, value: str):
        if self._language != value:
            self._language = value
            self.languageChanged.emit(value)
            self._save_config()
    
    # 配置属性
    @Property(dict, notify=configChanged)
    def config(self) -> Dict[str, Any]:
        return self._config.copy()
    
    @config.setter
    def config(self, value: Dict[str, Any]):
        if self._config != value:
            self._config.update(value)
            self.configChanged.emit(self._config)
            self._save_config()
    
    @Slot(str, result=bool)
    def get_config_value(self, key: str) -> bool:
        """获取配置值"""
        return self._config.get(key, False)
    
    @Slot(str, bool)
    def set_config_value(self, key: str, value: bool):
        """设置配置值"""
        if self._config.get(key) != value:
            self._config[key] = value
            self.configChanged.emit(self._config)
            self._save_config()
    
    def _save_config(self):
        """保存配置到文件(模拟)"""
        config_data = {
            "theme": self._theme,
            "language": self._language,
            "config": self._config
        }
        # 实际应用中这里应该保存到文件
        print(f"配置已保存: {config_data}")


@QmlElement
class UserManager(QObject):
    """用户管理器"""
    
    # 信号定义
    userLoggedIn = Signal(dict)
    userLoggedOut = Signal()
    profileUpdated = Signal(dict)
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self._current_user: Optional[UserProfile] = None
        self._is_logged_in = False
        
    # 当前用户属性
    @Property(bool, notify=userLoggedIn)
    def isLoggedIn(self) -> bool:
        return self._is_logged_in
    
    @Property(dict, notify=userLoggedIn)
    def currentUser(self) -> Dict[str, Any]:
        if self._current_user:
            return self._current_user.to_dict()
        return {}
    
    @Slot(str, str, result=bool)
    def login(self, username: str, password: str) -> bool:
        """用户登录"""
        # 模拟登录验证
        if username and password:
            self._current_user = UserProfile(
                username=username,
                email=f"{username}@example.com",
                role="user",
                avatar_url=f"https://api.dicebear.com/7.x/avataaars/svg?seed={username}",
                last_login=datetime.now(),
                preferences={"theme": "auto", "language": "zh_CN"}
            )
            self._is_logged_in = True
            self.userLoggedIn.emit(self._current_user.to_dict())
            return True
        return False
    
    @Slot()
    def logout(self):
        """用户退出"""
        self._current_user = None
        self._is_logged_in = False
        self.userLoggedOut.emit()
    
    @Slot(dict)
    def update_profile(self, profile_data: Dict[str, Any]):
        """更新用户资料"""
        if self._current_user:
            for key, value in profile_data.items():
                if hasattr(self._current_user, key):
                    setattr(self._current_user, key, value)
            self.profileUpdated.emit(self._current_user.to_dict())

上下文属性注册架构:

对应的QML前端代码:

javascript 复制代码
// Main.qml - 主界面
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import Dashboard.Backend 1.0

ApplicationWindow {
    id: window
    width: 1200
    height: 800
    visible: true
    title: "企业数据管理系统"
    
    // 通过上下文属性访问Python对象
    property var config: Backend.config
    property var userManager: Backend.userManager
    
    // 主题管理
    property color backgroundColor: config.theme === "dark" ? "#1a1a2e" : "#f5f5f5"
    property color textColor: config.theme === "dark" ? "#ffffff" : "#333333"
    
    // 监听主题变化
    Connections {
        target: config
        function onThemeChanged(theme) {
            console.log("主题已切换:", theme)
            updateTheme()
        }
    }
    
    // 监听登录状态
    Connections {
        target: userManager
        function onUserLoggedIn(userData) {
            console.log("用户登录:", userData.username)
            showMainContent()
        }
        
        function onUserLoggedOut() {
            console.log("用户退出")
            showLoginForm()
        }
    }
    
    // 主布局
    StackLayout {
        id: mainStack
        anchors.fill: parent
        currentIndex: userManager.isLoggedIn ? 1 : 0
        
        // 登录界面
        LoginPage {
            config: window.config
            userManager: window.userManager
        }
        
        // 主内容界面
        MainContent {
            config: window.config
            userManager: window.userManager
        }
    }
    
    function updateTheme() {
        // 更新主题相关样式
        window.color = backgroundColor
    }
    
    function showMainContent() {
        mainStack.currentIndex = 1
    }
    
    function showLoginForm() {
        mainStack.currentIndex = 0
    }
    
    Component.onCompleted: {
        console.log("应用启动完成")
        updateTheme()
    }
}

2.2 双向数据绑定:响应式数据流

双向数据绑定是构建响应式应用的核心。让我们深入理解其工作原理:

双向绑定示例代码:

python 复制代码
# data_binding.py - 双向绑定示例
from PySide6.QtCore import QObject, Property, Signal, Slot
from typing import Any, List, Dict
import json


@QmlElement
class DataModel(QObject):
    """数据模型 - 演示双向绑定"""
    
    # 信号定义
    dataChanged = Signal(dict)
    itemsChanged = Signal(list)
    filterChanged = Signal(str)
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self._data = {
            "name": "示例数据",
            "value": 100,
            "enabled": True,
            "options": ["选项1", "选项2", "选项3"]
        }
        self._items = []
        self._filter_text = ""
        self._selected_item = None
        
        # 初始化数据
        self._load_initial_data()
    
    # ----- 基本数据绑定 -----
    @Property(str, notify=dataChanged)
    def name(self) -> str:
        return self._data.get("name", "")
    
    @name.setter
    def name(self, value: str):
        if self._data.get("name") != value:
            self._data["name"] = value
            self.dataChanged.emit(self._data)
    
    @Property(int, notify=dataChanged)
    def value(self) -> int:
        return self._data.get("value", 0)
    
    @value.setter
    def value(self, value: int):
        if self._data.get("value") != value:
            self._data["value"] = value
            self.dataChanged.emit(self._data)
    
    @Property(bool, notify=dataChanged)
    def enabled(self) -> bool:
        return self._data.get("enabled", False)
    
    @enabled.setter
    def enabled(self, value: bool):
        if self._data.get("enabled") != value:
            self._data["enabled"] = value
            self.dataChanged.emit(self._data)
    
    # ----- 列表数据绑定 -----
    @Property(list, notify=itemsChanged)
    def items(self) -> List[Dict[str, Any]]:
        return self._items
    
    @items.setter
    def items(self, value: List[Dict[str, Any]]):
        if self._items != value:
            self._items = value
            self.itemsChanged.emit(self._items)
    
    # ----- 计算属性 -----
    @Property(int, notify=itemsChanged)
    def itemCount(self) -> int:
        return len(self._items)
    
    @Property(int, notify=dataChanged)
    def doubleValue(self) -> int:
        return self._data.get("value", 0) * 2
    
    # ----- 过滤功能 -----
    @Property(str, notify=filterChanged)
    def filterText(self) -> str:
        return self._filter_text
    
    @filterText.setter
    def filterText(self, value: str):
        if self._filter_text != value:
            self._filter_text = value
            self.filterChanged.emit(value)
            self._apply_filter()
    
    @Property(list, notify=itemsChanged)
    def filteredItems(self) -> List[Dict[str, Any]]:
        if not self._filter_text:
            return self._items
        
        return [
            item for item in self._items
            if self._filter_text.lower() in json.dumps(item).lower()
        ]
    
    # ----- 公共方法 -----
    @Slot(result=bool)
    def validate_data(self) -> bool:
        """验证数据"""
        if not self._data.get("name", "").strip():
            return False
        if self._data.get("value", 0) < 0:
            return False
        return True
    
    @Slot(str, int, bool)
    def update_data(self, name: str, value: int, enabled: bool):
        """批量更新数据"""
        changed = False
        
        if self._data.get("name") != name:
            self._data["name"] = name
            changed = True
        
        if self._data.get("value") != value:
            self._data["value"] = value
            changed = True
        
        if self._data.get("enabled") != enabled:
            self._data["enabled"] = enabled
            changed = True
        
        if changed:
            self.dataChanged.emit(self._data)
    
    @Slot(dict)
    def add_item(self, item: Dict[str, Any]):
        """添加项目"""
        self._items.append(item)
        self.itemsChanged.emit(self._items)
    
    @Slot(int)
    def remove_item(self, index: int):
        """删除项目"""
        if 0 <= index < len(self._items):
            self._items.pop(index)
            self.itemsChanged.emit(self._items)
    
    # ----- 私有方法 -----
    def _load_initial_data(self):
        """加载初始数据"""
        self._items = [
            {"id": 1, "name": "项目A", "value": 100, "status": "active"},
            {"id": 2, "name": "项目B", "value": 200, "status": "pending"},
            {"id": 3, "name": "项目C", "value": 300, "status": "completed"},
            {"id": 4, "name": "项目D", "value": 400, "status": "active"},
            {"id": 5, "name": "项目E", "value": 500, "status": "cancelled"},
        ]
    
    def _apply_filter(self):
        """应用过滤条件"""
        # 过滤逻辑已在filteredItems属性中实现
        pass

双向绑定QML界面:

javascript 复制代码
// DataBindingDemo.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import Dashboard.Backend 1.0

ApplicationWindow {
    id: window
    width: 1000
    height: 700
    visible: true
    title: "双向数据绑定演示"
    
    // 数据模型
    property var dataModel: Backend.dataModel
    
    // 主布局
    SplitView {
        anchors.fill: parent
        orientation: Qt.Horizontal
        
        // 左侧:数据编辑区
        ScrollView {
            SplitView.preferredWidth: 400
            
            ColumnLayout {
                width: parent.width
                spacing: 20
                padding: 20
                
                // 标题
                Label {
                    text: "数据编辑"
                    font.pixelSize: 24
                    font.bold: true
                    Layout.fillWidth: true
                }
                
                // 名称输入
                GroupBox {
                    title: "基本属性"
                    Layout.fillWidth: true
                    
                    ColumnLayout {
                        width: parent.width
                        spacing: 10
                        
                        // 名称 - 双向绑定
                        RowLayout {
                            Label { text: "名称:"; Layout.preferredWidth: 80 }
                            TextField {
                                id: nameField
                                text: dataModel.name
                                placeholderText: "输入名称"
                                Layout.fillWidth: true
                                
                                // 绑定到Python属性
                                onTextChanged: {
                                    if (activeFocus) {
                                        dataModel.name = text
                                    }
                                }
                            }
                        }
                        
                        // 数值 - 双向绑定
                        RowLayout {
                            Label { text: "数值:"; Layout.preferredWidth: 80 }
                            Slider {
                                id: valueSlider
                                from: 0
                                to: 200
                                value: dataModel.value
                                stepSize: 1
                                Layout.fillWidth: true
                                
                                onValueChanged: {
                                    if (activeFocus) {
                                        dataModel.value = value
                                    }
                                }
                            }
                            Label {
                                text: dataModel.value
                                font.bold: true
                                Layout.preferredWidth: 40
                            }
                        }
                        
                        // 启用状态 - 双向绑定
                        RowLayout {
                            Label { text: "启用:"; Layout.preferredWidth: 80 }
                            Switch {
                                id: enabledSwitch
                                checked: dataModel.enabled
                                
                                onCheckedChanged: {
                                    if (activeFocus) {
                                        dataModel.enabled = checked
                                    }
                                }
                            }
                        }
                        
                        // 计算属性显示
                        RowLayout {
                            Label { 
                                text: "双倍数值:" 
                                Layout.preferredWidth: 80 
                            }
                            Label {
                                text: dataModel.doubleValue
                                font.bold: true
                                color: "green"
                            }
                        }
                    }
                }
                
                // 批量操作
                GroupBox {
                    title: "批量操作"
                    Layout.fillWidth: true
                    
                    ColumnLayout {
                        width: parent.width
                        spacing: 10
                        
                        Button {
                            text: "验证数据"
                            onClicked: {
                                var valid = dataModel.validate_data()
                                validationLabel.text = valid ? "✓ 数据有效" : "✗ 数据无效"
                                validationLabel.color = valid ? "green" : "red"
                            }
                        }
                        
                        Label {
                            id: validationLabel
                            text: ""
                        }
                        
                        Button {
                            text: "重置数据"
                            onClicked: {
                                dataModel.update_data("默认名称", 100, true)
                            }
                        }
                    }
                }
                
                Item { Layout.fillHeight: true }
            }
        }
        
        // 右侧:数据显示区
        ScrollView {
            SplitView.fillWidth: true
            
            ColumnLayout {
                width: parent.width
                spacing: 20
                padding: 20
                
                // 标题
                Label {
                    text: "数据展示"
                    font.pixelSize: 24
                    font.bold: true
                    Layout.fillWidth: true
                }
                
                // 当前数据展示
                GroupBox {
                    title: "当前数据"
                    Layout.fillWidth: true
                    
                    GridLayout {
                        columns: 2
                        rowSpacing: 10
                        columnSpacing: 20
                        width: parent.width
                        
                        Label { text: "名称:"; font.bold: true }
                        Label { text: dataModel.name }
                        
                        Label { text: "数值:"; font.bold: true }
                        Label { text: dataModel.value }
                        
                        Label { text: "启用状态:"; font.bold: true }
                        Label { 
                            text: dataModel.enabled ? "已启用" : "已禁用"
                            color: dataModel.enabled ? "green" : "red"
                        }
                        
                        Label { text: "双倍数值:"; font.bold: true }
                        Label { 
                            text: dataModel.doubleValue
                            color: "blue"
                            font.bold: true
                        }
                    }
                }
                
                // 数据列表
                GroupBox {
                    title: "数据列表 (共" + dataModel.itemCount + "项)"
                    Layout.fillWidth: true
                    
                    ColumnLayout {
                        width: parent.width
                        spacing: 10
                        
                        // 搜索过滤
                        RowLayout {
                            TextField {
                                id: searchField
                                placeholderText: "搜索..."
                                Layout.fillWidth: true
                                
                                // 绑定到过滤属性
                                text: dataModel.filterText
                                onTextChanged: {
                                    if (activeFocus) {
                                        dataModel.filterText = text
                                    }
                                }
                            }
                            
                            Label {
                                text: "找到 " + dataModel.filteredItems.length + " 项"
                                color: "#666"
                            }
                        }
                        
                        // 列表视图
                        ListView {
                            id: listView
                            model: dataModel.filteredItems
                            clip: true
                            implicitHeight: 300
                            Layout.fillWidth: true
                            
                            delegate: Rectangle {
                                width: ListView.view.width
                                height: 60
                                color: index % 2 ? "#f5f5f5" : "#ffffff"
                                border.color: "#e0e0e0"
                                border.width: 1
                                
                                RowLayout {
                                    anchors.fill: parent
                                    anchors.margins: 10
                                    spacing: 20
                                    
                                    ColumnLayout {
                                        spacing: 5
                                        Layout.fillWidth: true
                                        
                                        Text {
                                            text: modelData.name || "未命名"
                                            font.bold: true
                                            font.pixelSize: 16
                                        }
                                        
                                        Text {
                                            text: "ID: " + modelData.id + " | 值: " + modelData.value
                                            color: "#666"
                                            font.pixelSize: 12
                                        }
                                    }
                                    
                                    // 状态标签
                                    Rectangle {
                                        radius: 10
                                        color: {
                                            switch(modelData.status) {
                                            case "active": return "#4CAF50"
                                            case "pending": return "#FF9800"
                                            case "completed": return "#2196F3"
                                            case "cancelled": return "#F44336"
                                            default: return "#9E9E9E"
                                            }
                                        }
                                        width: 80
                                        height: 24
                                        
                                        Text {
                                            text: {
                                                switch(modelData.status) {
                                                case "active": return "活跃"
                                                case "pending": return "待处理"
                                                case "completed": return "已完成"
                                                case "cancelled": return "已取消"
                                                default: return "未知"
                                                }
                                            }
                                            color: "white"
                                            font.bold: true
                                            font.pixelSize: 12
                                            anchors.centerIn: parent
                                        }
                                    }
                                    
                                    // 删除按钮
                                    Button {
                                        text: "删除"
                                        highlighted: true
                                        onClicked: dataModel.remove_item(index)
                                    }
                                }
                            }
                            
                            // 空状态
                            Label {
                                visible: listView.count === 0
                                text: "没有数据"
                                anchors.centerIn: parent
                                color: "#999"
                            }
                        }
                        
                        // 添加新项目
                        RowLayout {
                            Button {
                                text: "添加项目"
                                icon.source: "qrc:/icons/add.svg"
                                onClicked: {
                                    var newItem = {
                                        "id": dataModel.itemCount + 1,
                                        "name": "新项目" + (dataModel.itemCount + 1),
                                        "value": Math.floor(Math.random() * 1000),
                                        "status": ["active", "pending", "completed", "cancelled"][Math.floor(Math.random() * 4)]
                                    }
                                    dataModel.add_item(newItem)
                                }
                            }
                            
                            Item { Layout.fillWidth: true }
                        }
                    }
                }
                
                Item { Layout.fillHeight: true }
            }
        }
    }
    
    // 状态监听
    Connections {
        target: dataModel
        
        function onDataChanged(newData) {
            console.log("数据已更新:", JSON.stringify(newData))
        }
        
        function onItemsChanged(newItems) {
            console.log("项目列表已更新,共", newItems.length, "项")
        }
        
        function onFilterChanged(filterText) {
            console.log("过滤条件:", filterText)
        }
    }
}

2.3 模型视图架构:高效数据展示

对于列表、表格等大量数据的展示,模型视图架构是最佳选择。让我们深入理解其工作原理:

QAbstractItemModel实现:

python 复制代码
# table_model.py - 表格数据模型
from PySide6.QtCore import (
    QObject, 
    QAbstractTableModel, 
    QModelIndex, 
    Qt,
    Slot,
    Signal
)
from PySide6.QtQml import QmlElement
from typing import Any, Dict, List, Optional
import pandas as pd
import numpy as np
from datetime import datetime, timedelta


@QmlElement
class DataFrameModel(QAbstractTableModel):
    """DataFrame表格模型"""
    
    # 信号定义
    dataChanged = Signal()
    sortRequested = Signal(int, int)  # 列, 顺序
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self._dataframe = pd.DataFrame()
        self._column_headers = []
        self._sort_column = -1
        self._sort_order = Qt.AscendingOrder
        
    # ----- 必须实现的方法 -----
    def rowCount(self, parent=QModelIndex()) -> int:
        """返回行数"""
        return len(self._dataframe)
    
    def columnCount(self, parent=QModelIndex()) -> int:
        """返回列数"""
        return len(self._dataframe.columns)
    
    def data(self, index: QModelIndex, role: int = Qt.DisplayRole) -> Any:
        """返回数据"""
        if not index.isValid():
            return None
        
        row = index.row()
        col = index.column()
        
        if role == Qt.DisplayRole:
            # 显示数据
            value = self._dataframe.iat[row, col]
            
            # 处理特殊类型
            if pd.isna(value):
                return ""
            elif isinstance(value, (datetime, pd.Timestamp)):
                return value.strftime("%Y-%m-%d %H:%M")
            elif isinstance(value, float):
                return f"{value:.4f}"
            else:
                return str(value)
                
        elif role == Qt.EditRole:
            # 编辑数据
            return self._dataframe.iat[row, col]
            
        elif role == Qt.TextAlignmentRole:
            # 文本对齐
            value = self._dataframe.iat[row, col]
            if isinstance(value, (int, float, np.number)):
                return Qt.AlignRight | Qt.AlignVCenter
            else:
                return Qt.AlignLeft | Qt.AlignVCenter
                
        elif role == Qt.BackgroundRole:
            # 背景色
            value = self._dataframe.iat[row, col]
            if isinstance(value, (int, float)) and value < 0:
                return "#FFEBEE"  # 红色背景表示负数
            return None
            
        elif role == Qt.ForegroundRole:
            # 文字颜色
            value = self._dataframe.iat[row, col]
            if isinstance(value, (int, float)) and value < 0:
                return "#C62828"  # 红色文字表示负数
            return None
            
        elif role == Qt.FontRole:
            # 字体
            if col == 0:  # 第一列加粗
                from PySide6.QtGui import QFont
                font = QFont()
                font.setBold(True)
                return font
            return None
            
        elif role == Qt.ToolTipRole:
            # 工具提示
            value = self._dataframe.iat[row, col]
            col_name = self._dataframe.columns[col]
            return f"{col_name}: {value}"
            
        return None
    
    def headerData(self, section: int, orientation: Qt.Orientation, role: int = Qt.DisplayRole) -> Any:
        """返回表头数据"""
        if role == Qt.DisplayRole:
            if orientation == Qt.Horizontal:
                # 列标题
                if section < len(self._dataframe.columns):
                    return str(self._dataframe.columns[section])
            else:
                # 行号
                return str(section + 1)
        return None
    
    def flags(self, index: QModelIndex) -> Qt.ItemFlags:
        """返回项目标志"""
        if not index.isValid():
            return Qt.NoItemFlags
        
        flags = super().flags(index)
        flags |= Qt.ItemIsEnabled
        flags |= Qt.ItemIsSelectable
        flags |= Qt.ItemIsEditable
        
        return flags
    
    def setData(self, index: QModelIndex, value: Any, role: int = Qt.EditRole) -> bool:
        """设置数据"""
        if not index.isValid() or role != Qt.EditRole:
            return False
        
        try:
            row = index.row()
            col = index.column()
            
            # 转换数据类型
            current_type = type(self._dataframe.iat[row, col])
            if current_type == int:
                value = int(value)
            elif current_type == float:
                value = float(value)
            elif current_type == bool:
                value = bool(value)
            
            # 更新数据
            self._dataframe.iat[row, col] = value
            
            # 发射数据变化信号
            self.dataChanged.emit(index, index, [role])
            self.dataChanged.emit()
            
            return True
        except Exception as e:
            print(f"设置数据失败: {e}")
            return False
    
    # ----- 自定义方法 -----
    @Slot(str)
    def load_csv(self, filepath: str):
        """从CSV文件加载数据"""
        try:
            self.beginResetModel()
            
            # 读取CSV文件
            self._dataframe = pd.read_csv(filepath)
            
            # 清理列名
            self._column_headers = [str(col) for col in self._dataframe.columns]
            
            self.endResetModel()
            self.dataChanged.emit()
            
            print(f"成功加载CSV文件: {filepath}, 形状: {self._dataframe.shape}")
            return True
        except Exception as e:
            print(f"加载CSV失败: {e}")
            return False
    
    @Slot()
    def generate_sample_data(self):
        """生成示例数据"""
        self.beginResetModel()
        
        # 创建示例数据
        dates = pd.date_range(start='2024-01-01', periods=100, freq='D')
        data = {
            '日期': dates,
            '收入': np.random.normal(1000, 200, 100).cumsum(),
            '成本': np.random.normal(600, 150, 100).cumsum(),
            '利润': np.random.normal(400, 100, 100).cumsum(),
            '增长率': np.random.normal(0.05, 0.02, 100),
            '完成率': np.random.uniform(0, 1, 100),
            '状态': np.random.choice(['进行中', '已完成', '已取消', '待审核'], 100)
        }
        
        self._dataframe = pd.DataFrame(data)
        self._column_headers = list(self._dataframe.columns)
        
        self.endResetModel()
        self.dataChanged.emit()
        
        print(f"生成示例数据,形状: {self._dataframe.shape}")
    
    @Slot(int, int)
    def sort(self, column: int, order: int = Qt.AscendingOrder):
        """排序数据"""
        if column < 0 or column >= len(self._dataframe.columns):
            return
        
        self.beginResetModel()
        
        # 更新排序状态
        self._sort_column = column
        self._sort_order = order
        
        # 执行排序
        col_name = self._dataframe.columns[column]
        ascending = order == Qt.AscendingOrder
        self._dataframe = self._dataframe.sort_values(
            by=col_name, 
            ascending=ascending,
            na_position='last'
        )
        
        self.endResetModel()
        self.sortRequested.emit(column, order)
        self.dataChanged.emit()
    
    @Slot(int, result=list)
    def get_column_data(self, column: int) -> list:
        """获取列数据"""
        if 0 <= column < len(self._dataframe.columns):
            return self._dataframe.iloc[:, column].tolist()
        return []
    
    @Slot(result=dict)
    def get_statistics(self) -> dict:
        """获取统计信息"""
        if self._dataframe.empty:
            return {}
        
        stats = {}
        
        # 数值列统计
        numeric_cols = self._dataframe.select_dtypes(include=[np.number]).columns
        for col in numeric_cols:
            col_data = self._dataframe[col]
            stats[str(col)] = {
                'count': int(len(col_data)),
                'mean': float(col_data.mean()),
                'std': float(col_data.std()),
                'min': float(col_data.min()),
                'max': float(col_data.max()),
                'sum': float(col_data.sum())
            }
        
        # 整体统计
        stats['_overall'] = {
            'rows': len(self._dataframe),
            'columns': len(self._dataframe.columns),
            'memory_usage': self._dataframe.memory_usage(deep=True).sum()
        }
        
        return stats
    
    @Slot(str, result=list)
    def filter_by_status(self, status: str) -> list:
        """按状态过滤"""
        if '状态' in self._dataframe.columns:
            filtered = self._dataframe[self._dataframe['状态'] == status]
            return filtered.to_dict('records')
        return []
    
    # ----- 属性 -----
    @property
    def dataframe(self) -> pd.DataFrame:
        """获取DataFrame"""
        return self._dataframe
    
    @property
    def shape(self) -> tuple:
        """获取形状"""
        return self._dataframe.shape
    
    @property
    def columns(self) -> list:
        """获取列名"""
        return self._column_headers

表格视图QML界面:

javascript 复制代码
// TableViewDemo.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import Qt.labs.qmlmodels 1.0
import Dashboard.Backend 1.0

ApplicationWindow {
    id: window
    width: 1400
    height: 800
    visible: true
    title: "表格数据视图演示"
    
    // 数据模型
    property var tableModel: Backend.tableModel
    
    // 主布局
    ColumnLayout {
        anchors.fill: parent
        spacing: 0
        
        // 工具栏
        Rectangle {
            Layout.fillWidth: true
            Layout.preferredHeight: 60
            color: "#f5f5f5"
            border.color: "#e0e0e0"
            border.width: 1
            
            RowLayout {
                anchors.fill: parent
                anchors.margins: 10
                spacing: 10
                
                // 标题
                Label {
                    text: "数据表格"
                    font.pixelSize: 20
                    font.bold: true
                    Layout.fillWidth: true
                }
                
                // 数据操作按钮
                Button {
                    text: "加载示例数据"
                    icon.source: "qrc:/icons/refresh.svg"
                    onClicked: tableModel.generate_sample_data()
                }
                
                Button {
                    text: "加载CSV..."
                    icon.source: "qrc:/icons/folder.svg"
                    onClicked: fileDialog.open()
                }
                
                // 统计信息
                Button {
                    text: "显示统计"
                    icon.source: "qrc:/icons/stats.svg"
                    onClicked: statsPopup.open()
                }
                
                // 导出按钮
                Button {
                    text: "导出数据"
                    icon.source: "qrc:/icons/download.svg"
                    onClicked: exportData()
                }
            }
        }
        
        // 过滤栏
        Rectangle {
            Layout.fillWidth: true
            Layout.preferredHeight: 50
            color: "#ffffff"
            border.color: "#e0e0e0"
            border.width: 1
            visible: filterRow.visible
            
            RowLayout {
                id: filterRow
                anchors.fill: parent
                anchors.margins: 10
                spacing: 10
                visible: tableModel.shape[0] > 0
                
                Label {
                    text: "快速过滤:"
                    font.bold: true
                }
                
                ComboBox {
                    id: statusFilter
                    model: ["全部", "进行中", "已完成", "已取消", "待审核"]
                    currentIndex: 0
                    
                    onCurrentTextChanged: {
                        applyFilters()
                    }
                }
                
                TextField {
                    id: searchFilter
                    placeholderText: "搜索..."
                    Layout.fillWidth: true
                    
                    onTextChanged: {
                        searchTimer.restart()
                    }
                    
                    Timer {
                        id: searchTimer
                        interval: 500
                        onTriggered: applyFilters()
                    }
                }
                
                Button {
                    text: "清除过滤"
                    flat: true
                    onClicked: {
                        statusFilter.currentIndex = 0
                        searchFilter.text = ""
                    }
                }
            }
        }
        
        // 表格区域
        Rectangle {
            Layout.fillWidth: true
            Layout.fillHeight: true
            color: "#ffffff"
            
            ScrollView {
                anchors.fill: parent
                
                TableView {
                    id: tableView
                    anchors.fill: parent
                    clip: true
                    model: tableModel
                    
                    // 列宽
                    columnWidthProvider: function(column) {
                        if (column === 0) return 120
                        if (column === 1) return 100
                        if (column === 2) return 100
                        if (column === 3) return 100
                        if (column === 4) return 80
                        if (column === 5) return 80
                        if (column === 6) return 100
                        return 120
                    }
                    
                    // 行高
                    rowHeightProvider: function(row) {
                        return 40
                    }
                    
                    // 代理
                    delegate: DelegateChooser {
                        DelegateChoice {
                            column: 0
                            
                            TableDelegate {
                                required property var model
                                required property int row
                                required property int column
                                
                                text: display || ""
                                
                                // 双击编辑
                                onDoubleClicked: {
                                    editDialog.openForEdit(row, column, text)
                                }
                            }
                        }
                        
                        DelegateChoice {
                            column: 4
                            
                            TableDelegate {
                                required property var model
                                required property int row
                                required property int column
                                
                                text: {
                                    var value = display || 0
                                    return (value * 100).toFixed(1) + "%"
                                }
                                
                                // 进度条背景
                                Rectangle {
                                    anchors {
                                        left: parent.left
                                        right: parent.right
                                        bottom: parent.bottom
                                        margins: 5
                                    }
                                    height: 4
                                    radius: 2
                                    color: "#e0e0e0"
                                    
                                    Rectangle {
                                        width: parent.width * (model.display || 0)
                                        height: parent.height
                                        radius: parent.radius
                                        color: {
                                            var value = model.display || 0
                                            if (value < 0.3) return "#f44336"
                                            if (value < 0.7) return "#ff9800"
                                            return "#4caf50"
                                        }
                                    }
                                }
                            }
                        }
                        
                        DelegateChoice {
                            column: 6
                            
                            TableDelegate {
                                required property var model
                                required property int row
                                required property int column
                                
                                text: display || ""
                                
                                // 状态标签
                                Rectangle {
                                    anchors.centerIn: parent
                                    width: parent.width - 20
                                    height: 24
                                    radius: 12
                                    color: {
                                        var status = display || ""
                                        switch(status) {
                                        case "进行中": return "#2196F3"
                                        case "已完成": return "#4CAF50"
                                        case "已取消": return "#F44336"
                                        case "待审核": return "#FF9800"
                                        default: return "#9E9E9E"
                                        }
                                    }
                                    
                                    Text {
                                        text: parent.parent.text
                                        color: "white"
                                        font.bold: true
                                        font.pixelSize: 12
                                        anchors.centerIn: parent
                                    }
                                }
                            }
                        }
                        
                        // 默认代理
                        DelegateChoice {
                            TableDelegate {
                                required property var model
                                required property int row
                                required property int column
                                
                                text: display || ""
                                
                                // 数值列右对齐
                                horizontalAlignment: {
                                    var value = model.display
                                    if (typeof value === 'number' || !isNaN(value)) {
                                        return Text.AlignRight
                                    }
                                    return Text.AlignLeft
                                }
                            }
                        }
                    }
                    
                    // 表头
                    header: HorizontalHeaderView {
                        syncView: tableView
                        clip: true
                        
                        delegate: Rectangle {
                            id: headerDelegate
                            implicitWidth: 120
                            implicitHeight: 40
                            color: "#f5f5f5"
                            border.color: "#e0e0e0"
                            border.width: 1
                            
                            // 排序指示器
                            property bool isSorted: tableView.model.sort_column === column
                            property bool sortAscending: tableView.model.sort_order === Qt.AscendingOrder
                            
                            RowLayout {
                                anchors.fill: parent
                                anchors.margins: 5
                                spacing: 5
                                
                                Text {
                                    text: display || ""
                                    font.bold: true
                                    elide: Text.ElideRight
                                    Layout.fillWidth: true
                                }
                                
                                // 排序图标
                                Text {
                                    text: {
                                        if (!headerDelegate.isSorted) return "↕"
                                        return headerDelegate.sortAscending ? "↑" : "↓"
                                    }
                                    visible: headerDelegate.isSorted
                                    color: "#2196F3"
                                    font.bold: true
                                }
                            }
                            
                            // 点击排序
                            MouseArea {
                                anchors.fill: parent
                                onClicked: {
                                    var newOrder = headerDelegate.isSorted && !headerDelegate.sortAscending ?
                                        Qt.AscendingOrder : Qt.DescendingOrder
                                    tableView.model.sort(column, newOrder)
                                }
                            }
                        }
                    }
                    
                    // 空状态
                    Label {
                        visible: tableView.rows === 0
                        text: "没有数据\n点击'加载示例数据'或'加载CSV...'开始"
                        horizontalAlignment: Text.AlignHCenter
                        verticalAlignment: Text.AlignVCenter
                        anchors.centerIn: parent
                        color: "#999"
                        font.pixelSize: 16
                    }
                }
            }
        }
        
        // 状态栏
        Rectangle {
            Layout.fillWidth: true
            Layout.preferredHeight: 30
            color: "#f5f5f5"
            border.color: "#e0e0e0"
            border.width: 1
            
            RowLayout {
                anchors.fill: parent
                anchors.margins: 5
                spacing: 20
                
                Label {
                    text: "总计: " + (tableModel.shape[0] || 0) + " 行 × " + (tableModel.shape[1] || 0) + " 列"
                    font.pixelSize: 12
                    color: "#666"
                }
                
                Item { Layout.fillWidth: true }
                
                Label {
                    text: "排序: " + (tableModel.sort_column >= 0 ? 
                        tableModel.columns[tableModel.sort_column] + " " + 
                        (tableModel.sort_order === Qt.AscendingOrder ? "↑" : "↓") : "无")
                    font.pixelSize: 12
                    color: "#666"
                }
            }
        }
    }
    
    // 文件对话框
    FileDialog {
        id: fileDialog
        title: "选择CSV文件"
        nameFilters: ["CSV文件 (*.csv)", "所有文件 (*)"]
        
        onAccepted: {
            var filepath = selectedFile.toString().replace("file:///", "")
            tableModel.load_csv(filepath)
        }
    }
    
    // 统计信息弹窗
    Popup {
        id: statsPopup
        width: 600
        height: 400
        x: (parent.width - width) / 2
        y: (parent.height - height) / 2
        modal: true
        
        contentItem: Rectangle {
            color: "white"
            radius: 8
            
            ColumnLayout {
                anchors.fill: parent
                spacing: 0
                
                // 标题栏
                Rectangle {
                    Layout.fillWidth: true
                    Layout.preferredHeight: 50
                    color: "#2196F3"
                    radius: 8
                    
                    RowLayout {
                        anchors.fill: parent
                        anchors.margins: 15
                        
                        Label {
                            text: "数据统计"
                            color: "white"
                            font.bold: true
                            font.pixelSize: 18
                            Layout.fillWidth: true
                        }
                        
                        Button {
                            text: "关闭"
                            flat: true
                            onClicked: statsPopup.close()
                        }
                    }
                }
                
                // 统计内容
                ScrollView {
                    Layout.fillWidth: true
                    Layout.fillHeight: true
                    
                    ColumnLayout {
                        width: parent.width
                        spacing: 20
                        padding: 20
                        
                        Repeater {
                            model: Object.keys(tableModel.get_statistics())
                            
                            delegate: GroupBox {
                                title: modelData === "_overall" ? "整体统计" : modelData
                                Layout.fillWidth: true
                                
                                GridLayout {
                                    columns: 2
                                    rowSpacing: 5
                                    columnSpacing: 20
                                    width: parent.width
                                    
                                    Repeater {
                                        model: Object.entries(tableModel.get_statistics()[modelData])
                                        
                                        delegate: RowLayout {
                                            Label {
                                                text: modelData[0] + ":"
                                                font.bold: true
                                                Layout.preferredWidth: 80
                                            }
                                            
                                            Label {
                                                text: typeof modelData[1] === 'number' ? 
                                                    modelData[1].toLocaleString(Qt.locale(), 'f', 2) : 
                                                    modelData[1]
                                                color: "#2196F3"
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    
    // 编辑对话框
    Popup {
        id: editDialog
        width: 400
        height: 200
        x: (parent.width - width) / 2
        y: (parent.height - height) / 2
        modal: true
        
        property int editRow: -1
        property int editColumn: -1
        property string originalValue: ""
        
        function openForEdit(row, column, value) {
            editRow = row
            editColumn = column
            originalValue = value
            editField.text = value
            editLabel.text = "编辑 [" + tableModel.columns[column] + "]"
            open()
        }
        
        contentItem: Rectangle {
            color: "white"
            radius: 8
            
            ColumnLayout {
                anchors.fill: parent
                spacing: 15
                padding: 20
                
                Label {
                    id: editLabel
                    text: "编辑"
                    font.bold: true
                    font.pixelSize: 18
                }
                
                TextField {
                    id: editField
                    Layout.fillWidth: true
                    placeholderText: "输入新值..."
                }
                
                RowLayout {
                    spacing: 10
                    
                    Button {
                        text: "取消"
                        Layout.fillWidth: true
                        onClicked: editDialog.close()
                    }
                    
                    Button {
                        text: "保存"
                        highlighted: true
                        Layout.fillWidth: true
                        
                        onClicked: {
                            if (editDialog.editRow >= 0 && editDialog.editColumn >= 0) {
                                var index = tableModel.index(editDialog.editRow, editDialog.editColumn)
                                tableModel.setData(index, editField.text)
                                editDialog.close()
                            }
                        }
                    }
                }
            }
        }
    }
    
    // 过滤函数
    function applyFilters() {
        // 这里可以实现复杂的过滤逻辑
        var status = statusFilter.currentText
        var search = searchFilter.text.toLowerCase()
        
        if (status !== "全部") {
            var filtered = tableModel.filter_by_status(status)
            // 更新表格显示过滤后的数据
            // 注意:这里需要实现过滤逻辑
        }
    }
    
    // 导出函数
    function exportData() {
        // 导出数据逻辑
        console.log("导出数据...")
    }
    
    // 初始化
    Component.onCompleted: {
        // 初始加载示例数据
        tableModel.generate_sample_data()
    }
}

3. 高级集成技术

3.1 异步操作与多线程

在GUI应用中,避免阻塞主线程至关重要。让我们看看如何使用Qt的线程机制来处理耗时操作:

python 复制代码
# async_worker.py - 异步工作器
import time
import random
from datetime import datetime
from typing import Dict, Any, List
from enum import Enum

from PySide6.QtCore import (
    QObject, 
    QRunnable, 
    QThreadPool, 
    Slot, 
    Signal,
    QTimer,
    QMutex,
    QMutexLocker
)
from PySide6.QtQml import QmlElement


class TaskStatus(Enum):
    """任务状态枚举"""
    PENDING = "pending"
    RUNNING = "running"
    COMPLETED = "completed"
    FAILED = "failed"
    CANCELLED = "cancelled"


class WorkerSignals(QObject):
    """工作器信号"""
    started = Signal(str)  # 任务ID
    progress = Signal(str, float, str)  # 任务ID, 进度, 消息
    result = Signal(str, object)  # 任务ID, 结果
    error = Signal(str, str)  # 任务ID, 错误信息
    finished = Signal(str)  # 任务ID


@QmlElement
class AsyncTask(QRunnable):
    """异步任务基类"""
    
    def __init__(self, task_id: str, task_data: Dict[str, Any]):
        super().__init__()
        self.task_id = task_id
        self.task_data = task_data
        self.signals = WorkerSignals()
        self._is_cancelled = False
        self._mutex = QMutex()
        
    def run(self):
        """执行任务"""
        try:
            self.signals.started.emit(self.task_id)
            result = self._execute()
            if not self._is_cancelled:
                self.signals.result.emit(self.task_id, result)
                self.signals.finished.emit(self.task_id)
        except Exception as e:
            if not self._is_cancelled:
                self.signals.error.emit(self.task_id, str(e))
                self.signals.finished.emit(self.task_id)
    
    def cancel(self):
        """取消任务"""
        with QMutexLocker(self._mutex):
            self._is_cancelled = True
    
    def _execute(self):
        """具体执行逻辑,子类实现"""
        raise NotImplementedError


class DataProcessingTask(AsyncTask):
    """数据处理任务"""
    
    def _execute(self):
        """模拟数据处理"""
        total_steps = random.randint(5, 10)
        
        for i in range(total_steps):
            # 检查是否取消
            with QMutexLocker(self._mutex):
                if self._is_cancelled:
                    return None
            
            # 模拟处理
            time.sleep(0.5)
            
            # 更新进度
            progress = (i + 1) / total_steps
            message = f"处理步骤 {i + 1}/{total_steps}"
            self.signals.progress.emit(self.task_id, progress, message)
        
        # 返回结果
        return {
            "task_id": self.task_id,
            "processed_at": datetime.now().isoformat(),
            "data_size": len(str(self.task_data)),
            "status": "success"
        }


class NetworkRequestTask(AsyncTask):
    """网络请求任务"""
    
    def _execute(self):
        """模拟网络请求"""
        url = self.task_data.get("url", "")
        method = self.task_data.get("method", "GET")
        
        # 模拟延迟
        time.sleep(random.uniform(1.0, 3.0))
        
        # 检查是否取消
        with QMutexLocker(self._mutex):
            if self._is_cancelled:
                return None
        
        # 模拟进度更新
        self.signals.progress.emit(self.task_id, 0.3, "正在连接服务器...")
        time.sleep(0.5)
        
        self.signals.progress.emit(self.task_id, 0.6, "正在下载数据...")
        time.sleep(0.5)
        
        self.signals.progress.emit(self.task_id, 0.9, "正在解析响应...")
        time.sleep(0.2)
        
        # 模拟响应
        return {
            "task_id": self.task_id,
            "url": url,
            "method": method,
            "status_code": 200,
            "data": {
                "timestamp": datetime.now().isoformat(),
                "content": f"来自 {url} 的模拟响应数据"
            }
        }


@QmlElement
class TaskManager(QObject):
    """任务管理器"""
    
    # 信号
    taskAdded = Signal(dict)
    taskUpdated = Signal(dict)
    taskRemoved = Signal(str)
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self.thread_pool = QThreadPool.globalInstance()
        self.thread_pool.setMaxThreadCount(4)  # 最大4个线程
        
        self.tasks: Dict[str, Dict[str, Any]] = {}
        self.active_tasks: Dict[str, AsyncTask] = {}
        
        # 清理已完成任务的定时器
        self.cleanup_timer = QTimer()
        self.cleanup_timer.setInterval(30000)  # 30秒清理一次
        self.cleanup_timer.timeout.connect(self._cleanup_completed_tasks)
        self.cleanup_timer.start()
    
    @Slot(str, dict, result=str)
    def start_data_processing(self, name: str, data: dict) -> str:
        """启动数据处理任务"""
        task_id = f"data_{datetime.now().timestamp()}"
        
        task = DataProcessingTask(task_id, data)
        self._setup_task_signals(task, name)
        
        self.tasks[task_id] = {
            "id": task_id,
            "name": name,
            "type": "data_processing",
            "status": TaskStatus.PENDING.value,
            "progress": 0.0,
            "created_at": datetime.now().isoformat()
        }
        
        self.thread_pool.start(task)
        self.active_tasks[task_id] = task
        self.taskAdded.emit(self.tasks[task_id])
        
        return task_id
    
    @Slot(str, dict, result=str)
    def start_network_request(self, url: str, params: dict) -> str:
        """启动网络请求任务"""
        task_id = f"net_{datetime.now().timestamp()}"
        
        task_data = {"url": url, "params": params}
        task = NetworkRequestTask(task_id, task_data)
        self._setup_task_signals(task, f"请求: {url}")
        
        self.tasks[task_id] = {
            "id": task_id,
            "name": f"请求: {url}",
            "type": "network_request",
            "status": TaskStatus.PENDING.value,
            "progress": 0.0,
            "created_at": datetime.now().isoformat()
        }
        
        self.thread_pool.start(task)
        self.active_tasks[task_id] = task
        self.taskAdded.emit(self.tasks[task_id])
        
        return task_id
    
    @Slot(str)
    def cancel_task(self, task_id: str):
        """取消任务"""
        if task_id in self.active_tasks:
            task = self.active_tasks[task_id]
            task.cancel()
            
            if task_id in self.tasks:
                self.tasks[task_id]["status"] = TaskStatus.CANCELLED.value
                self.taskUpdated.emit(self.tasks[task_id])
    
    @Slot(str, result=dict)
    def get_task_info(self, task_id: str) -> dict:
        """获取任务信息"""
        return self.tasks.get(task_id, {})
    
    @Slot(result=list)
    def get_all_tasks(self) -> list:
        """获取所有任务"""
        return list(self.tasks.values())
    
    @Slot(result=list)
    def get_active_tasks(self) -> list:
        """获取活动任务"""
        return [
            task for task in self.tasks.values()
            if task["status"] in [TaskStatus.PENDING.value, TaskStatus.RUNNING.value]
        ]
    
    def _setup_task_signals(self, task: AsyncTask, task_name: str):
        """设置任务信号连接"""
        
        @Slot(str)
        def on_started(tid: str):
            self.tasks[tid]["status"] = TaskStatus.RUNNING.value
            self.tasks[tid]["started_at"] = datetime.now().isoformat()
            self.taskUpdated.emit(self.tasks[tid])
        
        @Slot(str, float, str)
        def on_progress(tid: str, progress: float, message: str):
            if tid in self.tasks:
                self.tasks[tid]["progress"] = progress
                self.tasks[tid]["message"] = message
                self.taskUpdated.emit(self.tasks[tid])
        
        @Slot(str, object)
        def on_result(tid: str, result: object):
            if tid in self.tasks:
                self.tasks[tid]["status"] = TaskStatus.COMPLETED.value
                self.tasks[tid]["result"] = result
                self.tasks[tid]["completed_at"] = datetime.now().isoformat()
                self.taskUpdated.emit(self.tasks[tid])
                
                # 从活动任务中移除
                if tid in self.active_tasks:
                    del self.active_tasks[tid]
        
        @Slot(str, str)
        def on_error(tid: str, error: str):
            if tid in self.tasks:
                self.tasks[tid]["status"] = TaskStatus.FAILED.value
                self.tasks[tid]["error"] = error
                self.tasks[tid]["completed_at"] = datetime.now().isoformat()
                self.taskUpdated.emit(self.tasks[tid])
                
                if tid in self.active_tasks:
                    del self.active_tasks[tid]
        
        @Slot(str)
        def on_finished(tid: str):
            # 可以在任务完成时执行一些清理操作
            pass
        
        # 连接信号
        task.signals.started.connect(on_started)
        task.signals.progress.connect(on_progress)
        task.signals.result.connect(on_result)
        task.signals.error.connect(on_error)
        task.signals.finished.connect(on_finished)
    
    def _cleanup_completed_tasks(self):
        """清理已完成的任务"""
        to_remove = []
        for task_id, task in self.tasks.items():
            if task["status"] in [TaskStatus.COMPLETED.value, TaskStatus.FAILED.value, TaskStatus.CANCELLED.value]:
                # 检查是否已完成超过5分钟
                completed_at = task.get("completed_at")
                if completed_at:
                    try:
                        completed_time = datetime.fromisoformat(completed_at)
                        if (datetime.now() - completed_time).total_seconds() > 300:  # 5分钟
                            to_remove.append(task_id)
                    except ValueError:
                        pass
        
        for task_id in to_remove:
            if task_id in self.tasks:
                del self.tasks[task_id]
                self.taskRemoved.emit(task_id)

异步任务执行流程:

异步任务QML界面:

javascript 复制代码
// AsyncTaskDemo.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import Dashboard.Backend 1.0

ApplicationWindow {
    id: window
    width: 1200
    height: 800
    visible: true
    title: "异步任务管理"
    
    // 任务管理器
    property var taskManager: Backend.taskManager
    
    // 主布局
    ColumnLayout {
        anchors.fill: parent
        spacing: 0
        
        // 工具栏
        Rectangle {
            Layout.fillWidth: true
            Layout.preferredHeight: 60
            color: "#f5f5f5"
            
            RowLayout {
                anchors.fill: parent
                anchors.margins: 10
                spacing: 10
                
                // 标题
                Label {
                    text: "异步任务管理器"
                    font.pixelSize: 20
                    font.bold: true
                    Layout.fillWidth: true
                }
                
                // 控制按钮
                Button {
                    text: "启动数据处理"
                    onClicked: {
                        var taskId = taskManager.start_data_processing(
                            "数据处理任务",
                            {"size": 1000, "complexity": "medium"}
                        )
                        console.log("启动数据处理任务:", taskId)
                    }
                }
                
                Button {
                    text: "启动网络请求"
                    onClicked: {
                        var taskId = taskManager.start_network_request(
                            "https://api.example.com/data",
                            {"timeout": 5000}
                        )
                        console.log("启动网络请求任务:", taskId)
                    }
                }
                
                Button {
                    text: "批量启动"
                    onClicked: startBatchTasks()
                }
            }
        }
        
        // 内容区域
        SplitView {
            Layout.fillWidth: true
            Layout.fillHeight: true
            orientation: Qt.Horizontal
            
            // 左侧:任务控制面板
            ScrollView {
                SplitView.preferredWidth: 400
                
                ColumnLayout {
                    width: parent.width
                    spacing: 20
                    padding: 20
                    
                    // 任务统计
                    GroupBox {
                        title: "任务统计"
                        Layout.fillWidth: true
                        
                        GridLayout {
                            columns: 2
                            rowSpacing: 10
                            columnSpacing: 20
                            width: parent.width
                            
                            Label { text: "总任务数:"; font.bold: true }
                            Label { 
                                text: taskRepeater.count
                                color: "#2196F3"
                            }
                            
                            Label { text: "运行中:"; font.bold: true }
                            Label { 
                                text: {
                                    var running = 0
                                    for (var i = 0; i < taskRepeater.count; i++) {
                                        var task = taskRepeater.itemAt(i)
                                        if (task && task.status === "running") {
                                            running++
                                        }
                                    }
                                    return running
                                }
                                color: "#4CAF50"
                            }
                            
                            Label { text: "已完成:"; font.bold: true }
                            Label { 
                                text: {
                                    var completed = 0
                                    for (var i = 0; i < taskRepeater.count; i++) {
                                        var task = taskRepeater.itemAt(i)
                                        if (task && task.status === "completed") {
                                            completed++
                                        }
                                    }
                                    return completed
                                }
                                color: "#9C27B0"
                            }
                            
                            Label { text: "失败/取消:"; font.bold: true }
                            Label { 
                                text: {
                                    var failed = 0
                                    for (var i = 0; i < taskRepeater.count; i++) {
                                        var task = taskRepeater.itemAt(i)
                                        if (task && (task.status === "failed" || task.status === "cancelled")) {
                                            failed++
                                        }
                                    }
                                    return failed
                                }
                                color: "#F44336"
                            }
                        }
                    }
                    
                    // 控制面板
                    GroupBox {
                        title: "任务控制"
                        Layout.fillWidth: true
                        
                        ColumnLayout {
                            width: parent.width
                            spacing: 10
                            
                            // 线程池设置
                            RowLayout {
                                Label { 
                                    text: "最大线程数:"
                                    Layout.preferredWidth: 100
                                }
                                
                                Slider {
                                    id: threadSlider
                                    from: 1
                                    to: 8
                                    value: 4
                                    stepSize: 1
                                    Layout.fillWidth: true
                                    
                                    onValueChanged: {
                                        // 这里应该设置线程池大小
                                        // 注意:实际应用中需要更复杂的线程池管理
                                    }
                                }
                                
                                Label {
                                    text: Math.round(threadSlider.value)
                                    font.bold: true
                                }
                            }
                            
                            // 批量操作
                            RowLayout {
                                Button {
                                    text: "全部暂停"
                                    onClicked: pauseAllTasks()
                                }
                                
                                Button {
                                    text: "全部取消"
                                    onClicked: cancelAllTasks()
                                }
                                
                                Button {
                                    text: "清除完成"
                                    onClicked: clearCompletedTasks()
                                }
                            }
                        }
                    }
                    
                    // 系统信息
                    GroupBox {
                        title: "系统信息"
                        Layout.fillWidth: true
                        
                        ColumnLayout {
                            width: parent.width
                            spacing: 5
                            
                            Label {
                                text: "线程池状态:"
                                font.bold: true
                            }
                            
                            Label {
                                text: "活动线程: " + taskManager.threadPool.activeThreadCount
                                color: "#666"
                            }
                            
                            Label {
                                text: "排队任务: " + taskManager.threadPool.queueSize
                                color: "#666"
                            }
                            
                            Label {
                                text: "内存使用: " + (Math.random() * 100).toFixed(1) + " MB"
                                color: "#666"
                            }
                        }
                    }
                    
                    Item { Layout.fillHeight: true }
                }
            }
            
            // 右侧:任务列表
            ScrollView {
                SplitView.fillWidth: true
                
                ColumnLayout {
                    width: parent.width
                    spacing: 10
                    padding: 20
                    
                    // 搜索栏
                    RowLayout {
                        TextField {
                            id: searchField
                            placeholderText: "搜索任务..."
                            Layout.fillWidth: true
                        }
                        
                        ComboBox {
                            id: filterCombo
                            model: ["全部", "运行中", "已完成", "失败", "已取消"]
                            currentIndex: 0
                        }
                    }
                    
                    // 任务列表
                    Repeater {
                        id: taskRepeater
                        model: taskManager.allTasks
                        
                        delegate: TaskItem {
                            taskId: modelData.id
                            taskName: modelData.name
                            taskType: modelData.type
                            status: modelData.status
                            progress: modelData.progress || 0
                            message: modelData.message || ""
                            createdTime: modelData.created_at
                            
                            Layout.fillWidth: true
                            Layout.preferredHeight: 100
                            
                            visible: {
                                if (!searchField.text) {
                                    if (filterCombo.currentIndex === 0) return true
                                    if (filterCombo.currentIndex === 1) return status === "running"
                                    if (filterCombo.currentIndex === 2) return status === "completed"
                                    if (filterCombo.currentIndex === 3) return status === "failed"
                                    if (filterCombo.currentIndex === 4) return status === "cancelled"
                                } else {
                                    var searchText = searchField.text.toLowerCase()
                                    return taskName.toLowerCase().includes(searchText) ||
                                           taskId.toLowerCase().includes(searchText)
                                }
                                return true
                            }
                            
                            onCancelClicked: taskManager.cancelTask(taskId)
                        }
                    }
                    
                    // 空状态
                    Label {
                        visible: taskRepeater.count === 0
                        text: "暂无任务\n点击上方按钮开始新任务"
                        horizontalAlignment: Text.AlignHCenter
                        verticalAlignment: Text.AlignVCenter
                        Layout.fillWidth: true
                        Layout.fillHeight: true
                        color: "#999"
                        font.pixelSize: 16
                    }
                }
            }
        }
    }
    
    // 任务项组件
    component TaskItem: Rectangle {
        id: taskRoot
        
        // 属性
        property string taskId: ""
        property string taskName: ""
        property string taskType: ""
        property string status: "pending"
        property real progress: 0
        property string message: ""
        property string createdTime: ""
        
        signal cancelClicked(string taskId)
        
        radius: 8
        border.color: "#e0e0e0"
        border.width: 1
        
        // 根据状态设置颜色
        property color statusColor: {
            switch(status) {
            case "running": return "#2196F3"
            case "completed": return "#4CAF50"
            case "failed": return "#F44336"
            case "cancelled": return "#FF9800"
            default: return "#9E9E9E"
            }
        }
        
        // 状态文本
        property string statusText: {
            switch(status) {
            case "pending": return "等待中"
            case "running": return "运行中"
            case "completed": return "已完成"
            case "failed": return "失败"
            case "cancelled": return "已取消"
            default: return "未知"
            }
        }
        
        ColumnLayout {
            anchors.fill: parent
            anchors.margins: 15
            spacing: 8
            
            // 标题行
            RowLayout {
                Layout.fillWidth: true
                
                // 任务图标
                Rectangle {
                    width: 36
                    height: 36
                    radius: 18
                    color: Qt.lighter(statusColor, 1.8)
                    
                    Text {
                        text: {
                            switch(taskType) {
                            case "data_processing": return "🔢"
                            case "network_request": return "🌐"
                            default: return "📄"
                            }
                        }
                        anchors.centerIn: parent
                        font.pixelSize: 16
                    }
                }
                
                // 任务信息
                ColumnLayout {
                    spacing: 2
                    Layout.fillWidth: true
                    
                    Text {
                        text: taskName
                        font.bold: true
                        font.pixelSize: 16
                        elide: Text.ElideRight
                        Layout.fillWidth: true
                    }
                    
                    Text {
                        text: "ID: " + taskId.substring(0, 8) + "..."
                        color: "#666"
                        font.pixelSize: 12
                    }
                }
                
                // 状态标签
                Rectangle {
                    radius: 10
                    color: statusColor
                    width: 80
                    height: 24
                    
                    Text {
                        text: statusText
                        color: "white"
                        font.bold: true
                        font.pixelSize: 12
                        anchors.centerIn: parent
                    }
                }
                
                // 取消按钮
                Button {
                    text: "取消"
                    visible: status === "running"
                    onClicked: taskRoot.cancelClicked(taskId)
                }
            }
            
            // 进度条
            Rectangle {
                Layout.fillWidth: true
                Layout.preferredHeight: 6
                radius: 3
                color: "#e0e0e0"
                visible: status === "running"
                
                Rectangle {
                    width: parent.width * progress
                    height: parent.height
                    radius: parent.radius
                    color: statusColor
                    
                    // 进度动画
                    Behavior on width {
                        NumberAnimation { duration: 300 }
                    }
                }
            }
            
            // 进度文本
            RowLayout {
                Layout.fillWidth: true
                visible: status === "running"
                
                Text {
                    text: "进度: " + (progress * 100).toFixed(1) + "%"
                    color: "#666"
                    font.pixelSize: 12
                }
                
                Item { Layout.fillWidth: true }
                
                Text {
                    text: message
                    color: "#666"
                    font.pixelSize: 12
                }
            }
            
            // 时间信息
            RowLayout {
                Layout.fillWidth: true
                
                Text {
                    text: "创建: " + formatTime(createdTime)
                    color: "#999"
                    font.pixelSize: 11
                }
                
                Item { Layout.fillWidth: true }
                
                // 运行时间
                Text {
                    text: {
                        if (status === "running") {
                            var created = new Date(createdTime)
                            var now = new Date()
                            var diff = Math.floor((now - created) / 1000)
                            return "运行: " + formatDuration(diff)
                        }
                        return ""
                    }
                    color: "#999"
                    font.pixelSize: 11
                }
            }
        }
        
        // 悬停效果
        HoverHandler {
            id: hoverHandler
        }
        
        states: State {
            name: "hovered"
            when: hoverHandler.hovered
            
            PropertyChanges {
                target: taskRoot
                scale: 1.02
            }
        }
        
        transitions: Transition {
            NumberAnimation {
                properties: "scale"
                duration: 200
            }
        }
    }
    
    // 批量启动任务
    function startBatchTasks() {
        for (var i = 0; i < 5; i++) {
            if (Math.random() > 0.5) {
                taskManager.start_data_processing(
                    "批量处理任务 " + (i + 1),
                    {"index": i, "size": Math.random() * 1000}
                )
            } else {
                taskManager.start_network_request(
                    "https://api.example.com/data/" + i,
                    {"index": i}
                )
            }
        }
    }
    
    // 暂停所有任务
    function pauseAllTasks() {
        // 注意:实际暂停逻辑需要更复杂的实现
        console.log("暂停所有任务")
    }
    
    // 取消所有任务
    function cancelAllTasks() {
        for (var i = 0; i < taskRepeater.count; i++) {
            var task = taskRepeater.itemAt(i)
            if (task && task.status === "running") {
                taskManager.cancelTask(task.taskId)
            }
        }
    }
    
    // 清除已完成任务
    function clearCompletedTasks() {
        // 这里应该调用任务管理器的清理方法
        console.log("清除已完成任务")
    }
    
    // 格式化时间
    function formatTime(timeString) {
        if (!timeString) return ""
        var date = new Date(timeString)
        return date.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})
    }
    
    // 格式化持续时间
    function formatDuration(seconds) {
        var hours = Math.floor(seconds / 3600)
        var minutes = Math.floor((seconds % 3600) / 60)
        var secs = seconds % 60
        
        if (hours > 0) {
            return hours + "h " + minutes + "m"
        } else if (minutes > 0) {
            return minutes + "m " + secs + "s"
        } else {
            return secs + "s"
        }
    }
    
    // 监听任务变化
    Connections {
        target: taskManager
        
        function onTaskAdded(task) {
            console.log("任务已添加:", task.id, task.name)
        }
        
        function onTaskUpdated(task) {
            console.log("任务已更新:", task.id, task.status, task.progress)
        }
        
        function onTaskRemoved(taskId) {
            console.log("任务已移除:", taskId)
        }
    }
}

3.2 错误处理与日志系统

健壮的应用程序需要完善的错误处理和日志系统:

python 复制代码
# error_handler.py - 错误处理与日志
import sys
import traceback
import logging
from datetime import datetime
from typing import Dict, Any, Optional, List
from enum import Enum

from PySide6.QtCore import QObject, Slot, Signal, Property, QTimer
from PySide6.QtQml import QmlElement


class ErrorLevel(Enum):
    """错误级别"""
    DEBUG = 0
    INFO = 1
    WARNING = 2
    ERROR = 3
    CRITICAL = 4


class LogEntry:
    """日志条目"""
    
    def __init__(self, level: ErrorLevel, message: str, details: str = "", 
                 timestamp: datetime = None, source: str = ""):
        self.level = level
        self.message = message
        self.details = details
        self.timestamp = timestamp or datetime.now()
        self.source = source
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        return {
            "level": self.level.value,
            "level_name": self.level.name,
            "message": self.message,
            "details": self.details,
            "timestamp": self.timestamp.isoformat(),
            "source": self.source
        }


@QmlElement
class ErrorHandler(QObject):
    """错误处理器"""
    
    # 信号
    errorOccurred = Signal(dict)  # 错误信息
    warningOccurred = Signal(dict)  # 警告信息
    infoMessage = Signal(dict)  # 信息消息
    logUpdated = Signal(list)  # 日志更新
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self._logs: List[LogEntry] = []
        self._max_logs = 1000
        
        # 设置Python日志
        self._setup_logging()
        
        # 自动清理日志的定时器
        self.cleanup_timer = QTimer()
        self.cleanup_timer.setInterval(60000)  # 1分钟清理一次
        self.cleanup_timer.timeout.connect(self._cleanup_old_logs)
        self.cleanup_timer.start()
    
    def _setup_logging(self):
        """设置Python日志系统"""
        # 创建格式化器
        formatter = logging.Formatter(
            '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
        )
        
        # 创建处理器
        console_handler = logging.StreamHandler()
        console_handler.setFormatter(formatter)
        
        # 获取根日志记录器
        root_logger = logging.getLogger()
        root_logger.setLevel(logging.DEBUG)
        root_logger.addHandler(console_handler)
        
        # 连接到我们的处理器
        logging.getLogger().addHandler(self)
    
    @Slot(str, str, result=bool)
    def handle_error(self, message: str, details: str = "") -> bool:
        """处理错误"""
        try:
            entry = LogEntry(
                level=ErrorLevel.ERROR,
                message=message,
                details=details,
                source="QML"
            )
            
            self._add_log(entry)
            self.errorOccurred.emit(entry.to_dict())
            
            # 记录到文件
            self._write_to_file(entry)
            
            return True
        except Exception as e:
            print(f"处理错误时发生异常: {e}")
            return False
    
    @Slot(str, str)
    def handle_warning(self, message: str, details: str = ""):
        """处理警告"""
        entry = LogEntry(
            level=ErrorLevel.WARNING,
            message=message,
            details=details,
            source="QML"
        )
        
        self._add_log(entry)
        self.warningOccurred.emit(entry.to_dict())
        self._write_to_file(entry)
    
    @Slot(str, str)
    def log_info(self, message: str, details: str = ""):
        """记录信息"""
        entry = LogEntry(
            level=ErrorLevel.INFO,
            message=message,
            details=details,
            source="QML"
        )
        
        self._add_log(entry)
        self.infoMessage.emit(entry.to_dict())
        self._write_to_file(entry)
    
    @Slot(str, str, str)
    def log_debug(self, message: str, details: str = "", source: str = ""):
        """记录调试信息"""
        entry = LogEntry(
            level=ErrorLevel.DEBUG,
            message=message,
            details=details,
            source=source or "QML"
        )
        
        self._add_log(entry)
        self._write_to_file(entry)
    
    @Slot(Exception, str)
    def handle_exception(self, exception: Exception, context: str = ""):
        """处理异常"""
        try:
            # 获取异常信息
            exc_type = type(exception).__name__
            exc_message = str(exception)
            exc_traceback = traceback.format_exc()
            
            # 创建详细消息
            details = f"类型: {exc_type}\n消息: {exc_message}\n上下文: {context}\n\n堆栈跟踪:\n{exc_traceback}"
            
            entry = LogEntry(
                level=ErrorLevel.ERROR,
                message=f"未处理的异常: {exc_message}",
                details=details,
                source="Python"
            )
            
            self._add_log(entry)
            self.errorOccurred.emit(entry.to_dict())
            self._write_to_file(entry)
            
        except Exception as e:
            print(f"处理异常时发生错误: {e}")
    
    @Slot(result=list)
    def get_logs(self, level: int = 0, limit: int = 100) -> List[Dict[str, Any]]:
        """获取日志"""
        filtered = [log for log in self._logs if log.level.value >= level]
        limited = filtered[-limit:] if limit > 0 else filtered
        return [log.to_dict() for log in limited]
    
    @Slot(int, result=list)
    def get_logs_by_level(self, level: int) -> List[Dict[str, Any]]:
        """按级别获取日志"""
        filtered = [log for log in self._logs if log.level.value == level]
        return [log.to_dict() for log in filtered]
    
    @Slot()
    def clear_logs(self):
        """清空日志"""
        self._logs.clear()
        self.logUpdated.emit([])
    
    @Slot(str, result=str)
    def export_logs(self, filepath: str) -> str:
        """导出日志到文件"""
        try:
            with open(filepath, 'w', encoding='utf-8') as f:
                for log in self._logs:
                    f.write(f"[{log.timestamp.isoformat()}] {log.level.name}: {log.message}\n")
                    if log.details:
                        f.write(f"详细信息: {log.details}\n")
                    f.write("-" * 50 + "\n")
            return "success"
        except Exception as e:
            return f"导出失败: {str(e)}"
    
    def _add_log(self, entry: LogEntry):
        """添加日志条目"""
        self._logs.append(entry)
        
        # 限制日志数量
        if len(self._logs) > self._max_logs:
            self._logs = self._logs[-self._max_logs:]
        
        # 发送更新信号
        self.logUpdated.emit(self.get_logs(limit=50))
    
    def _write_to_file(self, entry: LogEntry):
        """写入日志文件"""
        try:
            with open("application.log", "a", encoding="utf-8") as f:
                f.write(f"[{entry.timestamp.isoformat()}] {entry.level.name}: {entry.message}\n")
                if entry.details:
                    f.write(f"详细信息: {entry.details}\n")
        except Exception as e:
            print(f"写入日志文件失败: {e}")
    
    def _cleanup_old_logs(self):
        """清理旧日志"""
        # 保留最近24小时的日志
        cutoff_time = datetime.now().timestamp() - 24 * 3600
        
        self._logs = [
            log for log in self._logs
            if log.timestamp.timestamp() > cutoff_time
        ]
    
    # Python日志处理器接口
    def emit(self, record):
        """处理Python日志记录"""
        level_map = {
            logging.DEBUG: ErrorLevel.DEBUG,
            logging.INFO: ErrorLevel.INFO,
            logging.WARNING: ErrorLevel.WARNING,
            logging.ERROR: ErrorLevel.ERROR,
            logging.CRITICAL: ErrorLevel.CRITICAL
        }
        
        entry = LogEntry(
            level=level_map.get(record.levelno, ErrorLevel.INFO),
            message=record.getMessage(),
            details="",
            timestamp=datetime.fromtimestamp(record.created),
            source=record.name
        )
        
        self._add_log(entry)

错误处理QML界面:

javascript 复制代码
// ErrorHandlerDemo.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import Dashboard.Backend 1.0

ApplicationWindow {
    id: window
    width: 1200
    height: 800
    visible: true
    title: "错误处理与日志系统"
    
    // 错误处理器
    property var errorHandler: Backend.errorHandler
    
    // 错误级别颜色
    property var levelColors: {
        "DEBUG": "#9E9E9E",
        "INFO": "#2196F3",
        "WARNING": "#FF9800",
        "ERROR": "#F44336",
        "CRITICAL": "#D32F2F"
    }
    
    // 主布局
    ColumnLayout {
        anchors.fill: parent
        spacing: 0
        
        // 工具栏
        Rectangle {
            Layout.fillWidth: true
            Layout.preferredHeight: 60
            color: "#f5f5f5"
            
            RowLayout {
                anchors.fill: parent
                anchors.margins: 10
                spacing: 10
                
                // 标题
                Label {
                    text: "错误处理与日志系统"
                    font.pixelSize: 20
                    font.bold: true
                    Layout.fillWidth: true
                }
                
                // 控制按钮
                Button {
                    text: "测试错误"
                    onClicked: testError()
                }
                
                Button {
                    text: "测试警告"
                    onClicked: testWarning()
                }
                
                Button {
                    text: "测试异常"
                    onClicked: testException()
                }
                
                Button {
                    text: "清空日志"
                    onClicked: errorHandler.clear_logs()
                }
                
                Button {
                    text: "导出日志"
                    onClicked: exportLogs()
                }
            }
        }
        
        // 过滤栏
        Rectangle {
            Layout.fillWidth: true
            Layout.preferredHeight: 50
            color: "#ffffff"
            
            RowLayout {
                anchors.fill: parent
                anchors.margins: 10
                spacing: 10
                
                Label { text: "过滤级别:" }
                
                ComboBox {
                    id: levelFilter
                    model: ["全部", "DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
                    currentIndex: 2
                    
                    onCurrentTextChanged: refreshLogs()
                }
                
                TextField {
                    id: searchFilter
                    placeholderText: "搜索日志..."
                    Layout.fillWidth: true
                    
                    onTextChanged: searchTimer.restart()
                    
                    Timer {
                        id: searchTimer
                        interval: 500
                        onTriggered: refreshLogs()
                    }
                }
                
                Button {
                    text: "自动滚动"
                    checkable: true
                    checked: true
                }
            }
        }
        
        // 日志列表
        ScrollView {
            id: logScrollView
            Layout.fillWidth: true
            Layout.fillHeight: true
            
            ListView {
                id: logListView
                model: ListModel {}
                spacing: 1
                
                delegate: Rectangle {
                    width: ListView.view.width
                    height: logContent.height + 20
                    color: index % 2 ? "#fafafa" : "#ffffff"
                    
                    // 左侧颜色条
                    Rectangle {
                        width: 5
                        height: parent.height
                        color: levelColors[level_name] || "#9E9E9E"
                    }
                    
                    // 日志内容
                    ColumnLayout {
                        id: logContent
                        anchors {
                            left: parent.left
                            right: parent.right
                            top: parent.top
                            margins: 10
                        }
                        spacing: 5
                        
                        // 标题行
                        RowLayout {
                            Layout.fillWidth: true
                            
                            // 级别标签
                            Rectangle {
                                radius: 3
                                color: levelColors[level_name] || "#9E9E9E"
                                width: 70
                                height: 20
                                
                                Text {
                                    text: level_name
                                    color: "white"
                                    font.bold: true
                                    font.pixelSize: 11
                                    anchors.centerIn: parent
                                }
                            }
                            
                            // 时间
                            Text {
                                text: formatTime(timestamp)
                                color: "#666"
                                font.pixelSize: 12
                            }
                            
                            Item { Layout.fillWidth: true }
                            
                            // 来源
                            Text {
                                text: source || "未知"
                                color: "#999"
                                font.pixelSize: 11
                                font.italic: true
                            }
                        }
                        
                        // 消息
                        Text {
                            text: message
                            font.pixelSize: 14
                            wrapMode: Text.WordWrap
                            Layout.fillWidth: true
                        }
                        
                        // 详细信息
                        Text {
                            text: details || ""
                            visible: details && details.length > 0
                            color: "#666"
                            font.pixelSize: 12
                            wrapMode: Text.WordWrap
                            Layout.fillWidth: true
                            
                            // 更多/收起按钮
                            MouseArea {
                                anchors.fill: parent
                                onClicked: {
                                    if (parent.maximumLineCount === 1) {
                                        parent.maximumLineCount = 1000
                                    } else {
                                        parent.maximumLineCount = 1
                                    }
                                }
                            }
                        }
                    }
                    
                    // 分隔线
                    Rectangle {
                        width: parent.width
                        height: 1
                        color: "#e0e0e0"
                        anchors.bottom: parent.bottom
                    }
                }
                
                // 空状态
                Label {
                    visible: logListView.count === 0
                    text: "暂无日志"
                    anchors.centerIn: parent
                    color: "#999"
                    font.pixelSize: 16
                }
            }
        }
        
        // 状态栏
        Rectangle {
            Layout.fillWidth: true
            Layout.preferredHeight: 30
            color: "#f5f5f5"
            
            RowLayout {
                anchors.fill: parent
                anchors.margins: 5
                
                Label {
                    text: "日志数量: " + logListView.count
                    color: "#666"
                    font.pixelSize: 12
                }
                
                Item { Layout.fillWidth: true }
                
                Label {
                    text: "最后更新: " + formatTime(new Date())
                    color: "#666"
                    font.pixelSize: 12
                }
            }
        }
    }
    
    // 测试函数
    function testError() {
        var messages = [
            "数据库连接失败",
            "网络请求超时",
            "文件不存在",
            "权限不足",
            "数据格式错误"
        ]
        
        var details = [
            "无法连接到数据库服务器 127.0.0.1:5432",
            "请求 https://api.example.com/data 在5000ms后超时",
            "文件 /path/to/data.json 不存在",
            "用户没有写入 /var/log 目录的权限",
            "JSON解析失败: Unexpected token ' in JSON at position 42"
        ]
        
        var index = Math.floor(Math.random() * messages.length)
        errorHandler.handle_error(messages[index], details[index])
    }
    
    function testWarning() {
        var messages = [
            "内存使用过高",
            "磁盘空间不足",
            "网络延迟较高",
            "缓存即将过期",
            "API调用频率接近限制"
        ]
        
        var details = [
            "当前内存使用率: 85%,建议优化",
            "磁盘剩余空间: 2.3GB,建议清理",
            "平均网络延迟: 250ms,可能影响性能",
            "缓存将在5分钟后过期,建议刷新",
            "API调用: 95/100,接近限制"
        ]
        
        var index = Math.floor(Math.random() * messages.length)
        errorHandler.handle_warning(messages[index], details[index])
    }
    
    function testException() {
        try {
            // 故意抛出异常
            throw new Error("测试异常: 除零错误")
        } catch (e) {
            errorHandler.handle_exception(e, "测试异常处理")
        }
    }
    
    // 导出日志
    function exportLogs() {
        var dialog = Qt.createComponent("FileDialog.qml").createObject(window)
        dialog.selectExisting = false
        dialog.nameFilters = ["日志文件 (*.log)", "文本文件 (*.txt)", "所有文件 (*)"]
        
        dialog.onAccepted.connect(function() {
            var result = errorHandler.export_logs(dialog.fileUrl.toString().replace("file:///", ""))
            if (result === "success") {
                showMessage("日志导出成功")
            } else {
                showMessage("导出失败: " + result)
            }
        })
        
        dialog.open()
    }
    
    // 刷新日志
    function refreshLogs() {
        logListView.model.clear()
        
        var logs = []
        if (levelFilter.currentText === "全部") {
            logs = errorHandler.get_logs(0, 100)
        } else {
            var levelMap = {
                "DEBUG": 0, "INFO": 1, "WARNING": 2, 
                "ERROR": 3, "CRITICAL": 4
            }
            var level = levelMap[levelFilter.currentText] || 1
            logs = errorHandler.get_logs(level, 100)
        }
        
        // 应用搜索过滤
        var searchText = searchFilter.text.toLowerCase()
        var filteredLogs = logs.filter(function(log) {
            if (!searchText) return true
            return log.message.toLowerCase().includes(searchText) ||
                   log.details.toLowerCase().includes(searchText) ||
                   log.source.toLowerCase().includes(searchText)
        })
        
        // 添加到列表
        for (var i = 0; i < filteredLogs.length; i++) {
            logListView.model.append(filteredLogs[i])
        }
        
        // 自动滚动到底部
        if (logScrollView.ScrollBar.vertical.position > 0.9) {
            logListView.positionViewAtEnd()
        }
    }
    
    // 格式化时间
    function formatTime(dateString) {
        var date = new Date(dateString)
        return date.toLocaleTimeString([], { 
            hour: '2-digit', 
            minute: '2-digit',
            second: '2-digit'
        })
    }
    
    // 显示消息
    function showMessage(text) {
        var popup = Qt.createComponent("Popup.qml").createObject(window)
        popup.text = text
        popup.open()
    }
    
    // 监听日志更新
    Connections {
        target: errorHandler
        
        function onLogUpdated(logs) {
            refreshLogs()
        }
        
        function onErrorOccurred(error) {
            console.log("错误发生:", error.message)
        }
        
        function onWarningOccurred(warning) {
            console.log("警告发生:", warning.message)
        }
    }
    
    // 初始化
    Component.onCompleted: {
        // 添加一些测试日志
        errorHandler.log_info("应用程序启动", "初始化完成")
        errorHandler.log_debug("系统信息", "内存: 8GB, 磁盘: 256GB", "System")
        
        // 初始加载日志
        refreshLogs()
    }
}

4. 实战项目:企业数据管理系统

现在让我们将所有技术整合到一个完整的实战项目中:

4.1 系统架构

4.2 核心模块实现

python 复制代码
# enterprise_system.py - 企业数据管理系统核心
import sys
import os
from datetime import datetime, timedelta
from typing import Dict, Any, List, Optional, Tuple
from dataclasses import dataclass, field, asdict
from enum import Enum
import json
import csv
import sqlite3
from pathlib import Path

from PySide6.QtCore import (
    QObject, 
    Slot, 
    Property, 
    Signal, 
    QUrl,
    QTimer,
    QThreadPool,
    QSettings,
    QStandardPaths
)
from PySide6.QtGui import QGuiApplication
from PySide6.QtQml import QQmlApplicationEngine, QmlElement


class DataSourceType(Enum):
    """数据源类型"""
    DATABASE = "database"
    CSV_FILE = "csv_file"
    EXCEL_FILE = "excel_file"
    JSON_FILE = "json_file"
    API = "api"
    CUSTOM = "custom"


class DataPermission(Enum):
    """数据权限"""
    READ = "read"
    WRITE = "write"
    DELETE = "delete"
    ADMIN = "admin"


@dataclass
class DataSource:
    """数据源定义"""
    id: str
    name: str
    type: DataSourceType
    connection_string: str
    description: str = ""
    enabled: bool = True
    created_at: datetime = field(default_factory=datetime.now)
    updated_at: datetime = field(default_factory=datetime.now)
    config: Dict[str, Any] = field(default_factory=dict)
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        data = asdict(self)
        data['type'] = self.type.value
        data['created_at'] = self.created_at.isoformat()
        data['updated_at'] = self.updated_at.isoformat()
        return data


@dataclass
class DataSet:
    """数据集定义"""
    id: str
    name: str
    source_id: str
    query: str
    description: str = ""
    columns: List[Dict[str, Any]] = field(default_factory=list)
    row_count: int = 0
    last_refresh: Optional[datetime] = None
    refresh_interval: int = 0  # 秒
    enabled: bool = True
    
    def to_dict(self) -> Dict[str, Any]:
        """转换为字典"""
        data = asdict(self)
        if self.last_refresh:
            data['last_refresh'] = self.last_refresh.isoformat()
        return data


@QmlElement
class DataSourceManager(QObject):
    """数据源管理器"""
    
    # 信号
    sourceAdded = Signal(dict)
    sourceUpdated = Signal(dict)
    sourceRemoved = Signal(str)
    sourceConnected = Signal(str, bool, str)  # id, success, message
    dataRefreshed = Signal(str, list)  # dataset_id, data
    
    def __init__(self, parent=None):
        super().__init__(parent)
        self.sources: Dict[str, DataSource] = {}
        self.datasets: Dict[str, DataSet] = {}
        self.connections: Dict[str, Any] = {}  # 数据库连接等
        
        # 加载配置
        self._load_config()
        
        # 自动刷新定时器
        self.refresh_timer = QTimer()
        self.refresh_timer.setInterval(60000)  # 1分钟
        self.refresh_timer.timeout.connect(self._auto_refresh)
        self.refresh_timer.start()
    
    @Slot(str, str, str, str, result=str)
    def add_source(self, name: str, type_str: str, connection: str, description: str = "") -> str:
        """添加数据源"""
        try:
            source_type = DataSourceType(type_str)
            source_id = f"src_{datetime.now().timestamp()}"
            
            source = DataSource(
                id=source_id,
                name=name,
                type=source_type,
                connection_string=connection,
                description=description
            )
            
            self.sources[source_id] = source
            self.sourceAdded.emit(source.to_dict())
            self._save_config()
            
            return source_id
        except Exception as e:
            print(f"添加数据源失败: {e}")
            return ""
    
    @Slot(str, result=bool)
    def connect_source(self, source_id: str) -> bool:
        """连接数据源"""
        if source_id not in self.sources:
            self.sourceConnected.emit(source_id, False, "数据源不存在")
            return False
        
        source = self.sources[source_id]
        
        try:
            if source.type == DataSourceType.DATABASE:
                # 连接数据库
                conn = sqlite3.connect(source.connection_string)
                self.connections[source_id] = conn
                
                self.sourceConnected.emit(source_id, True, "连接成功")
                return True
                
            elif source.type == DataSourceType.CSV_FILE:
                # 验证CSV文件
                if not os.path.exists(source.connection_string):
                    self.sourceConnected.emit(source_id, False, "文件不存在")
                    return False
                
                self.sourceConnected.emit(source_id, True, "文件验证成功")
                return True
                
            else:
                # 其他类型
                self.sourceConnected.emit(source_id, True, "连接成功")
                return True
                
        except Exception as e:
            error_msg = f"连接失败: {str(e)}"
            self.sourceConnected.emit(source_id, False, error_msg)
            return False
    
    @Slot(str, str, str, str, result=str)
    def add_dataset(self, name: str, source_id: str, query: str, description: str = "") -> str:
        """添加数据集"""
        if source_id not in self.sources:
            return ""
        
        dataset_id = f"ds_{datetime.now().timestamp()}"
        
        dataset = DataSet(
            id=dataset_id,
            name=name,
            source_id=source_id,
            query=query,
            description=description
        )
        
        self.datasets[dataset_id] = dataset
        self._save_config()
        
        # 初始加载数据
        self.refresh_dataset(dataset_id)
        
        return dataset_id
    
    @Slot(str, result=list)
    def refresh_dataset(self, dataset_id: str) -> list:
        """刷新数据集"""
        if dataset_id not in self.datasets:
            return []
        
        dataset = self.datasets[dataset_id]
        source = self.sources.get(dataset.source_id)
        
        if not source:
            return []
        
        try:
            data = []
            
            if source.type == DataSourceType.DATABASE:
                # 从数据库查询
                if dataset.source_id in self.connections:
                    conn = self.connections[dataset.source_id]
                    cursor = conn.cursor()
                    cursor.execute(dataset.query)
                    
                    # 获取列名
                    columns = [desc[0] for desc in cursor.description]
                    dataset.columns = [{"name": col, "type": "string"} for col in columns]
                    
                    # 获取数据
                    rows = cursor.fetchall()
                    for row in rows:
                        item = {}
                        for i, col in enumerate(columns):
                            item[col] = row[i]
                        data.append(item)
                    
                    dataset.row_count = len(data)
            
            elif source.type == DataSourceType.CSV_FILE:
                # 从CSV文件读取
                with open(source.connection_string, 'r', encoding='utf-8') as f:
                    reader = csv.DictReader(f)
                    
                    # 获取列名
                    if reader.fieldnames:
                        dataset.columns = [{"name": col, "type": "string"} for col in reader.fieldnames]
                    
                    # 读取数据
                    for row in reader:
                        data.append(row)
                    
                    dataset.row_count = len(data)
            
            # 更新数据集信息
            dataset.last_refresh = datetime.now()
            
            # 发射信号
            self.dataRefreshed.emit(dataset_id, data)
            
            return data
            
        except Exception as e:
            print(f"刷新数据集失败: {e}")
            return []
    
    @Slot(str, result=list)
    def get_dataset_data(self, dataset_id: str) -> list:
        """获取数据集数据"""
        # 这里应该实现数据缓存逻辑
        return []
    
    @Slot(str, result=list)
    def query(self, query_str: str) -> list:
        """执行查询"""
        # 这里可以实现一个简单的查询引擎
        return []
    
    def _auto_refresh(self):
        """自动刷新数据集"""
        now = datetime.now()
        
        for dataset in self.datasets.values():
            if dataset.enabled and dataset.refresh_interval > 0:
                if not dataset.last_refresh or \
                   (now - dataset.last_refresh).total_seconds() >= dataset.refresh_interval:
                    self.refresh_dataset(dataset.id)
    
    def _load_config(self):
        """加载配置"""
        config_path = self._get_config_path()
        if config_path.exists():
            try:
                with open(config_path, 'r', encoding='utf-8') as f:
                    config = json.load(f)
                    
                    # 加载数据源
                    for source_data in config.get('sources', []):
                        source = DataSource(
                            id=source_data['id'],
                            name=source_data['name'],
                            type=DataSourceType(source_data['type']),
                            connection_string=source_data['connection_string'],
                            description=source_data.get('description', ''),
                            enabled=source_data.get('enabled', True)
                        )
                        self.sources[source.id] = source
                    
                    # 加载数据集
                    for dataset_data in config.get('datasets', []):
                        dataset = DataSet(
                            id=dataset_data['id'],
                            name=dataset_data['name'],
                            source_id=dataset_data['source_id'],
                            query=dataset_data['query'],
                            description=dataset_data.get('description', ''),
                            refresh_interval=dataset_data.get('refresh_interval', 0),
                            enabled=dataset_data.get('enabled', True)
                        )
                        self.datasets[dataset.id] = dataset
                        
            except Exception as e:
                print(f"加载配置失败: {e}")
    
    def _save_config(self):
        """保存配置"""
        config = {
            'sources': [source.to_dict() for source in self.sources.values()],
            'datasets': [dataset.to_dict() for dataset in self.datasets.values()]
        }
        
        config_path = self._get_config_path()
        config_path.parent.mkdir(parents=True, exist_ok=True)
        
        with open(config_path, 'w', encoding='utf-8') as f:
            json.dump(config, f, indent=2, ensure_ascii=False)
    
    def _get_config_path(self) -> Path:
        """获取配置文件路径"""
        app_data = QStandardPaths.writableLocation(QStandardPaths.AppDataLocation)
        return Path(app_data) / "enterprise_system" / "config.json"

由于篇幅限制,这里只展示了企业数据管理系统的核心部分。完整的系统应该包括:

  1. 用户管理模块​ - 用户认证、权限控制

  2. 报表生成模块​ - 数据报表、图表导出

  3. 工作流引擎​ - 业务流程自动化

  4. 通知系统​ - 邮件、桌面通知

  5. API网关​ - 与其他系统集成

  6. 监控系统​ - 性能监控、告警

5. 性能优化与调试

5.1 性能优化策略

5.2 调试技巧

  1. 使用Qt Creator调试器

  2. 添加详细的日志记录

  3. 使用性能分析工具

  4. 内存泄漏检测

  5. QML控制台输出

总结

通过本篇的学习,你已经掌握了Python与QML深度集成的核心技术:

  1. 上下文属性​ - 全局数据共享

  2. 双向数据绑定​ - 响应式数据流

  3. 模型视图架构​ - 高效数据展示

  4. 异步编程​ - 多线程处理

  5. 错误处理​ - 健壮性保障

  6. 实战项目​ - 企业级应用开发

这些技术是构建现代桌面应用程序的基础。在实际开发中,还需要结合具体业务需求,灵活运用这些技术。

记住,良好的架构设计和代码组织是成功的关键。

相关推荐
北冥有羽Victoria2 小时前
Django Auth组件完整版教程:从原理到项目落地
大数据·服务器·数据库·后端·python·django·sqlite
斯维赤2 小时前
Python学习超简单第八弹:网络编程
网络·python·学习
wengqidaifeng3 小时前
C++从菜鸟到强手:1.基础入门
开发语言·c++
我喜欢山,也喜欢海3 小时前
Java和go在并发上的表现为什么不一样
java·python·golang
hhb_6183 小时前
PHP 8.x 核心特性与工程化开发实践指南
开发语言·php
geovindu4 小时前
go: Flyweight Pattern
开发语言·设计模式·golang·享元模式
Wenzar_4 小时前
**零信任架构下的微服务权限控制:用Go实现基于JWT的动态访问策略**在现代云原生环境中,
java·python·微服务·云原生·架构
不是起点的终点4 小时前
【实战】Python 一键生成数据库说明文档(对接阿里云百炼 AI,输出 Word 格式)
数据库·python·阿里云
xyq20245 小时前
TypeScript中的String类型详解
开发语言