《PySide6 GUI开发指南:QML核心与实践》 第二篇:QML语法精要——构建声明式UI的基础

📖 前言:掌握语言的DNA

如果说第一篇是探索GUI开发的"星辰大海",那么本篇将带你深入QML语言的"基因序列"。就像建筑师需要精通建筑材料,画家需要掌握色彩理论一样,要成为QML开发高手,你必须深入理解这门声明式语言的每一个语法细节、每一种设计模式和每一种最佳实践。

在本篇中,我们将通过构建一个功能完整的计算器应用,系统性地学习QML的核心语法。这不是一个简单的"Hello World",而是一个包含多种交互、状态管理和布局技巧的实用工具。通过这个项目,你将不仅仅是"知道"语法,更是"掌握"如何运用这些语法构建真实的应用程序。

准备好探索QML语言的奥秘了吗?让我们开始吧!


🎯 本篇学习目标

阅读完本篇,你将能够:

  • ✅ 深入理解QML对象模型和属性系统

  • ✅ 掌握QML的信号槽机制和事件处理

  • ✅ 精通QML的布局系统和视觉元素

  • ✅ 学会使用状态和转换创建动态UI

  • ✅ 理解组件化开发和代码复用

  • ✅ 掌握JavaScript在QML中的高级用法

  • ✅ 独立构建一个功能完整的计算器应用

  • ✅ 理解QML代码的性能优化原则


📊 知识地图:QML语法全览

1. QML对象模型:一切都是对象

1.1 对象声明与属性

QML的核心是对象树。在QML中,一切皆是对象,每个对象都有属性、信号和方法。让我们从最基本的对象声明开始。

基本对象声明语法:

javascript 复制代码
// 语法:对象类型 { 属性: 值; 子对象 {} }
对象类型 {
    id: 对象标识符
    属性1: 值1
    属性2: 值2
    
    子对象类型 {
        id: 子对象标识符
        属性: 值
    }
}

实际示例:

javascript 复制代码
import QtQuick 2.15

Rectangle {
    // id是对象的唯一标识符,在同一作用域内必须唯一
    id: root
    
    // 基本属性设置
    width: 300
    height: 200
    color: "lightblue"
    radius: 10
    
    // 子对象
    Text {
        id: titleText
        text: "欢迎使用QML"
        font.pixelSize: 24
        color: "white"
        anchors.centerIn: parent
    }
}

1.2 属性系统深度解析

QML的属性系统是其响应式编程的核心。理解属性系统是掌握QML的关键。

1.2.1 属性类型
javascript 复制代码
Rectangle {
    id: example
    
    // 1. 基本类型属性
    property int count: 0                    // 整数
    property real scale: 1.0                 // 浮点数
    property bool isActive: true             // 布尔值
    property string message: "Hello"         // 字符串
    property color backgroundColor: "blue"   // 颜色
    property var complexData: {              // 任意类型
        "name": "QML",
        "version": 2.15
    }
    
    // 2. 列表类型属性
    property list<Item> items                // Item对象列表
    property list<string> names: ["Alice", "Bob"]  // 字符串列表
    
    // 3. 对象类型属性
    property Rectangle targetRect: null      // Rectangle对象引用
    
    // 4. 只读属性
    readonly property int calculatedValue: width * height
    
    // 5. 别名属性
    property alias textValue: innerText.text
    property alias innerColor: innerRect.color
    
    // 子对象
    Rectangle {
        id: innerRect
        width: 100; height: 50
        color: "red"
        
        Text {
            id: innerText
            text: "Inner Text"
        }
    }
}
1.2.2 属性绑定与赋值

理解属性绑定与赋值的区别至关重要:

代码示例对比:

javascript 复制代码
import QtQuick 2.15

Rectangle {
    id: container
    width: 400
    height: 300
    
    // 示例1:属性绑定(响应式)
    Rectangle {
        id: boundRect
        width: parent.width * 0.5  // 绑定到父对象宽度的一半
        height: parent.height * 0.5
        color: "lightgreen"
        
        Text {
            text: "绑定宽度:" + width
            anchors.centerIn: parent
        }
    }
    
    // 示例2:属性赋值(静态)
    Rectangle {
        id: assignedRect
        width: 100  // 固定值
        height: 100
        color: "lightcoral"
        x: 10
        y: boundRect.height + 20
        
        Text {
            text: "固定宽度:" + parent.width
            anchors.centerIn: parent
        }
    }
    
    // 示例3:动态改变绑定
    Rectangle {
        id: dynamicRect
        property bool useHalfWidth: true
        
        // 条件绑定
        width: useHalfWidth ? parent.width * 0.5 : 200
        height: 100
        color: "lightblue"
        x: 10
        y: assignedRect.y + assignedRect.height + 20
        
        MouseArea {
            anchors.fill: parent
            onClicked: parent.useHalfWidth = !parent.useHalfWidth
        }
        
        Text {
            text: "点击切换绑定\n当前:" + (parent.useHalfWidth ? "一半宽度" : "200像素")
            anchors.centerIn: parent
        }
    }
    
    // 控制按钮:演示绑定与赋值的区别
    Rectangle {
        id: controlPanel
        width: 200
        height: 80
        color: "#f0f0f0"
        radius: 5
        border.color: "#ccc"
        x: boundRect.width + 20
        y: 20
        
        Column {
            anchors.centerIn: parent
            spacing: 5
            
            Text {
                text: "容器宽度:" + container.width
                font.bold: true
            }
            
            Button {
                text: "增大容器宽度"
                onClicked: container.width += 50
            }
            
            Button {
                text: "绑定矩形宽度 = 100"
                onClicked: boundRect.width = 100  // 这会破坏绑定!
            }
        }
    }
}

关键点:

  1. 绑定:使用冒号(:)创建响应式关系

  2. 赋值:使用等号(=)设置静态值(会破坏已有绑定)

  3. 重新绑定 :使用Qt.binding()函数

javascript 复制代码
// 重新绑定的正确方式
function restoreBinding() {
    boundRect.width = Qt.binding(function() { 
        return parent.width * 0.5 
    })
}

1.3 信号与处理器

信号和处理器是QML中对象间通信的基础。理解这种机制对于创建交互式应用至关重要。

1.3.1 信号定义与发射
javascript 复制代码
import QtQuick 2.15

Rectangle {
    id: signalExample
    width: 300
    height: 200
    color: "lightgray"
    
    // 1. 定义信号
    signal buttonClicked(string buttonName, int clickCount)
    signal dataChanged(var newData)
    signal simpleSignal  // 无参数信号
    
    // 2. 发射信号的方法
    function triggerSignals() {
        // 方法1:直接调用信号函数
        buttonClicked("main", 5)
        
        // 方法2:使用emit关键字
        emit dataChanged({"value": 42, "status": "active"})
        
        // 方法3:简单信号
        simpleSignal()
    }
    
    // 3. 自动信号(某些属性变化时自动发射)
    property int counter: 0
    onCounterChanged: {
        console.log("计数器变化:", counter)
    }
    
    Column {
        anchors.centerIn: parent
        spacing: 10
        
        Button {
            text: "发射所有信号"
            onClicked: signalExample.triggerSignals()
        }
        
        Button {
            text: "增加计数 (" + signalExample.counter + ")"
            onClicked: signalExample.counter++
        }
    }
}
1.3.2 信号处理方式

QML提供了多种方式处理信号,每种方式都有其适用场景:

javascript 复制代码
import QtQuick 2.15
import QtQuick.Controls 2.15

Rectangle {
    id: handlerExample
    width: 500
    height: 400
    color: "#f5f5f5"
    
    // 定义一些测试信号
    signal testSignal(string message)
    signal numberSignal(int value)
    signal complexSignal(var data)
    
    // 子对象
    Rectangle {
        id: sourceObject
        width: 200
        height: 150
        color: "lightblue"
        radius: 10
        x: 20
        y: 20
        
        signal innerSignal(string info)
        
        Text {
            text: "信号源"
            anchors.centerIn: parent
            font.bold: true
        }
        
        MouseArea {
            anchors.fill: parent
            onClicked: {
                // 发射内部信号
                sourceObject.innerSignal("来自内部的信号")
                // 发射父对象信号
                handlerExample.testSignal("通过点击发送")
            }
        }
    }
    
    // 日志显示
    Rectangle {
        id: logArea
        width: 460
        height: 200
        color: "white"
        border.color: "#ddd"
        radius: 5
        x: 20
        y: 180
        
        ListView {
            id: logView
            anchors.fill: parent
            anchors.margins: 5
            model: ListModel {}
            
            delegate: Text {
                text: message
                color: type === "error" ? "red" : "black"
                font.pixelSize: 12
            }
        }
        
        function addLog(type, msg) {
            logView.model.insert(0, {"type": type, "message": msg})
            if (logView.model.count > 10) {
                logView.model.remove(10)
            }
        }
    }
    
    // ==================== 信号处理方式1:内联处理器 ====================
    Rectangle {
        id: method1
        width: 150
        height: 100
        color: "#e8f5e8"
        radius: 5
        border.color: "#4caf50"
        x: 240
        y: 20
        
        Text {
            text: "方法1\n内联处理器"
            anchors.centerIn: parent
            horizontalAlignment: Text.AlignHCenter
        }
        
        // 内联处理器 - 直接在信号源上定义
        Connections {
            target: sourceObject
            function onInnerSignal(info) {
                logArea.addLog("info", "内联处理器: " + info)
            }
        }
    }
    
    // ==================== 信号处理方式2:onSignal语法 ====================
    Rectangle {
        id: method2
        width: 150
        height: 100
        color: "#fff3e0"
        radius: 5
        border.color: "#ff9800"
        x: 240
        y: 130
        
        Text {
            text: "方法2\nonSignal语法"
            anchors.centerIn: parent
            horizontalAlignment: Text.AlignHCenter
        }
        
        // onSignal语法 - 在对象自身中处理
        Component.onCompleted: {
            console.log("组件加载完成")
        }
        
        onWidthChanged: {
            logArea.addLog("info", "宽度变化: " + width)
        }
    }
    
    // ==================== 信号处理方式3:Connections对象 ====================
    Rectangle {
        id: method3
        width: 150
        height: 100
        color: "#f3e5f5"
        radius: 5
        border.color: "#9c27b0"
        x: 240
        y: 240
        
        Text {
            text: "方法3\nConnections对象"
            anchors.centerIn: parent
            horizontalAlignment: Text.AlignHCenter
        }
    }
    
    // 全局Connections对象
    Connections {
        target: handlerExample
        
        // 使用新的函数语法
        function onTestSignal(message) {
            logArea.addLog("signal", "Connections处理: " + message)
        }
        
        function onNumberSignal(value) {
            logArea.addLog("signal", "数字信号: " + value)
        }
    }
    
    // ==================== 信号处理方式4:信号连接函数 ====================
    Rectangle {
        id: method4
        width: 150
        height: 100
        color: "#e3f2fd"
        radius: 5
        border.color: "#2196f3"
        x: 240
        y: 350
        
        Text {
            text: "方法4\nconnect()函数"
            anchors.centerIn: parent
            horizontalAlignment: Text.AlignHCenter
        }
        
        Component.onCompleted: {
            // 使用connect函数动态连接信号
            handlerExample.testSignal.connect(function(msg) {
                logArea.addLog("dynamic", "动态连接: " + msg)
            })
            
            // 连接到对象方法
            handlerExample.numberSignal.connect(handleNumber)
        }
        
        function handleNumber(value) {
            logArea.addLog("dynamic", "处理方法: " + value)
        }
    }
    
    // 测试按钮
    Column {
        x: 20
        y: 390
        spacing: 5
        
        Button {
            text: "发射测试信号"
            onClicked: handlerExample.testSignal("测试消息")
        }
        
        Button {
            text: "发射数字信号"
            onClicked: handlerExample.numberSignal(Math.floor(Math.random() * 100))
        }
        
        Button {
            text: "发射复杂信号"
            onClicked: handlerExample.complexSignal({
                "name": "测试数据",
                "value": 123,
                "active": true
            })
        }
        
        Button {
            text: "清空日志"
            onClicked: logView.model.clear()
        }
    }
}

信号处理方式对比表:

处理方式 语法示例 优点 缺点 适用场景
内联处理器 onClicked: { ... } 简洁直观 只能处理一个信号 简单交互
onSignal语法 onWidthChanged: ... 自动生成处理器 信号名必须匹配 属性变化处理
Connections对象 Connections { target: obj; onSignal: ... } 灵活,可连接任意对象 语法稍复杂 连接外部对象信号
connect()函数 signal.connect(handler) 动态连接/断开 需要手动管理 运行时动态连接

2. 视觉元素与布局系统

2.1 基本视觉元素

QML提供了丰富的视觉元素,让我们从最基本的开始:

javascript 复制代码
import QtQuick 2.15

Rectangle {
    id: visualElements
    width: 600
    height: 500
    color: "#f8f9fa"
    
    // 标题
    Text {
        text: "QML基本视觉元素"
        font.pixelSize: 24
        font.bold: true
        color: "#2c3e50"
        anchors.horizontalCenter: parent.horizontalCenter
        y: 20
    }
    
    Grid {
        id: elementsGrid
        columns: 2
        spacing: 20
        anchors.centerIn: parent
        
        // ==================== 1. Rectangle ====================
        Column {
            spacing: 5
            
            Text {
                text: "1. Rectangle"
                font.bold: true
                color: "#3498db"
            }
            
            Rectangle {
                width: 200
                height: 120
                color: "#3498db"
                radius: 10
                border.color: "#2980b9"
                border.width: 2
                
                // 渐变填充
                gradient: Gradient {
                    GradientStop { position: 0.0; color: "#3498db" }
                    GradientStop { position: 1.0; color: "#2c3e50" }
                }
                
                Text {
                    text: "圆角矩形\n带边框和渐变"
                    color: "white"
                    anchors.centerIn: parent
                    horizontalAlignment: Text.AlignHCenter
                }
            }
        }
        
        // ==================== 2. Text ====================
        Column {
            spacing: 5
            
            Text {
                text: "2. Text"
                font.bold: true
                color: "#e74c3c"
            }
            
            Rectangle {
                width: 200
                height: 120
                color: "#f8f9fa"
                border.color: "#ddd"
                border.width: 1
                
                Column {
                    anchors.centerIn: parent
                    spacing: 8
                    
                    Text {
                        text: "普通文本"
                        color: "#2c3e50"
                    }
                    
                    Text {
                        text: "粗体文本"
                        font.bold: true
                        color: "#e74c3c"
                    }
                    
                    Text {
                        text: "大号文本"
                        font.pixelSize: 20
                        color: "#27ae60"
                    }
                    
                    Text {
                        text: "富文本<b>加粗</b> <i>斜体</i>"
                        textFormat: Text.RichText
                        color: "#8e44ad"
                    }
                }
            }
        }
        
        // ==================== 3. Image ====================
        Column {
            spacing: 5
            
            Text {
                text: "3. Image"
                font.bold: true
                color: "#2ecc71"
            }
            
            Rectangle {
                width: 200
                height: 120
                color: "#f8f9fa"
                border.color: "#ddd"
                border.width: 1
                
                // 使用在线图片或本地图片
                Image {
                    id: sampleImage
                    source: "https://via.placeholder.com/150x80/2ecc71/ffffff?text=QML+Image"
                    width: 150
                    height: 80
                    anchors.centerIn: parent
                    
                    // 图片填充模式
                    fillMode: Image.PreserveAspectFit
                    
                    // 异步加载
                    asynchronous: true
                    
                    // 加载状态处理
                    onStatusChanged: {
                        if (status === Image.Error) {
                            console.log("图片加载失败")
                        }
                    }
                }
                
                Text {
                    text: sampleImage.sourceSize.width + "×" + sampleImage.sourceSize.height
                    font.pixelSize: 10
                    color: "#7f8c8d"
                    anchors.bottom: parent.bottom
                    anchors.horizontalCenter: parent.horizontalCenter
                }
            }
        }
        
        // ==================== 4. MouseArea ====================
        Column {
            spacing: 5
            
            Text {
                text: "4. MouseArea"
                font.bold: true
                color: "#f39c12"
            }
            
            Rectangle {
                id: mouseRect
                width: 200
                height: 120
                color: "#f1c40f"
                radius: 10
                
                property int clickCount: 0
                
                Text {
                    text: "点击我\n点击次数:" + parent.clickCount
                    color: "white"
                    font.bold: true
                    anchors.centerIn: parent
                    horizontalAlignment: Text.AlignHCenter
                }
                
                // MouseArea覆盖整个矩形
                MouseArea {
                    anchors.fill: parent
                    
                    // 鼠标悬停
                    hoverEnabled: true
                    
                    // 点击事件
                    onClicked: {
                        mouseRect.clickCount++
                        mouseRect.color = Qt.lighter("#f1c40f", 1.2)
                    }
                    
                    // 双击事件
                    onDoubleClicked: {
                        mouseRect.clickCount = 0
                        mouseRect.color = "#f1c40f"
                    }
                    
                    // 鼠标进入
                    onEntered: {
                        mouseRect.border.width = 2
                        mouseRect.border.color = "#f39c12"
                    }
                    
                    // 鼠标离开
                    onExited: {
                        mouseRect.border.width = 0
                    }
                    
                    // 鼠标位置跟踪
                    onPositionChanged: {
                        var x = Math.floor(mouse.x)
                        var y = Math.floor(mouse.y)
                        // 可以在这里添加鼠标位置相关逻辑
                    }
                }
            }
        }
    }
    
    // ==================== 5. 其他常用元素 ====================
    Column {
        x: 20
        y: 320
        spacing: 10
        
        Text {
            text: "5. 其他常用元素"
            font.bold: true
            color: "#9b59b6"
        }
        
        Row {
            spacing: 10
            
            // Canvas - 自定义绘图
            Rectangle {
                width: 100
                height: 100
                color: "#f8f9fa"
                border.color: "#ddd"
                
                Canvas {
                    id: canvasExample
                    anchors.fill: parent
                    
                    onPaint: {
                        var ctx = getContext("2d")
                        ctx.clearRect(0, 0, width, height)
                        
                        // 画圆
                        ctx.fillStyle = "#9b59b6"
                        ctx.beginPath()
                        ctx.arc(50, 50, 40, 0, Math.PI * 2)
                        ctx.fill()
                        
                        // 画文字
                        ctx.fillStyle = "white"
                        ctx.font = "bold 16px Arial"
                        ctx.textAlign = "center"
                        ctx.textBaseline = "middle"
                        ctx.fillText("Canvas", 50, 50)
                    }
                }
            }
            
            // Loader - 动态加载组件
            Rectangle {
                width: 100
                height: 100
                color: "#f8f9fa"
                border.color: "#ddd"
                
                property bool showCircle: true
                
                Loader {
                    id: loaderExample
                    anchors.fill: parent
                    sourceComponent: parent.showCircle ? circleComponent : rectComponent
                }
                
                Component {
                    id: circleComponent
                    Rectangle {
                        color: "#e74c3c"
                        radius: width / 2
                        Text {
                            text: "圆形"
                            color: "white"
                            anchors.centerIn: parent
                        }
                    }
                }
                
                Component {
                    id: rectComponent
                    Rectangle {
                        color: "#3498db"
                        Text {
                            text: "矩形"
                            color: "white"
                            anchors.centerIn: parent
                        }
                    }
                }
                
                MouseArea {
                    anchors.fill: parent
                    onClicked: parent.showCircle = !parent.showCircle
                }
            }
            
            // Repeater - 重复元素
            Rectangle {
                width: 100
                height: 100
                color: "#f8f9fa"
                border.color: "#ddd"
                
                Flow {
                    anchors.fill: parent
                    anchors.margins: 5
                    
                    Repeater {
                        model: 9
                        
                        Rectangle {
                            width: 20
                            height: 20
                            color: index % 2 ? "#2ecc71" : "#27ae60"
                            Text {
                                text: index + 1
                                color: "white"
                                font.pixelSize: 10
                                anchors.centerIn: parent
                            }
                        }
                    }
                }
            }
        }
    }
}

2.2 布局系统详解

QML提供了多种布局方式,每种方式适合不同的场景。理解各种布局的优缺点对于创建响应式UI至关重要。

2.2.1 基础布局容器
javascript 复制代码
import QtQuick 2.15
import QtQuick.Layouts 1.15

Rectangle {
    id: layoutDemo
    width: 800
    height: 600
    color: "#f5f7fa"
    
    // 标题
    Text {
        text: "QML布局系统详解"
        font.pixelSize: 28
        font.bold: true
        color: "#1a237e"
        anchors.horizontalCenter: parent.horizontalCenter
        y: 20
    }
    
    // 布局选择标签
    TabBar {
        id: layoutTabs
        width: parent.width - 40
        anchors.horizontalCenter: parent.horizontalCenter
        y: 70
        
        TabButton { text: "Column/Row" }
        TabButton { text: "Grid" }
        TabButton { text: "锚点布局" }
        TabButton { text: "GridLayout" }
        TabButton { text: "综合示例" }
    }
    
    // 布局内容
    StackLayout {
        width: parent.width - 40
        height: 400
        anchors.horizontalCenter: parent.horizontalCenter
        y: 120
        currentIndex: layoutTabs.currentIndex
        
        // ==================== 1. Column和Row布局 ====================
        Rectangle {
            id: columnRowTab
            color: "transparent"
            
            Column {
                spacing: 20
                
                // Column示例
                Column {
                    spacing: 10
                    
                    Text {
                        text: "Column布局(垂直排列)"
                        font.bold: true
                        color: "#0d47a1"
                    }
                    
                    Rectangle {
                        width: 300
                        height: 150
                        color: "white"
                        border.color: "#ddd"
                        radius: 5
                        
                        Column {
                            id: columnExample
                            anchors.centerIn: parent
                            spacing: 8
                            
                            Repeater {
                                model: 4
                                
                                Rectangle {
                                    width: 200
                                    height: 30
                                    color: ["#bbdefb", "#90caf9", "#64b5f6", "#42a5f5"][index]
                                    radius: 3
                                    
                                    Text {
                                        text: "项目 " + (index + 1)
                                        color: "white"
                                        anchors.centerIn: parent
                                    }
                                }
                            }
                        }
                    }
                }
                
                // Row示例
                Column {
                    spacing: 10
                    
                    Text {
                        text: "Row布局(水平排列)"
                        font.bold: true
                        color: "#0d47a1"
                    }
                    
                    Rectangle {
                        width: 300
                        height: 150
                        color: "white"
                        border.color: "#ddd"
                        radius: 5
                        
                        Row {
                            id: rowExample
                            anchors.centerIn: parent
                            spacing: 8
                            
                            Repeater {
                                model: 4
                                
                                Rectangle {
                                    width: 50
                                    height: 80
                                    color: ["#c8e6c9", "#a5d6a7", "#81c784", "#66bb6a"][index]
                                    radius: 3
                                    
                                    Text {
                                        text: index + 1
                                        color: "white"
                                        font.bold: true
                                        font.pixelSize: 20
                                        anchors.centerIn: parent
                                    }
                                }
                            }
                        }
                    }
                }
                
                // 布局属性控制
                Column {
                    spacing: 10
                    width: 300
                    
                    Text {
                        text: "布局属性控制"
                        font.bold: true
                        color: "#0d47a1"
                    }
                    
                    Row {
                        spacing: 10
                        
                        Column {
                            spacing: 5
                            
                            Text { text: "间距:"; font.pixelSize: 12 }
                            Slider {
                                id: spacingSlider
                                from: 0
                                to: 30
                                value: 8
                                onValueChanged: {
                                    columnExample.spacing = value
                                    rowExample.spacing = value
                                }
                            }
                        }
                        
                        Column {
                            spacing: 5
                            
                            Text { text: "对齐方式:"; font.pixelSize: 12 }
                            ComboBox {
                                id: alignmentCombo
                                model: ["左对齐", "居中", "右对齐"]
                                onCurrentIndexChanged: {
                                    var alignments = [Qt.AlignLeft, Qt.AlignHCenter, Qt.AlignRight]
                                    columnExample.horizontalAlignment = alignments[currentIndex]
                                }
                            }
                        }
                    }
                }
            }
        }
        
        // ==================== 2. Grid布局 ====================
        Rectangle {
            id: gridTab
            color: "transparent"
            
            Column {
                spacing: 20
                
                // 基础Grid示例
                Column {
                    spacing: 10
                    
                    Text {
                        text: "Grid布局(网格排列)"
                        font.bold: true
                        color: "#004d40"
                    }
                    
                    Rectangle {
                        width: 300
                        height: 200
                        color: "white"
                        border.color: "#ddd"
                        radius: 5
                        
                        Grid {
                            id: gridExample
                            anchors.centerIn: parent
                            columns: 3
                            rows: 3
                            spacing: 5
                            
                            Repeater {
                                model: 9
                                
                                Rectangle {
                                    width: 60
                                    height: 60
                                    color: ["#b2dfdb", "#80cbc4", "#4db6ac", "#26a69a"][index % 4]
                                    radius: 5
                                    
                                    Text {
                                        text: index + 1
                                        color: "white"
                                        font.bold: true
                                        font.pixelSize: 20
                                        anchors.centerIn: parent
                                    }
                                }
                            }
                        }
                    }
                }
                
                // Grid属性控制
                Column {
                    spacing: 10
                    width: 300
                    
                    Text {
                        text: "网格属性控制"
                        font.bold: true
                        color: "#004d40"
                    }
                    
                    Grid {
                        columns: 2
                        spacing: 10
                        
                        Column {
                            spacing: 5
                            
                            Text { text: "列数:"; font.pixelSize: 12 }
                            SpinBox {
                                id: columnsSpin
                                from: 1
                                to: 5
                                value: 3
                                onValueChanged: gridExample.columns = value
                            }
                        }
                        
                        Column {
                            spacing: 5
                            
                            Text { text: "行数:"; font.pixelSize: 12 }
                            SpinBox {
                                id: rowsSpin
                                from: 1
                                to: 5
                                value: 3
                                onValueChanged: gridExample.rows = value
                            }
                        }
                        
                        Column {
                            spacing: 5
                            
                            Text { text: "水平间距:"; font.pixelSize: 12 }
                            Slider {
                                id: hSpacingSlider
                                from: 0
                                to: 20
                                value: 5
                                onValueChanged: gridExample.spacing = Qt.point(value, vSpacingSlider.value)
                            }
                        }
                        
                        Column {
                            spacing: 5
                            
                            Text { text: "垂直间距:"; font.pixelSize: 12 }
                            Slider {
                                id: vSpacingSlider
                                from: 0
                                to: 20
                                value: 5
                                onValueChanged: gridExample.spacing = Qt.point(hSpacingSlider.value, value)
                            }
                        }
                    }
                }
                
                // 流式布局
                Column {
                    spacing: 10
                    
                    Text {
                        text: "流式布局(Flow)"
                        font.bold: true
                        color: "#004d40"
                    }
                    
                    Rectangle {
                        width: 300
                        height: 120
                        color: "white"
                        border.color: "#ddd"
                        radius: 5
                        
                        Flow {
                            anchors.fill: parent
                            anchors.margins: 10
                            spacing: 5
                            
                            Repeater {
                                model: 12
                                
                                Rectangle {
                                    width: 40
                                    height: 40
                                    color: index % 2 ? "#ffccbc" : "#ffab91"
                                    radius: 3
                                    
                                    Text {
                                        text: index + 1
                                        anchors.centerIn: parent
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
        
        // ==================== 3. 锚点布局 ====================
        Rectangle {
            id: anchorsTab
            color: "transparent"
            
            Column {
                spacing: 20
                
                Text {
                    text: "锚点布局(相对定位)"
                    font.bold: true
                    color: "#bf360c"
                }
                
                Rectangle {
                    width: 400
                    height: 300
                    color: "white"
                    border.color: "#ddd"
                    radius: 5
                    
                    // 父容器
                    Rectangle {
                        id: anchorParent
                        width: 300
                        height: 200
                        color: "#f5f5f5"
                        border.color: "#bdbdbd"
                        anchors.centerIn: parent
                        
                        // 中心对象
                        Rectangle {
                            id: centerRect
                            width: 80
                            height: 80
                            color: "#ff9800"
                            radius: 10
                            anchors.centerIn: parent
                            
                            Text {
                                text: "中心"
                                anchors.centerIn: parent
                                color: "white"
                            }
                        }
                        
                        // 左上角
                        Rectangle {
                            width: 60
                            height: 40
                            color: "#2196f3"
                            anchors.left: parent.left
                            anchors.top: parent.top
                            anchors.margins: 10
                            
                            Text {
                                text: "左上"
                                anchors.centerIn: parent
                                color: "white"
                                font.pixelSize: 12
                            }
                        }
                        
                        // 右上角
                        Rectangle {
                            width: 60
                            height: 40
                            color: "#2196f3"
                            anchors.right: parent.right
                            anchors.top: parent.top
                            anchors.margins: 10
                            
                            Text {
                                text: "右上"
                                anchors.centerIn: parent
                                color: "white"
                                font.pixelSize: 12
                            }
                        }
                        
                        // 左下角
                        Rectangle {
                            width: 60
                            height: 40
                            color: "#2196f3"
                            anchors.left: parent.left
                            anchors.bottom: parent.bottom
                            anchors.margins: 10
                            
                            Text {
                                text: "左下"
                                anchors.centerIn: parent
                                color: "white"
                                font.pixelSize: 12
                            }
                        }
                        
                        // 右下角
                        Rectangle {
                            width: 60
                            height: 40
                            color: "#2196f3"
                            anchors.right: parent.right
                            anchors.bottom: parent.bottom
                            anchors.margins: 10
                            
                            Text {
                                text: "右下"
                                anchors.centerIn: parent
                                color: "white"
                                font.pixelSize: 12
                            }
                        }
                        
                        // 填充底部
                        Rectangle {
                            height: 30
                            color: "#4caf50"
                            anchors.left: parent.left
                            anchors.right: parent.right
                            anchors.bottom: parent.bottom
                            anchors.margins: 5
                            
                            Text {
                                text: "底部栏"
                                anchors.centerIn: parent
                                color: "white"
                            }
                        }
                        
                        // 填充顶部
                        Rectangle {
                            height: 30
                            color: "#9c27b0"
                            anchors.left: parent.left
                            anchors.right: parent.right
                            anchors.top: parent.top
                            anchors.margins: 5
                            
                            Text {
                                text: "顶部栏"
                                anchors.centerIn: parent
                                color: "white"
                            }
                        }
                    }
                }
                
                // 锚点关系图示
                Column {
                    spacing: 10
                    width: 400
                    
                    Text {
                        text: "锚点关系图示"
                        font.bold: true
                        color: "#bf360c"
                    }
                    
                    Rectangle {
                        width: 400
                        height: 120
                        color: "white"
                        border.color: "#ddd"
                        radius: 5
                        
                        // 图示说明
                        Grid {
                            anchors.centerIn: parent
                            columns: 2
                            spacing: 20
                            
                            Column {
                                spacing: 5
                                
                                Row {
                                    spacing: 5
                                    Rectangle { width: 20; height: 20; color: "#2196f3" }
                                    Text { text: "anchors.left"; font.pixelSize: 12 }
                                }
                                Row {
                                    spacing: 5
                                    Rectangle { width: 20; height: 20; color: "#4caf50" }
                                    Text { text: "anchors.right"; font.pixelSize: 12 }
                                }
                                Row {
                                    spacing: 5
                                    Rectangle { width: 20; height: 20; color: "#ff9800" }
                                    Text { text: "anchors.top"; font.pixelSize: 12 }
                                }
                                Row {
                                    spacing: 5
                                    Rectangle { width: 20; height: 20; color: "#9c27b0" }
                                    Text { text: "anchors.bottom"; font.pixelSize: 12 }
                                }
                            }
                            
                            Column {
                                spacing: 5
                                
                                Row {
                                    spacing: 5
                                    Rectangle { width: 20; height: 20; color: "#2196f3" }
                                    Text { text: "anchors.centerIn"; font.pixelSize: 12 }
                                }
                                Row {
                                    spacing: 5
                                    Rectangle { width: 20; height: 20; color: "#4caf50" }
                                    Text { text: "anchors.fill"; font.pixelSize: 12 }
                                }
                                Row {
                                    spacing: 5
                                    Rectangle { width: 20; height: 20; color: "#ff9800" }
                                    Text { text: "anchors.margins"; font.pixelSize: 12 }
                                }
                                Row {
                                    spacing: 5
                                    Rectangle { width: 20; height: 20; color: "#9c27b0" }
                                    Text { text: "anchors.horizontalCenter"; font.pixelSize: 12 }
                                }
                            }
                        }
                    }
                }
            }
        }
        
        // ==================== 4. GridLayout ====================
        Rectangle {
            id: gridLayoutTab
            color: "transparent"
            
            Column {
                spacing: 20
                
                Text {
                    text: "GridLayout(自动网格布局)"
                    font.bold: true
                    color: "#4a148c"
                }
                
                Rectangle {
                    width: 400
                    height: 300
                    color: "white"
                    border.color: "#ddd"
                    radius: 5
                    
                    GridLayout {
                        id: advancedGrid
                        anchors.fill: parent
                        anchors.margins: 20
                        columns: 3
                        rows: 3
                        columnSpacing: 10
                        rowSpacing: 10
                        
                        // 跨行跨列示例
                        Rectangle {
                            Layout.fillWidth: true
                            Layout.fillHeight: true
                            Layout.columnSpan: 2
                            Layout.rowSpan: 2
                            color: "#e1bee7"
                            radius: 5
                            
                            Text {
                                text: "2列 × 2行"
                                anchors.centerIn: parent
                            }
                        }
                        
                        Rectangle {
                            Layout.fillWidth: true
                            Layout.fillHeight: true
                            color: "#bbdefb"
                            radius: 5
                            
                            Text {
                                text: "单元格 3"
                                anchors.centerIn: parent
                            }
                        }
                        
                        Rectangle {
                            Layout.fillWidth: true
                            Layout.fillHeight: true
                            Layout.columnSpan: 2
                            color: "#c8e6c9"
                            radius: 5
                            
                            Text {
                                text: "跨2列"
                                anchors.centerIn: parent
                            }
                        }
                        
                        Rectangle {
                            Layout.fillWidth: true
                            Layout.fillHeight: true
                            Layout.rowSpan: 2
                            color: "#fff9c4"
                            radius: 5
                            
                            Text {
                                text: "跨2行"
                                anchors.centerIn: parent
                            }
                        }
                        
                        Rectangle {
                            Layout.fillWidth: true
                            Layout.fillHeight: true
                            color: "#ffccbc"
                            radius: 5
                            
                            Text {
                                text: "单元格 7"
                                anchors.centerIn: parent
                            }
                        }
                        
                        Rectangle {
                            Layout.fillWidth: true
                            Layout.fillHeight: true
                            color: "#f8bbd0"
                            radius: 5
                            
                            Text {
                                text: "单元格 8"
                                anchors.centerIn: parent
                            }
                        }
                    }
                }
                
                // GridLayout控制
                Column {
                    spacing: 10
                    width: 400
                    
                    Text {
                        text: "GridLayout属性控制"
                        font.bold: true
                        color: "#4a148c"
                    }
                    
                    Grid {
                        columns: 2
                        spacing: 10
                        
                        Column {
                            spacing: 5
                            
                            Text { text: "列数:"; font.pixelSize: 12 }
                            SpinBox {
                                from: 2
                                to: 4
                                value: 3
                                onValueChanged: advancedGrid.columns = value
                            }
                        }
                        
                        Column {
                            spacing: 5
                            
                            Text { text: "行数:"; font.pixelSize: 12 }
                            SpinBox {
                                from: 2
                                to: 4
                                value: 3
                                onValueChanged: advancedGrid.rows = value
                            }
                        }
                    }
                }
            }
        }
        
        // ==================== 5. 综合示例 ====================
        Rectangle {
            id: combinedTab
            color: "transparent"
            
            Column {
                spacing: 20
                
                Text {
                    text: "布局综合示例 - 模拟应用界面"
                    font.bold: true
                    color: "#006064"
                }
                
                Rectangle {
                    width: 500
                    height: 350
                    color: "white"
                    border.color: "#ddd"
                    radius: 5
                    
                    // 模拟应用界面
                    ColumnLayout {
                        anchors.fill: parent
                        anchors.margins: 10
                        spacing: 10
                        
                        // 顶部标题栏
                        Rectangle {
                            Layout.fillWidth: true
                            Layout.preferredHeight: 50
                            color: "#1565c0"
                            radius: 5
                            
                            RowLayout {
                                anchors.fill: parent
                                anchors.margins: 10
                                
                                Text {
                                    text: "我的应用"
                                    color: "white"
                                    font.bold: true
                                    font.pixelSize: 20
                                }
                                
                                Item { Layout.fillWidth: true }
                                
                                Rectangle {
                                    Layout.preferredWidth: 30
                                    Layout.preferredHeight: 30
                                    color: "white"
                                    radius: 15
                                    
                                    Text {
                                        text: "U"
                                        anchors.centerIn: parent
                                        color: "#1565c0"
                                        font.bold: true
                                    }
                                }
                            }
                        }
                        
                        // 主要内容区域
                        Rectangle {
                            Layout.fillWidth: true
                            Layout.fillHeight: true
                            color: "#f5f5f5"
                            radius: 5
                            
                            GridLayout {
                                anchors.fill: parent
                                anchors.margins: 10
                                columns: 2
                                
                                // 左侧边栏
                                ColumnLayout {
                                    Layout.fillHeight: true
                                    Layout.preferredWidth: 150
                                    spacing: 10
                                    
                                    Repeater {
                                        model: ["首页", "消息", "设置", "帮助", "关于"]
                                        
                                        Rectangle {
                                            Layout.fillWidth: true
                                            Layout.preferredHeight: 40
                                            color: index === 0 ? "#e3f2fd" : "white"
                                            border.color: index === 0 ? "#2196f3" : "#ddd"
                                            radius: 3
                                            
                                            Text {
                                                text: modelData
                                                anchors.centerIn: parent
                                                color: index === 0 ? "#2196f3" : "#666"
                                            }
                                        }
                                    }
                                    
                                    Item { Layout.fillHeight: true }
                                }
                                
                                // 右侧内容
                                ColumnLayout {
                                    Layout.fillWidth: true
                                    Layout.fillHeight: true
                                    spacing: 10
                                    
                                    // 卡片1
                                    Rectangle {
                                        Layout.fillWidth: true
                                        Layout.preferredHeight: 100
                                        color: "white"
                                        border.color: "#ddd"
                                        radius: 5
                                        
                                        Column {
                                            anchors.centerIn: parent
                                            spacing: 5
                                            
                                            Text {
                                                text: "欢迎使用"
                                                font.bold: true
                                                font.pixelSize: 18
                                            }
                                            
                                            Text {
                                                text: "这是一个综合布局示例,展示了多种布局方式的组合使用。"
                                                color: "#666"
                                            }
                                        }
                                    }
                                    
                                    // 卡片2
                                    Rectangle {
                                        Layout.fillWidth: true
                                        Layout.fillHeight: true
                                        color: "white"
                                        border.color: "#ddd"
                                        radius: 5
                                        
                                        GridLayout {
                                            anchors.fill: parent
                                            anchors.margins: 10
                                            columns: 2
                                            
                                            Repeater {
                                                model: 6
                                                
                                                Rectangle {
                                                    Layout.fillWidth: true
                                                    Layout.fillHeight: true
                                                    color: ["#f3e5f5", "#e8eaf6", "#e3f2fd"][index % 3]
                                                    radius: 3
                                                    
                                                    Text {
                                                        text: "项目 " + (index + 1)
                                                        anchors.centerIn: parent
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        
                        // 底部状态栏
                        Rectangle {
                            Layout.fillWidth: true
                            Layout.preferredHeight: 30
                            color: "#e0e0e0"
                            radius: 3
                            
                            Text {
                                text: "就绪"
                                anchors.verticalCenter: parent.verticalCenter
                                anchors.left: parent.left
                                anchors.leftMargin: 10
                                color: "#666"
                            }
                            
                            Text {
                                text: "© 2024 我的公司"
                                anchors.verticalCenter: parent.verticalCenter
                                anchors.right: parent.right
                                anchors.rightMargin: 10
                                color: "#666"
                                font.pixelSize: 12
                            }
                        }
                    }
                }
            }
        }
    }
}
2.2.2 布局选择指南

了解每种布局的适用场景很重要,这里是一个快速参考:

布局性能优化建议:

  1. 避免过度嵌套:布局嵌套不要超过4层

  2. 使用合适的布局:简单的排列用Column/Row,复杂的用Anchor

  3. 避免频繁变化:尽量减少布局属性的动态变化

  4. 使用Loader延迟加载:不需要立即显示的内容可以延迟加载

  5. 缓存复杂布局:使用CacheBuffer提高滚动性能


3. 状态管理与转换

3.1 状态系统基础

状态系统是QML创建动态交互界面的核心。让我们通过一个实际的例子来理解状态管理:

javascript 复制代码
import QtQuick 2.15
import QtQuick.Controls 2.15

Rectangle {
    id: stateDemo
    width: 600
    height: 400
    color: "#f8f9fa"
    
    // 标题
    Text {
        text: "QML状态管理系统"
        font.pixelSize: 24
        font.bold: true
        color: "#1a237e"
        anchors.horizontalCenter: parent.horizontalCenter
        y: 20
    }
    
    // 示例1:基本状态切换
    Column {
        id: basicExample
        x: 30
        y: 70
        spacing: 10
        
        Text {
            text: "1. 基本状态切换"
            font.bold: true
            color: "#0d47a1"
        }
        
        // 状态切换按钮
        Rectangle {
            id: toggleButton
            width: 200
            height: 50
            color: "#2196f3"
            radius: 5
            
            property bool isActive: false
            
            Text {
                text: parent.isActive ? "激活状态" : "默认状态"
                color: "white"
                font.bold: true
                anchors.centerIn: parent
            }
            
            // 状态定义
            states: [
                State {
                    name: "active"
                    when: toggleButton.isActive
                    
                    PropertyChanges {
                        target: toggleButton
                        color: "#4caf50"
                        scale: 1.1
                    }
                    
                    PropertyChanges {
                        target: buttonText
                        text: "✓ 已激活"
                    }
                }
            ]
            
            // 状态转换
            transitions: [
                Transition {
                    from: "*"
                    to: "*"
                    
                    // 颜色动画
                    ColorAnimation {
                        target: toggleButton
                        properties: "color"
                        duration: 300
                    }
                    
                    // 缩放动画
                    NumberAnimation {
                        target: toggleButton
                        properties: "scale"
                        duration: 300
                    }
                }
            ]
            
            MouseArea {
                anchors.fill: parent
                onClicked: parent.isActive = !parent.isActive
            }
        }
        
        // 状态信息显示
        Rectangle {
            width: 200
            height: 60
            color: "white"
            border.color: "#ddd"
            radius: 5
            
            Column {
                anchors.centerIn: parent
                spacing: 5
                
                Text {
                    id: stateInfo
                    text: "当前状态: " + (toggleButton.isActive ? "active" : "默认")
                    font.pixelSize: 12
                }
                
                Text {
                    text: "颜色: " + toggleButton.color
                    font.pixelSize: 10
                    color: "#666"
                }
            }
        }
    }
    
    // 示例2:多状态管理
    Column {
        id: multiStateExample
        x: 300
        y: 70
        spacing: 10
        
        Text {
            text: "2. 多状态管理"
            font.bold: true
            color: "#004d40"
        }
        
        // 状态对象
        Rectangle {
            id: multiStateRect
            width: 250
            height: 120
            color: "white"
            border.color: "#ddd"
            radius: 5
            
            // 状态定义
            states: [
                State {
                    name: "normal"
                    PropertyChanges {
                        target: displayRect
                        color: "#e3f2fd"
                        x: 20
                        y: 20
                        width: 60
                        height: 60
                    }
                },
                
                State {
                    name: "highlighted"
                    PropertyChanges {
                        target: displayRect
                        color: "#bbdefb"
                        x: 80
                        y: 20
                        width: 80
                        height: 40
                    }
                },
                
                State {
                    name: "selected"
                    PropertyChanges {
                        target: displayRect
                        color: "#90caf9"
                        x: 140
                        y: 20
                        width: 100
                        height: 80
                    }
                },
                
                State {
                    name: "disabled"
                    PropertyChanges {
                        target: displayRect
                        color: "#f5f5f5"
                        x: 20
                        y: 20
                        width: 60
                        height: 60
                        opacity: 0.5
                    }
                }
            ]
            
            // 当前状态
            state: "normal"
            
            // 显示矩形
            Rectangle {
                id: displayRect
                color: "#e3f2fd"
                radius: 3
                
                Text {
                    text: multiStateRect.state
                    anchors.centerIn: parent
                    font.bold: true
                }
            }
            
            // 状态切换按钮
            Row {
                anchors.bottom: parent.bottom
                anchors.horizontalCenter: parent.horizontalCenter
                anchors.bottomMargin: 10
                spacing: 5
                
                Repeater {
                    model: ["normal", "highlighted", "selected", "disabled"]
                    
                    Button {
                        text: modelData
                        highlighted: multiStateRect.state === modelData
                        onClicked: multiStateRect.state = modelData
                    }
                }
            }
        }
        
        // 状态转换
        Text {
            text: "状态转换配置"
            font.bold: true
            font.pixelSize: 12
            color: "#004d40"
        }
        
        Row {
            spacing: 10
            
            CheckBox {
                id: enableAnimation
                text: "启用动画"
                checked: true
            }
            
            CheckBox {
                id: enableSequential
                text: "顺序动画"
                checked: false
            }
        }
    }
    
    // 示例3:状态切换与条件状态
    Column {
        id: conditionalExample
        x: 30
        y: 250
        spacing: 10
        
        Text {
            text: "3. 条件状态与when属性"
            font.bold: true
            color: "#bf360c"
        }
        
        Rectangle {
            width: 250
            height: 120
            color: "white"
            border.color: "#ddd"
            radius: 5
            
            property int score: 0
            
            // 条件状态
            states: [
                State {
                    name: "poor"
                    when: score < 60
                    
                    PropertyChanges {
                        target: scoreIndicator
                        color: "#ef5350"
                        width: 50
                    }
                },
                
                State {
                    name: "average"
                    when: score >= 60 && score < 80
                    
                    PropertyChanges {
                        target: scoreIndicator
                        color: "#ffb74d"
                        width: 100
                    }
                },
                
                State {
                    name: "good"
                    when: score >= 80 && score < 90
                    
                    PropertyChanges {
                        target: scoreIndicator
                        color: "#66bb6a"
                        width: 150
                    }
                },
                
                State {
                    name: "excellent"
                    when: score >= 90
                    
                    PropertyChanges {
                        target: scoreIndicator
                        color: "#4caf50"
                        width: 200
                    }
                }
            ]
            
            Column {
                anchors.centerIn: parent
                spacing: 10
                
                // 分数指示器
                Rectangle {
                    id: scoreBar
                    width: 200
                    height: 20
                    color: "#f5f5f5"
                    radius: 10
                    
                    Rectangle {
                        id: scoreIndicator
                        height: parent.height
                        radius: 10
                        color: "#ef5350"
                        width: 50
                        
                        // 动画
                        Behavior on width {
                            NumberAnimation { duration: 500 }
                        }
                        
                        Behavior on color {
                            ColorAnimation { duration: 500 }
                        }
                    }
                }
                
                // 分数显示
                Text {
                    text: "分数: " + parent.parent.score + " - " + parent.parent.state
                    font.bold: true
                }
                
                // 分数控制
                Row {
                    spacing: 5
                    
                    Button {
                        text: "-10"
                        onClicked: parent.parent.parent.score = Math.max(0, parent.parent.parent.score - 10)
                    }
                    
                    Button {
                        text: "+10"
                        onClicked: parent.parent.parent.score = Math.min(100, parent.parent.parent.score + 10)
                    }
                    
                    Button {
                        text: "随机"
                        onClicked: parent.parent.parent.score = Math.floor(Math.random() * 101)
                    }
                }
            }
        }
    }
    
    // 示例4:状态组与复杂转换
    Column {
        id: complexExample
        x: 300
        y: 250
        spacing: 10
        
        Text {
            text: "4. 复杂状态与转换"
            font.bold: true
            color: "#4a148c"
        }
        
        Rectangle {
            width: 250
            height: 120
            color: "white"
            border.color: "#ddd"
            radius: 5
            
            // 动画对象
            Rectangle {
                id: animRect
                width: 50
                height: 50
                color: "#9c27b0"
                radius: 5
                
                // 状态定义
                states: [
                    State {
                        name: "left"
                        PropertyChanges {
                            target: animRect
                            x: 10
                            y: 10
                            rotation: 0
                        }
                    },
                    
                    State {
                        name: "right"
                        PropertyChanges {
                            target: animRect
                            x: 190
                            y: 10
                            rotation: 180
                        }
                    },
                    
                    State {
                        name: "bottom"
                        PropertyChanges {
                            target: animRect
                            x: 100
                            y: 60
                            rotation: 90
                        }
                    },
                    
                    State {
                        name: "center"
                        PropertyChanges {
                            target: animRect
                            x: 100
                            y: 10
                            rotation: 360
                        }
                    }
                ]
                
                // 默认状态
                state: "left"
                
                // 复杂转换
                transitions: [
                    Transition {
                        from: "*"
                        to: "*"
                        
                        // 并行动画
                        ParallelAnimation {
                            NumberAnimation {
                                properties: "x,y"
                                duration: 1000
                                easing.type: Easing.InOutQuad
                            }
                            
                            RotationAnimation {
                                duration: 1000
                                direction: RotationAnimation.Clockwise
                            }
                            
                            ColorAnimation {
                                duration: 1000
                            }
                        }
                    }
                ]
            }
            
            // 状态控制
            Row {
                anchors.bottom: parent.bottom
                anchors.horizontalCenter: parent.horizontalCenter
                anchors.bottomMargin: 10
                spacing: 5
                
                Repeater {
                    model: ["left", "center", "right", "bottom"]
                    
                    Button {
                        text: modelData
                        highlighted: animRect.state === modelData
                        onClicked: {
                            // 随机颜色
                            animRect.color = Qt.rgba(
                                Math.random(),
                                Math.random(),
                                Math.random(),
                                1
                            )
                            animRect.state = modelData
                        }
                    }
                }
            }
        }
        
        // 转换配置
        Column {
            spacing: 5
            
            Text {
                text: "动画配置"
                font.bold: true
                font.pixelSize: 12
                color: "#4a148c"
            }
            
            Row {
                spacing: 10
                
                Column {
                    spacing: 2
                    
                    Text {
                        text: "持续时间: " + durationSlider.value + "ms"
                        font.pixelSize: 10
                    }
                    
                    Slider {
                        id: durationSlider
                        from: 100
                        to: 2000
                        value: 1000
                    }
                }
                
                Column {
                    spacing: 2
                    
                    Text {
                        text: "缓动类型"
                        font.pixelSize: 10
                    }
                    
                    ComboBox {
                        id: easingCombo
                        model: ["Linear", "InOutQuad", "OutBounce", "InOutBack"]
                        width: 100
                    }
                }
            }
        }
    }
}

3.2 状态管理最佳实践

通过前面的示例,我们了解了状态管理的基础。现在让我们总结一些最佳实践:

状态管理原则:

  1. 状态命名明确:使用描述性名称,如"active"、"disabled"、"loading"

  2. 状态互斥:确保同一时间只有一个状态是激活的

  3. 状态层次:使用子状态处理复杂状态机

  4. 状态持久化:重要状态应该可以保存和恢复

  5. 状态验证:确保状态转换是有效的


4. 组件化开发

4.1 创建自定义组件

组件化是构建可维护、可复用UI的关键。让我们学习如何创建和使用自定义组件:

javascript 复制代码
// CalculatorButton.qml - 计算器按钮组件
import QtQuick 2.15
import QtQuick.Controls 2.15

Rectangle {
    id: button
    
    // ==================== 组件属性 ====================
    // 公共接口 - 这些属性可以被外部访问和设置
    
    // 1. 基本属性
    property string text: ""                 // 按钮文本
    property string buttonType: "number"     // 按钮类型: number, operator, function, equals
    property bool isPressed: false           // 按下状态
    
    // 2. 样式属性
    property color normalColor: getColorByType()
    property color pressedColor: Qt.darker(normalColor, 1.2)
    property color textColor: "white"
    property int fontSize: 20
    
    // 3. 信号
    signal clicked(string buttonText, string type)
    signal pressed()
    signal released()
    
    // ==================== 组件实现 ====================
    // 私有实现 - 这些是组件内部使用的
    
    // 计算颜色
    function getColorByType() {
        switch(buttonType) {
        case "number":
            return "#4a4a4a"
        case "operator":
            return "#f57c00"
        case "function":
            return "#616161"
        case "equals":
            return "#2196f3"
        default:
            return "#4a4a4a"
        }
    }
    
    // ==================== 组件样式 ====================
    width: 60
    height: 60
    radius: 5
    color: isPressed ? pressedColor : normalColor
    
    // 文字阴影
    layer.enabled: true
    layer.effect: DropShadow {
        color: "#20000000"
        radius: 2
        samples: 4
        verticalOffset: 1
    }
    
    // ==================== 组件内容 ====================
    Text {
        id: buttonText
        text: parent.text
        color: textColor
        font.pixelSize: fontSize
        font.bold: buttonType === "equals"
        anchors.centerIn: parent
    }
    
    // ==================== 组件交互 ====================
    MouseArea {
        id: mouseArea
        anchors.fill: parent
        
        // 鼠标按下
        onPressed: {
            button.isPressed = true
            button.pressed()
        }
        
        // 鼠标释放
        onReleased: {
            button.isPressed = false
            button.released()
        }
        
        // 点击
        onClicked: {
            // 播放点击动画
            clickAnimation.start()
            // 发射点击信号
            button.clicked(button.text, button.buttonType)
        }
        
        // 取消点击
        onCanceled: {
            button.isPressed = false
        }
    }
    
    // ==================== 组件动画 ====================
    // 点击动画
    SequentialAnimation {
        id: clickAnimation
        
        // 缩小
        NumberAnimation {
            target: button
            property: "scale"
            from: 1.0
            to: 0.9
            duration: 50
        }
        
        // 恢复
        NumberAnimation {
            target: button
            property: "scale"
            from: 0.9
            to: 1.0
            duration: 50
        }
    }
    
    // 悬停动画
    PropertyAnimation {
        id: hoverAnimation
        target: button
        property: "scale"
        to: 1.05
        duration: 150
    }
    
    // 启用悬停效果
    HoverHandler {
        id: hoverHandler
        onHoveredChanged: {
            if (hovered) {
                hoverAnimation.start()
            } else {
                hoverAnimation.stop()
                button.scale = 1.0
            }
        }
    }
    
    // ==================== 组件状态 ====================
    states: [
        State {
            name: "disabled"
            when: !mouseArea.enabled
            
            PropertyChanges {
                target: button
                opacity: 0.5
            }
        },
        
        State {
            name: "hovered"
            when: hoverHandler.hovered
            
            PropertyChanges {
                target: button
                layer.effect.radius: 4
                layer.effect.samples: 8
            }
        }
    ]
    
    // ==================== 状态转换 ====================
    transitions: [
        Transition {
            from: "*"
            to: "*"
            
            NumberAnimation {
                properties: "scale,opacity"
                duration: 150
            }
        }
    ]
}

4.2 使用自定义组件

现在让我们在计算器应用中使用这个组件:

javascript 复制代码
// Calculator.qml - 计算器主界面
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15

ApplicationWindow {
    id: calculator
    title: "QML计算器"
    width: 320
    height: 480
    visible: true
    color: "#2c3e50"
    
    // 计算器逻辑
    property string currentInput: "0"
    property string previousInput: ""
    property string currentOperator: ""
    property bool shouldClearDisplay: false
    
    // 初始化
    Component.onCompleted: {
        console.log("计算器应用启动")
    }
    
    // 主布局
    ColumnLayout {
        anchors.fill:parent
        spacing: 10
        anchors.margins: 15
        
        // 显示屏区域
        Rectangle {
            id: displayArea
            Layout.fillWidth: true
            Layout.preferredHeight: 120
            color: "#34495e"
            radius: 8
            
            Column {
                anchors.fill: parent
                anchors.margins: 10
                spacing: 5
                
                // 历史记录显示
                Text {
                    id: historyDisplay
                    width: parent.width
                    text: previousInput + (currentOperator ? " " + currentOperator : "")
                    color: "#95a5a6"
                    font.pixelSize: 16
                    elide: Text.ElideRight
                    horizontalAlignment: Text.AlignRight
                }
                
                // 当前输入显示
                Text {
                    id: currentDisplay
                    width: parent.width
                    text: currentInput
                    color: "white"
                    font.pixelSize: 36
                    font.bold: true
                    elide: Text.ElideRight
                    horizontalAlignment: Text.AlignRight
                }
                
                // 状态指示器
                Row {
                    width: parent.width
                    spacing: 5
                    
                    Text {
                        text: "内存: " + (memoryValue !== 0 ? "M" : "")
                        color: "#7f8c8d"
                        font.pixelSize: 12
                    }
                    
                    Text {
                        text: "操作: " + (currentOperator || "无")
                        color: "#7f8c8d"
                        font.pixelSize: 12
                    }
                    
                    Item { width: 10; height: 1 }
                    
                    Text {
                        text: "长度: " + currentInput.length
                        color: "#7f8c8d"
                        font.pixelSize: 12
                    }
                }
            }
            
            // 显示屏点击效果
            MouseArea {
                anchors.fill: parent
                onClicked: copyToClipboard()
                onPressAndHold: showDisplayMenu()
            }
        }
        
        // 按钮区域
        GridLayout {
            id: buttonGrid
            Layout.fillWidth: true
            Layout.fillHeight: true
            columns: 4
            columnSpacing: 8
            rowSpacing: 8
            
            // 第一行:功能按钮
            CalculatorButton {
                text: "C"
                buttonType: "function"
                Layout.fillWidth: true
                onClicked: clearAll()
            }
            
            CalculatorButton {
                text: "±"
                buttonType: "function"
                Layout.fillWidth: true
                onClicked: toggleSign()
            }
            
            CalculatorButton {
                text: "%"
                buttonType: "function"
                Layout.fillWidth: true
                onClicked: calculatePercentage()
            }
            
            CalculatorButton {
                text: "÷"
                buttonType: "operator"
                Layout.fillWidth: true
                onClicked: operatorClicked("/")
            }
            
            // 第二行:7 8 9 ×
            CalculatorButton {
                text: "7"
                buttonType: "number"
                Layout.fillWidth: true
                onClicked: numberClicked("7")
            }
            
            CalculatorButton {
                text: "8"
                buttonType: "number"
                Layout.fillWidth: true
                onClicked: numberClicked("8")
            }
            
            CalculatorButton {
                text: "9"
                buttonType: "number"
                Layout.fillWidth: true
                onClicked: numberClicked("9")
            }
            
            CalculatorButton {
                text: "×"
                buttonType: "operator"
                Layout.fillWidth: true
                onClicked: operatorClicked("*")
            }
            
            // 第三行:4 5 6 -
            CalculatorButton {
                text: "4"
                buttonType: "number"
                Layout.fillWidth: true
                onClicked: numberClicked("4")
            }
            
            CalculatorButton {
                text: "5"
                buttonType: "number"
                Layout.fillWidth: true
                onClicked: numberClicked("5")
            }
            
            CalculatorButton {
                text: "6"
                buttonType: "number"
                Layout.fillWidth: true
                onClicked: numberClicked("6")
            }
            
            CalculatorButton {
                text: "-"
                buttonType: "operator"
                Layout.fillWidth: true
                onClicked: operatorClicked("-")
            }
            
            // 第四行:1 2 3 +
            CalculatorButton {
                text: "1"
                buttonType: "number"
                Layout.fillWidth: true
                onClicked: numberClicked("1")
            }
            
            CalculatorButton {
                text: "2"
                buttonType: "number"
                Layout.fillWidth: true
                onClicked: numberClicked("2")
            }
            
            CalculatorButton {
                text: "3"
                buttonType: "number"
                Layout.fillWidth: true
                onClicked: numberClicked("3")
            }
            
            CalculatorButton {
                text: "+"
                buttonType: "operator"
                Layout.fillWidth: true
                onClicked: operatorClicked("+")
            }
            
            // 第五行:0 . = 
            CalculatorButton {
                text: "0"
                buttonType: "number"
                Layout.fillWidth: true
                Layout.columnSpan: 2
                onClicked: numberClicked("0")
            }
            
            CalculatorButton {
                text: "."
                buttonType: "number"
                Layout.fillWidth: true
                onClicked: decimalClicked()
            }
            
            CalculatorButton {
                text: "="
                buttonType: "equals"
                Layout.fillWidth: true
                onClicked: calculateResult()
            }
        }
    }
    
    // 计算器状态
    property double memoryValue: 0
    property bool isError: false
    
    // 计算器逻辑函数
    function numberClicked(number) {
        if (isError) {
            clearAll()
        }
        
        if (shouldClearDisplay) {
            currentInput = ""
            shouldClearDisplay = false
        }
        
        if (currentInput === "0" && number !== ".") {
            currentInput = number
        } else {
            // 限制输入长度
            if (currentInput.length < 15) {
                currentInput += number
            }
        }
    }
    
    function decimalClicked() {
        if (shouldClearDisplay) {
            currentInput = "0"
            shouldClearDisplay = false
        }
        
        if (currentInput.indexOf(".") === -1) {
            currentInput += "."
        }
    }
    
    function operatorClicked(op) {
        if (isError) {
            clearAll()
        }
        
        if (previousInput && currentOperator && !shouldClearDisplay) {
            calculateResult()
        }
        
        previousInput = currentInput
        currentOperator = op
        shouldClearDisplay = true
    }
    
    function calculateResult() {
        if (!previousInput || !currentOperator || isError) {
            return
        }
        
        var prev = parseFloat(previousInput)
        var current = parseFloat(currentInput)
        var result = 0
        
        try {
            switch(currentOperator) {
                case "+":
                    result = prev + current
                    break
                case "-":
                    result = prev - current
                    break
                case "×":
                case "*":
                    result = prev * current
                    break
                case "÷":
                case "/":
                    if (current === 0) {
                        throw new Error("除数不能为零")
                    }
                    result = prev / current
                    break
                default:
                    return
            }
            
            // 处理结果
            if (!isFinite(result)) {
                throw new Error("计算结果无限大")
            }
            
            // 格式化结果
            var resultStr = result.toString()
            if (resultStr.length > 15) {
                // 科学计数法显示
                resultStr = result.toExponential(8)
            }
            
            currentInput = resultStr
            previousInput = ""
            currentOperator = ""
            shouldClearDisplay = true
            isError = false
            
        } catch (error) {
            currentInput = "错误"
            isError = true
            console.error("计算错误:", error.message)
        }
    }
    
    function clearAll() {
        currentInput = "0"
        previousInput = ""
        currentOperator = ""
        shouldClearDisplay = false
        isError = false
    }
    
    function clearEntry() {
        currentInput = "0"
        shouldClearDisplay = false
    }
    
    function toggleSign() {
        if (isError) return
        
        if (currentInput !== "0") {
            if (currentInput.charAt(0) === "-") {
                currentInput = currentInput.substring(1)
            } else {
                currentInput = "-" + currentInput
            }
        }
    }
    
    function calculatePercentage() {
        if (isError) return
        
        var value = parseFloat(currentInput)
        if (!isNaN(value)) {
            currentInput = (value / 100).toString()
            shouldClearDisplay = true
        }
    }
    
    function copyToClipboard() {
        // 复制当前显示内容到剪贴板
        // 注意:实际应用中需要适当的剪贴板API
        console.log("复制到剪贴板:", currentInput)
    }
    
    function showDisplayMenu() {
        // 显示显示区域的上下文菜单
        console.log("显示菜单")
    }
    
    // 键盘支持
    Shortcut {
        sequences: ["0", "Ctrl+0"]
        onActivated: numberClicked("0")
    }
    
    Shortcut {
        sequences: ["1", "Ctrl+1"]
        onActivated: numberClicked("1")
    }
    
    Shortcut {
        sequences: ["2", "Ctrl+2"]
        onActivated: numberClicked("2")
    }
    
    Shortcut {
        sequences: ["3", "Ctrl+3"]
        onActivated: numberClicked("3")
    }
    
    Shortcut {
        sequences: ["4", "Ctrl+4"]
        onActivated: numberClicked("4")
    }
    
    Shortcut {
        sequences: ["5", "Ctrl+5"]
        onActivated: numberClicked("5")
    }
    
    Shortcut {
        sequences: ["6", "Ctrl+6"]
        onActivated: numberClicked("6")
    }
    
    Shortcut {
        sequences: ["7", "Ctrl+7"]
        onActivated: numberClicked("7")
    }
    
    Shortcut {
        sequences: ["8", "Ctrl+8"]
        onActivated: numberClicked("8")
    }
    
    Shortcut {
        sequences: ["9", "Ctrl+9"]
        onActivated: numberClicked("9")
    }
    
    Shortcut {
        sequences: ["+", "Ctrl++"]
        onActivated: operatorClicked("+")
    }
    
    Shortcut {
        sequences: ["-", "Ctrl+-"]
        onActivated: operatorClicked("-")
    }
    
    Shortcut {
        sequences: ["*", "Ctrl+*"]
        onActivated: operatorClicked("*")
    }
    
    Shortcut {
        sequences: ["/", "Ctrl+/"]
        onActivated: operatorClicked("/")
    }
    
    Shortcut {
        sequences: ["=", "Enter", "Return"]
        onActivated: calculateResult()
    }
    
    Shortcut {
        sequence: "Escape"
        onActivated: clearAll()
    }
    
    Shortcut {
        sequence: "Delete"
        onActivated: clearEntry()
    }
    
    // 计算器状态
    states: [
        State {
            name: "error"
            when: isError
            
            PropertyChanges {
                target: displayArea
                color: "#c0392b"
            }
        },
        
        State {
            name: "memory"
            when: memoryValue !== 0
            
            PropertyChanges {
                target: displayArea
                border.color: "#f1c40f"
                border.width: 2
            }
        }
    ]
    
    // 动画
    Behavior on color {
        ColorAnimation { duration: 300 }
    }
    
    // 主题支持
    property bool darkTheme: true
    
    onDarkThemeChanged: {
        calculator.color = darkTheme ? "#2c3e50" : "#ecf0f1"
    }
}

现在让我们创建一个完整的计算器应用,包含主题切换和历史记录功能:

javascript 复制代码
// AdvancedCalculator.qml - 高级计算器
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Layouts 1.15
import QtQuick.Dialogs 1.3

ApplicationWindow {
    id: advancedCalculator
    title: "QML高级计算器"
    width: 400
    height: 600
    minimumWidth: 350
    minimumHeight: 500
    visible: true
    
    // 计算引擎
    QtObject {
        id: calculatorEngine
        
        property var history: []
        property var memory: 0
        property var lastResult: 0
        
        function calculate(expression) {
            try {
                // 安全地评估表达式
                var result = evaluateExpression(expression)
                addToHistory(expression + " = " + result)
                lastResult = result
                return result
            } catch (error) {
                console.error("计算错误:", error)
                return "错误"
            }
        }
        
        function evaluateExpression(expr) {
            // 简单的表达式求值
            // 注意:实际应用中应使用更安全的求值方法
            expr = expr.replace(/×/g, "*").replace(/÷/g, "/")
            return Function('"use strict"; return (' + expr + ')')()
        }
        
        function addToHistory(entry) {
            history.unshift({
                "timestamp": new Date().toLocaleTimeString(),
                "entry": entry
            })
            if (history.length > 50) {
                history.pop()
            }
        }
        
        function clearHistory() {
            history = []
        }
        
        function memoryStore(value) {
            memory = parseFloat(value) || 0
        }
        
        function memoryRecall() {
            return memory
        }
        
        function memoryClear() {
            memory = 0
        }
        
        function memoryAdd(value) {
            memory += parseFloat(value) || 0
        }
        
        function memorySubtract(value) {
            memory -= parseFloat(value) || 0
        }
    }
    
    // 主题管理
    property var currentTheme: lightTheme
    
    property var lightTheme: {
        "primary": "#2196f3",
        "secondary": "#f50057",
        "background": "#f5f5f5",
        "surface": "#ffffff",
        "text": "#212121",
        "textSecondary": "#757575",
        "divider": "#e0e0e0"
    }
    
    property var darkTheme: {
        "primary": "#2196f3",
        "secondary": "#f50057",
        "background": "#121212",
        "surface": "#1e1e1e",
        "text": "#ffffff",
        "textSecondary": "#b0b0b0",
        "divider": "#333333"
    }
    
    // 应用栏
    header: ToolBar {
        height: 56
        background: Rectangle {
            color: currentTheme.primary
        }
        
        RowLayout {
            anchors.fill: parent
            anchors.leftMargin: 16
            anchors.rightMargin: 16
            
            // 标题
            Label {
                text: "高级计算器"
                font.pixelSize: 20
                font.bold: true
                color: "white"
                elide: Label.ElideRight
                Layout.fillWidth: true
            }
            
            // 主题切换按钮
            ToolButton {
                icon.source: "qrc:/icons/theme.svg"
                icon.color: "white"
                onClicked: toggleTheme()
                
                ToolTip.visible: hovered
                ToolTip.text: "切换主题"
            }
            
            // 历史记录按钮
            ToolButton {
                icon.source: "qrc:/icons/history.svg"
                icon.color: "white"
                onClicked: historyPopup.open()
                
                ToolTip.visible: hovered
                ToolTip.text: "历史记录"
            }
            
            // 设置按钮
            ToolButton {
                icon.source: "qrc:/icons/settings.svg"
                icon.color: "white"
                onClicked: settingsPopup.open()
                
                ToolTip.visible: hovered
                ToolTip.text: "设置"
            }
        }
    }
    
    // 主内容
    ColumnLayout {
        anchors.fill: parent
        anchors.margins: 16
        spacing: 16
        
        // 显示屏
        Rectangle {
            Layout.fillWidth: true
            Layout.preferredHeight: 120
            color: currentTheme.surface
            radius: 8
            border.color: currentTheme.divider
            border.width: 1
            
            Column {
                anchors.fill: parent
                anchors.margins: 12
                spacing: 4
                
                // 表达式显示
                Text {
                    width: parent.width
                    text: expressionInput.text
                    color: currentTheme.textSecondary
                    font.pixelSize: 18
                    elide: Text.ElideRight
                    horizontalAlignment: Text.AlignRight
                }
                
                // 结果显示
                Text {
                    width: parent.width
                    text: resultDisplay
                    color: currentTheme.text
                    font.pixelSize: 36
                    font.bold: true
                    elide: Text.ElideRight
                    horizontalAlignment: Text.AlignRight
                }
                
                // 内存指示器
                Row {
                    width: parent.width
                    spacing: 8
                    visible: calculatorEngine.memory !== 0
                    
                    Text {
                        text: "M: " + calculatorEngine.memory
                        color: currentTheme.secondary
                        font.pixelSize: 12
                    }
                }
            }
            
            // 复制按钮
            ToolButton {
                anchors.right: parent.right
                anchors.bottom: parent.bottom
                anchors.margins: 8
                icon.source: "qrc:/icons/copy.svg"
                icon.color: currentTheme.textSecondary
                onClicked: copyResult()
                
                ToolTip.visible: hovered
                ToolTip.text: "复制结果"
            }
        }
        
        // 表达式输入
        TextField {
            id: expressionInput
            Layout.fillWidth: true
            placeholderText: "输入表达式..."
            font.pixelSize: 16
            color: currentTheme.text
            background: Rectangle {
                color: currentTheme.surface
                border.color: currentTheme.divider
                radius: 4
            }
            
            onAccepted: calculate()
        }
        
        // 按钮网格
        GridLayout {
            Layout.fillWidth: true
            Layout.fillHeight: true
            columns: 4
            columnSpacing: 8
            rowSpacing: 8
            
            // 第一行
            CalcButton { text: "C"; onClicked: clearExpression() }
            CalcButton { text: "("; onClicked: appendToExpression("(") }
            CalcButton { text: ")"; onClicked: appendToExpression(")") }
            CalcButton { 
                text: "÷"; 
                backgroundColor: currentTheme.primary
                onClicked: appendToExpression("÷")
            }
            
            // 第二行
            CalcButton { text: "7"; onClicked: appendToExpression("7") }
            CalcButton { text: "8"; onClicked: appendToExpression("8") }
            CalcButton { text: "9"; onClicked: appendToExpression("9") }
            CalcButton { 
                text: "×"; 
                backgroundColor: currentTheme.primary
                onClicked: appendToExpression("×")
            }
            
            // 第三行
            CalcButton { text: "4"; onClicked: appendToExpression("4") }
            CalcButton { text: "5"; onClicked: appendToExpression("5") }
            CalcButton { text: "6"; onClicked: appendToExpression("6") }
            CalcButton { 
                text: "-"; 
                backgroundColor: currentTheme.primary
                onClicked: appendToExpression("-")
            }
            
            // 第四行
            CalcButton { text: "1"; onClicked: appendToExpression("1") }
            CalcButton { text: "2"; onClicked: appendToExpression("2") }
            CalcButton { text: "3"; onClicked: appendToExpression("3") }
            CalcButton { 
                text: "+"; 
                backgroundColor: currentTheme.primary
                onClicked: appendToExpression("+")
            }
            
            // 第五行
            CalcButton { 
                text: "0"; 
                Layout.columnSpan: 2
                onClicked: appendToExpression("0")
            }
            CalcButton { text: "."; onClicked: appendToExpression(".") }
            CalcButton { 
                text: "="; 
                backgroundColor: currentTheme.secondary
                onClicked: calculate()
            }
        }
        
        // 内存功能行
        RowLayout {
            Layout.fillWidth: true
            spacing: 8
            
            CalcButton { 
                text: "MC"; 
                Layout.fillWidth: true
                onClicked: memoryClear()
            }
            
            CalcButton { 
                text: "MR"; 
                Layout.fillWidth: true
                onClicked: memoryRecall()
            }
            
            CalcButton { 
                text: "M+"; 
                Layout.fillWidth: true
                onClicked: memoryAdd()
            }
            
            CalcButton { 
                text: "M-"; 
                Layout.fillWidth: true
                onClicked: memorySubtract()
            }
            
            CalcButton { 
                text: "MS"; 
                Layout.fillWidth: true
                onClicked: memoryStore()
            }
        }
    }
    
    // 计算器逻辑
    property string resultDisplay: "0"
    
    function appendToExpression(text) {
        expressionInput.text += text
    }
    
    function clearExpression() {
        expressionInput.text = ""
        resultDisplay = "0"
    }
    
    function calculate() {
        if (expressionInput.text === "") return
        
        var result = calculatorEngine.calculate(expressionInput.text)
        resultDisplay = result.toString()
    }
    
    function toggleTheme() {
        currentTheme = (currentTheme === lightTheme) ? darkTheme : lightTheme
        advancedCalculator.color = currentTheme.background
    }
    
    function copyResult() {
        // 复制结果到剪贴板
        console.log("复制:", resultDisplay)
    }
    
    // 内存功能
    function memoryStore() {
        if (resultDisplay !== "0" && resultDisplay !== "错误") {
            calculatorEngine.memoryStore(resultDisplay)
        }
    }
    
    function memoryRecall() {
        if (calculatorEngine.memory !== 0) {
            appendToExpression(calculatorEngine.memoryRecall())
        }
    }
    
    function memoryClear() {
        calculatorEngine.memoryClear()
    }
    
    function memoryAdd() {
        if (resultDisplay !== "0" && resultDisplay !== "错误") {
            calculatorEngine.memoryAdd(resultDisplay)
        }
    }
    
    function memorySubtract() {
        if (resultDisplay !== "0" && resultDisplay !== "错误") {
            calculatorEngine.memorySubtract(resultDisplay)
        }
    }
    
    // 历史记录弹窗
    Popup {
        id: historyPopup
        width: parent.width * 0.8
        height: parent.height * 0.6
        x: (parent.width - width) / 2
        y: (parent.height - height) / 2
        modal: true
        focus: true
        
        contentItem: Rectangle {
            color: currentTheme.surface
            radius: 8
            
            ColumnLayout {
                anchors.fill: parent
                spacing: 0
                
                // 标题栏
                Rectangle {
                    Layout.fillWidth: true
                    Layout.preferredHeight: 56
                    color: currentTheme.primary
                    radius: 8
                    
                    RowLayout {
                        anchors.fill: parent
                        anchors.leftMargin: 16
                        anchors.rightMargin: 16
                        
                        Label {
                            text: "历史记录"
                            color: "white"
                            font.pixelSize: 18
                            font.bold: true
                            Layout.fillWidth: true
                        }
                        
                        ToolButton {
                            icon.source: "qrc:/icons/close.svg"
                            icon.color: "white"
                            onClicked: historyPopup.close()
                        }
                    }
                }
                
                // 历史列表
                ListView {
                    Layout.fillWidth: true
                    Layout.fillHeight: true
                    model: calculatorEngine.history
                    clip: true
                    
                    delegate: Rectangle {
                        width: ListView.view.width
                        height: 60
                        color: index % 2 ? currentTheme.background : currentTheme.surface
                        
                        Column {
                            anchors.fill: parent
                            anchors.margins: 12
                            spacing: 4
                            
                            Text {
                                text: modelData.entry
                                color: currentTheme.text
                                font.pixelSize: 16
                                elide: Text.ElideRight
                            }
                            
                            Text {
                                text: modelData.timestamp
                                color: currentTheme.textSecondary
                                font.pixelSize: 12
                            }
                        }
                        
                        MouseArea {
                            anchors.fill: parent
                            onClicked: {
                                expressionInput.text = modelData.entry.split(" = ")[0]
                                historyPopup.close()
                            }
                        }
                    }
                    
                    ScrollBar.vertical: ScrollBar {
                        policy: ScrollBar.AlwaysOn
                    }
                }
                
                // 底部操作栏
                RowLayout {
                    Layout.fillWidth: true
                    Layout.preferredHeight: 48
                    
                    Button {
                        text: "清空历史"
                        Layout.fillWidth: true
                        onClicked: {
                            calculatorEngine.clearHistory()
                            historyPopup.close()
                        }
                    }
                }
            }
        }
    }
    
    // 设置弹窗
    Popup {
        id: settingsPopup
        width: parent.width * 0.8
        height: 300
        x: (parent.width - width) / 2
        y: (parent.height - height) / 2
        modal: true
        focus: true
        
        contentItem: Rectangle {
            color: currentTheme.surface
            radius: 8
            
            ColumnLayout {
                anchors.fill: parent
                anchors.margins: 16
                spacing: 16
                
                Label {
                    text: "设置"
                    font.pixelSize: 20
                    font.bold: true
                    color: currentTheme.text
                }
                
                Column {
                    spacing: 12
                    
                    Row {
                        spacing: 12
                        
                        CheckBox {
                            id: soundCheckbox
                            text: "启用音效"
                            checked: true
                        }
                        
                        CheckBox {
                            id: vibrationCheckbox
                            text: "启用震动"
                            checked: false
                        }
                    }
                    
                    Column {
                        spacing: 8
                        
                        Label {
                            text: "精度设置"
                            color: currentTheme.text
                        }
                        
                        ComboBox {
                            model: ["自动", "2位小数", "4位小数", "8位小数", "完整精度"]
                            width: 200
                        }
                    }
                }
                
                Item { Layout.fillHeight: true }
                
                RowLayout {
                    Layout.fillWidth: true
                    
                    Button {
                        text: "取消"
                        Layout.fillWidth: true
                        onClicked: settingsPopup.close()
                    }
                    
                    Button {
                        text: "保存"
                        Layout.fillWidth: true
                        highlighted: true
                        onClicked: {
                            // 保存设置
                            settingsPopup.close()
                        }
                    }
                }
            }
        }
    }
    
    // 自定义按钮组件
    component CalcButton: Button {
        property color backgroundColor: currentTheme.surface
        
        text: ""
        font.pixelSize: 18
        font.bold: true
        
        background: Rectangle {
            radius: 4
            color: parent.pressed ? Qt.darker(parent.backgroundColor, 1.2) : 
                   parent.hovered ? Qt.lighter(parent.backgroundColor, 1.1) : 
                   parent.backgroundColor
            border.color: currentTheme.divider
            border.width: 1
        }
        
        contentItem: Text {
            text: parent.text
            color: parent.highlighted ? "white" : currentTheme.text
            horizontalAlignment: Text.AlignHCenter
            verticalAlignment: Text.AlignVCenter
            font: parent.font
        }
    }
    
    // 键盘快捷键
    Shortcut {
        sequences: ["Ctrl+L", "Ctrl+Shift+L"]
        onActivated: clearExpression()
    }
    
    Shortcut {
        sequence: "Ctrl+H"
        onActivated: historyPopup.open()
    }
    
    Shortcut {
        sequence: "Ctrl+T"
        onActivated: toggleTheme()
    }
    
    // 初始化
    Component.onCompleted: {
        advancedCalculator.color = currentTheme.background
    }
}

现在让我们创建一个完整的项目结构,将所有这些组件组织在一起:

bash 复制代码
calculator_project/
├── main.py                      # Python主入口
├── requirements.txt             # 依赖文件
├── README.md                    # 项目说明
├── qml/                         # QML文件目录
│   ├── main.qml                # 主QML文件
│   ├── Calculator.qml          # 基础计算器
│   ├── AdvancedCalculator.qml  # 高级计算器
│   ├── components/             # 组件目录
│   │   ├── CalculatorButton.qml
│   │   ├── Display.qml
│   │   └── ThemeManager.qml
│   └── resources/             # 资源文件
│       └── icons/
├── python/                     # Python后端
│   ├── calculator_logic.py     # 计算器逻辑
│   ├── history_manager.py      # 历史记录管理
│   └── settings.py            # 设置管理
└── assets/                    # 静态资源
    ├── fonts/
    └── images/

下面是Python后端代码,用于启动计算器应用:

javascript 复制代码
// main.qml - 应用主入口
import QtQuick 2.15
import QtQuick.Controls 2.15
import QtQuick.Window 2.15

ApplicationWindow {
    id: mainWindow
    width: 400
    height: 600
    visible: true
    title: qsTr("QML计算器")
    
    // 启动界面
    Rectangle {
        anchors.fill: parent
        color: "#2196f3"
        
        Column {
            anchors.centerIn: parent
            spacing: 20
            
            Text {
                text: "QML计算器"
                color: "white"
                font.pixelSize: 32
                font.bold: true
            }
            
            Text {
                text: "选择计算器类型"
                color: "white"
                font.pixelSize: 16
            }
            
            Column {
                spacing: 10
                width: 200
                
                Button {
                    text: "基础计算器"
                    width: parent.width
                    highlighted: true
                    onClicked: {
                        loader.source = "Calculator.qml"
                        stackView.push(loader.item)
                    }
                }
                
                Button {
                    text: "高级计算器"
                    width: parent.width
                    onClicked: {
                        loader.source = "AdvancedCalculator.qml"
                        stackView.push(loader.item)
                    }
                }
            }
        }
    }
    
    // 主内容区域
    StackView {
        id: stackView
        anchors.fill: parent
        initialItem: mainWindow.contentItem
        
        pushEnter: Transition {
            PropertyAnimation {
                property: "opacity"
                from: 0
                to: 1
                duration: 200
            }
        }
        
        pushExit: Transition {
            PropertyAnimation {
                property: "opacity"
                from: 1
                to: 0
                duration: 200
            }
        }
        
        popEnter: Transition {
            PropertyAnimation {
                property: "opacity"
                from: 0
                to: 1
                duration: 200
            }
        }
        
        popExit: Transition {
            PropertyAnimation {
                property: "opacity"
                from: 1
                to: 0
                duration: 200
            }
        }
    }
    
    // 动态加载器
    Loader {
        id: loader
        visible: false
    }
    
    // 返回按钮
    Button {
        id: backButton
        text: "返回"
        visible: stackView.depth > 1
        anchors.left: parent.left
        anchors.top: parent.top
        anchors.margins: 10
        z: 100
        
        onClicked: stackView.pop()
    }
}

5. JavaScript在QML中的高级用法

5.1 JavaScript集成方式

QML内置了JavaScript引擎,支持多种方式集成JavaScript代码:

javascript 复制代码
import QtQuick 2.15

Rectangle {
    id: jsExample
    width: 600
    height: 400
    color: "#f5f5f5"
    
    // 标题
    Text {
        text: "JavaScript在QML中的集成"
        font.pixelSize: 24
        font.bold: true
        color: "#1a237e"
        anchors.horizontalCenter: parent.horizontalCenter
        y: 20
    }
    
    TabBar {
        id: jsTabs
        width: parent.width - 40
        anchors.horizontalCenter: parent.horizontalCenter
        y: 70
        
        TabButton { text: "内联JS" }
        TabButton { text: "外部JS" }
        TabButton { text: "模块化" }
        TabButton { text: "异步" }
    }
    
    StackLayout {
        width: parent.width - 40
        height: 300
        anchors.horizontalCenter: parent.horizontalCenter
        y: 120
        currentIndex: jsTabs.currentIndex
        
        // ==================== 1. 内联JavaScript ====================
        Column {
            spacing: 20
            
            Text {
                text: "1. 内联JavaScript"
                font.bold: true
                color: "#0d47a1"
            }
            
            Rectangle {
                width: 500
                height: 200
                color: "white"
                border.color: "#ddd"
                radius: 5
                
                Column {
                    anchors.centerIn: parent
                    spacing: 10
                    
                    // 内联函数定义
                    property string result: ""
                    
                    // 内联函数调用
                    Button {
                        text: "执行内联函数"
                        onClicked: {
                            // 直接在onClicked中写JavaScript
                            var a = 10
                            var b = 20
                            var sum = a + b
                            parent.result = "计算结果: " + sum
                            console.log("内联计算完成:", sum)
                        }
                    }
                    
                    Text {
                        text: parent.result
                        color: "#2196f3"
                    }
                    
                    // 复杂的条件逻辑
                    Button {
                        text: "复杂逻辑示例"
                        onClicked: {
                            // 复杂的JavaScript逻辑
                            var numbers = [1, 2, 3, 4, 5]
                            var evenNumbers = numbers.filter(function(n) {
                                return n % 2 === 0
                            })
                            var sum = evenNumbers.reduce(function(a, b) {
                                return a + b
                            }, 0)
                            
                            parent.result = "偶数之和: " + sum
                        }
                    }
                }
            }
        }
        
        // ==================== 2. 外部JavaScript文件 ====================
        Column {
            spacing: 20
            
            Text {
                text: "2. 外部JavaScript文件"
                font.bold: true
                color: "#004d40"
            }
            
            Rectangle {
                width: 500
                height: 200
                color: "white"
                border.color: "#ddd"
                radius: 5
                
                // 导入外部JavaScript文件
                // 假设有一个mathUtils.js文件
                // import "mathUtils.js" as MathUtils
                
                Column {
                    anchors.centerIn: parent
                    spacing: 10
                    
                    property var utils: ({
                        add: function(a, b) { return a + b },
                        multiply: function(a, b) { return a * b },
                        factorial: function(n) {
                            if (n <= 1) return 1
                            return n * this.factorial(n - 1)
                        }
                    })
                    
                    Button {
                        text: "调用外部函数"
                        onClicked: {
                            // 模拟调用外部函数
                            var result = parent.utils.factorial(5)
                            console.log("5! =", result)
                        }
                    }
                    
                    Text {
                        text: "查看控制台输出"
                        color: "#666"
                    }
                }
            }
        }
        
        // ==================== 3. 模块化JavaScript ====================
        Column {
            spacing: 20
            
            Text {
                text: "3. 模块化JavaScript"
                font.bold: true
                color: "#bf360c"
            }
            
            Rectangle {
                width: 500
                height: 200
                color: "white"
                border.color: "#ddd"
                radius: 5
                
                // 模拟模块
                QtObject {
                    id: mathModule
                    
                    function calculateCircleArea(radius) {
                        return Math.PI * radius * radius
                    }
                    
                    function calculateHypotenuse(a, b) {
                        return Math.sqrt(a * a + b * b)
                    }
                    
                    function generateRandom(min, max) {
                        return Math.floor(Math.random() * (max - min + 1)) + min
                    }
                }
                
                Column {
                    anchors.centerIn: parent
                    spacing: 10
                    
                    Button {
                        text: "计算圆面积 (r=5)"
                        onClicked: {
                            var area = mathModule.calculateCircleArea(5)
                            console.log("圆面积:", area.toFixed(2))
                        }
                    }
                    
                    Button {
                        text: "计算斜边 (3,4)"
                        onClicked: {
                            var hypotenuse = mathModule.calculateHypotenuse(3, 4)
                            console.log("斜边长度:", hypotenuse)
                        }
                    }
                    
                    Button {
                        text: "生成随机数 (1-100)"
                        onClicked: {
                            var random = mathModule.generateRandom(1, 100)
                            console.log("随机数:", random)
                        }
                    }
                }
            }
        }
        
        // ==================== 4. 异步JavaScript ====================
        Column {
            spacing: 20
            
            Text {
                text: "4. 异步JavaScript"
                font.bold: true
                color: "#4a148c"
            }
            
            Rectangle {
                width: 500
                height: 200
                color: "white"
                border.color: "#ddd"
                radius: 5
                
                Column {
                    anchors.centerIn: parent
                    spacing: 10
                    
                    property bool isProcessing: false
                    
                    // 模拟异步操作
                    function asyncOperation(duration) {
                        return new Promise(function(resolve) {
                            setTimeout(function() {
                                resolve("操作完成,耗时 " + duration + "ms")
                            }, duration)
                        })
                    }
                    
                    Button {
                        text: "开始异步操作"
                        enabled: !parent.isProcessing
                        
                        onClicked: {
                            parent.isProcessing = true
                            
                            // 使用Promise处理异步
                            parent.asyncOperation(2000).then(function(result) {
                                console.log("异步结果:", result)
                                parent.isProcessing = false
                            })
                        }
                    }
                    
                    Text {
                        text: parent.isProcessing ? "处理中..." : "就绪"
                        color: parent.isProcessing ? "#f57c00" : "#4caf50"
                    }
                    
                    // 使用Timer模拟异步
                    Timer {
                        id: asyncTimer
                        interval: 1000
                        running: false
                        repeat: true
                        
                        onTriggered: {
                            console.log("定时器触发:", new Date().toLocaleTimeString())
                        }
                    }
                    
                    Button {
                        text: asyncTimer.running ? "停止定时器" : "启动定时器"
                        onClicked: asyncTimer.running = !asyncTimer.running
                    }
                }
            }
        }
    }
}

5.2 JavaScript最佳实践

JavaScript使用指南:

  1. 简单逻辑用内联:一两行的简单操作可以直接写在信号处理器中

  2. 复杂逻辑用函数:超过3行的逻辑应该提取为函数

  3. 可复用代码用模块:多个组件共享的逻辑应该放在模块中

  4. 避免阻塞UI:长时间运行的操作应该使用异步或Worker

  5. 错误处理:重要的JavaScript代码应该有try-catch块


6. 总结与提高

6.1 本篇核心要点回顾

通过本篇的学习,我们深入掌握了QML语法的核心要素:

  1. 对象模型:理解了QML的对象树结构和属性系统

  2. 信号槽机制:掌握了多种信号处理方式

  3. 布局系统:熟练使用各种布局容器

  4. 状态管理:学会了创建动态交互界面

  5. 组件化:掌握了创建和使用自定义组件

  6. JavaScript集成:了解了在QML中使用JavaScript的最佳实践

6.2 性能优化建议

  1. 属性绑定优化

    • 避免深层属性绑定链

    • 使用缓存减少重复计算

    • 对频繁变化的属性使用节流

  2. 渲染优化

    • 使用visible属性控制显示,而不是opacity: 0

    • 复杂组件使用Loader延迟加载

    • 列表项使用cacheBuffer提高滚动性能

  3. 内存管理

    • 及时销毁不需要的组件

    • 使用weak引用避免内存泄漏

    • 监控JavaScript对象生命周期

6.3 常见问题解答

Q1: 什么时候使用属性绑定,什么时候使用赋值?

A1: 需要响应式更新时用绑定,设置固定值时用赋值。注意赋值会破坏已有绑定。

Q2: 如何调试QML代码?

A2: 可以使用Qt Creator的QML调试器,或者在代码中使用console.log()console.debug()等。

Q3: 如何优化QML应用的启动速度?

A3: 延迟加载非关键组件,预编译QML文件,减少初始加载的JavaScript代码。

Q4: QML和JavaScript的性能关系?

A4: 复杂的JavaScript计算会影响UI响应,应该将耗时操作放在Web Worker中。

6.4 练习与挑战

基础练习
  1. 修改计算器按钮样式,添加按下效果

  2. 为计算器添加键盘快捷键支持

  3. 实现计算器的撤销/重做功能

  4. 添加计算结果的复制功能

进阶挑战
  1. 实现科学计算器功能(三角函数、对数等)

  2. 添加计算历史记录的保存和加载

  3. 实现多语言支持

  4. 创建计算器插件系统

项目扩展
  1. 将计算器打包为桌面应用

  2. 添加云端同步功能

  3. 实现计算器小工具模式

  4. 添加语音输入支持

6.5 下一步学习建议

在下一篇《响应式UI艺术------状态、转换与动画》中,我们将深入学习:

  1. 高级动画技术:关键帧动画、路径动画、粒子系统

  2. 状态机设计:复杂状态管理和状态转换

  3. 视觉效果:阴影、模糊、着色器效果

  4. 性能优化:GPU加速、内存管理、渲染优化

  5. 实战项目:创建一个音乐播放器UI


📚 扩展阅读

官方文档

  1. QML语言基础

  2. Qt Quick布局

  3. JavaScript环境

学习资源

  1. QML最佳实践

  2. 性能优化指南

  3. 调试技巧

工具推荐

  1. QML Scene Graph Viewer:查看QML场景图

  2. GammaRay:Qt应用调试工具

  3. HotQML:QML热重载工具

社区资源

  1. Qt官方论坛

  2. Stack Overflow QML标签

  3. GitHub优秀QML项目


🎉 结语

恭喜你完成了第二篇的学习!通过构建一个功能完整的计算器应用,你已经掌握了QML语法的核心要素。从简单的属性绑定到复杂的组件化架构,你现在具备了构建真实QML应用的能力。

记住,掌握语法只是第一步。真正的艺术在于如何将这些语法元素组合成优雅、高效、可维护的代码。在接下来的篇章中,我们将学习如何让界面"活"起来------通过动画、状态转换和视觉效果,创建令人惊艳的用户体验。

相关推荐
码界筑梦坊2 小时前
357-基于Java的大型商场应急预案管理系统
java·开发语言·毕业设计·知识分享
anzhxu2 小时前
Go基础之环境搭建
开发语言·后端·golang
ai_coder_ai2 小时前
自动化脚本ui编程之垂直滚动布局(vscroll)
ui·autojs·自动化脚本·冰狐智能辅助·easyclick
云烟成雨TD2 小时前
Spring AI Alibaba 1.x 系列【31】集成 Studio 模块实现可视化 Agent 调试
java·人工智能·spring
014-code2 小时前
Spring Data JPA 实战指南
java·spring
安小牛2 小时前
Android 开发汉字转带声调的拼音
android·java·学习·android studio
聚美智数2 小时前
企业实际控制人查询-公司实控人查询
android·java·javascript
刀法如飞2 小时前
一款Python语言Django框架DDD脚手架,助你快速搭建项目
python·ddd·脚手架
刀法如飞2 小时前
一款Python语言Django框架DDD脚手架,适合中大型项目
后端·python·领域驱动设计