强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

04 - QML 与 Qt Quick / QML & Qt Quick

QML 与 Qt Quick / QML & Qt Quick

掌握 Qt 的声明式 UI 框架,构建现代化、流畅的触控友好界面。 Master Qt’s declarative UI framework for modern, fluid, touch-friendly interfaces.


4.1 QML 是什么? / What is QML?

QML(Qt Meta Language)是 Qt 的声明式 UI 语言,专门用于构建动态、流畅的用户界面。

QML (Qt Meta Language) is Qt’s declarative UI language designed for dynamic, fluid user interfaces.

QML vs Qt Widgets 对比 / QML vs Qt Widgets Comparison

特性 / FeatureQML / Qt QuickQt Widgets
UI 范式声明式 / Declarative命令式 / Imperative
渲染引擎场景图 (Scene Graph)CPU / QPainter
动画内置声明式动画需要 QPropertyAnimation
触控支持✅ 原生支持⚠️ 有限支持
响应式布局✅ 锚点系统需要布局管理器
适合场景移动端、嵌入式、仪表盘桌面办公软件
学习曲线JavaScript 语法C++ 开发
性能GPU 加速CPU 渲染

4.2 QML 基础语法 / QML Basics

QML 类型系统 / QML Type System

类型 / Type说明 / Description示例 / Example
基本类型int, string, bool, color, urlwidth: 100
Item所有可视元素的基类Item { }
Text文本显示Text { text: "Hello" }
Image图片显示Image { source: "logo.png" }
Rectangle矩形Rectangle { color: "red" }
MouseArea鼠标/触控区域MouseArea { onClicked: ... }
Column/Row/Grid布局Column { spacing: 10 }
ListView列表视图ListView { model: listModel }
StackView页面栈导航StackView { }
Timer定时器Timer { interval: 1000 }

完整 QML 示例 / Complete QML Example

// Main.qml - 简单计数器应用
// Simple Counter Application
import QtQuick
import QtQuick.Controls
import QtQuick.Layouts

ApplicationWindow {
    id: root
    width: 400
    height: 300
    visible: true
    title: qsTr("QML 计数器 / Counter")
    color: "#f0f0f0"

    // 属性绑定 / Property bindings
    property int count: 0
    property bool isEven: count % 2 === 0

    ColumnLayout {
        anchors.centerIn: parent
        spacing: 20

        // 标题
        Text {
            text: qsTr("QML 计数器演示")
            font.pixelSize: 24
            font.bold: true
            color: "#2c3e50"
            Layout.alignment: Qt.AlignHCenter
        }

        // 计数显示
        Rectangle {
            width: 200
            height: 80
            radius: 10
            color: root.isEven ? "#3498db" : "#e74c3c"

            Behavior on color {
                ColorAnimation { duration: 300 }
            }

            Text {
                anchors.centerIn: parent
                text: root.count.toString()
                font.pixelSize: 48
                font.bold: true
                color: "white"
            }
        }

        // 状态指示
        Text {
            text: root.isEven ? qsTr("偶数 / Even") : qsTr("奇数 / Odd")
            font.pixelSize: 16
            color: "#7f8c8d"
            Layout.alignment: Qt.AlignHCenter
        }

        // 按钮行
        RowLayout {
            spacing: 10
            Layout.alignment: Qt.AlignHCenter

            Button {
                text: qsTr("减少 / -")
                onClicked: root.count--
                Material.background: "#e74c3c"
            }

            Button {
                text: qsTr("重置 / Reset")
                onClicked: root.count = 0
                flat: true
            }

            Button {
                text: qsTr("增加 / +")
                onClicked: root.count++
                Material.background: "#2ecc71"
            }
        }
    }
}

C++ 后端集成 / C++ Backend Integration

// main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QSettings>

class Backend : public QObject {
    Q_OBJECT
    Q_PROPERTY(QString userName READ userName
               WRITE setUserName NOTIFY userNameChanged)
    Q_PROPERTY(int themeIndex READ themeIndex
               WRITE setThemeIndex NOTIFY themeIndexChanged)

public:
    explicit Backend(QObject *parent = nullptr) : QObject(parent) {
        QSettings s;
        m_userName = s.value("user/name", "Guest").toString();
        m_themeIndex = s.value("ui/theme", 0).toInt();
    }

    QString userName() const { return m_userName; }
    int themeIndex() const { return m_themeIndex; }

    void setUserName(const QString &name) {
        if (m_userName != name) {
            m_userName = name;
            QSettings().setValue("user/name", name);
            emit userNameChanged();
        }
    }

    void setThemeIndex(int index) {
        if (m_themeIndex != index) {
            m_themeIndex = index;
            QSettings().setValue("ui/theme", index);
            emit themeIndexChanged();
        }
    }

    // Q_INVOKABLE 方法可从 QML 调用
    Q_INVOKABLE QString formatGreeting() const {
        return QStringLiteral("Hello, %1!").arg(m_userName);
    }

signals:
    void userNameChanged();
    void themeIndexChanged();

private:
    QString m_userName;
    int m_themeIndex;
};

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    app.setOrganizationName("MyCompany");
    app.setApplicationName("QMLDemo");

    Backend backend;

    QQmlApplicationEngine engine;

    // 将 C++ 对象暴露给 QML
    engine.rootContext()->setContextProperty("backend", &backend);

    QObject::connect(
        &engine, &QQmlApplicationEngine::objectCreationFailed,
        &app, []() { QCoreApplication::exit(-1); },
        Qt::QueuedConnection);

    engine.loadFromModule("QMLDemo", "Main");

    return app.exec();
}

#include "main.moc"

4.3 锚点布局系统 / Anchor Layout System

QML 的锚点系统提供灵活的相对布局。 QML’s anchor system provides flexible relative positioning.

// anchor-layout.qml - 锚点布局示例
import QtQuick

Item {
    width: 400; height: 300

    // 红色矩形固定在左侧
    Rectangle {
        id: sidebar
        width: 80
        color: "#e74c3c"
        anchors {
            top: parent.top
            bottom: parent.bottom
            left: parent.left
        }

        Column {
            anchors.centerIn: parent
            spacing: 10

            Repeater {
                model: ["首页", "设置", "关于"]
                Rectangle {
                    width: 60; height: 30
                    radius: 4
                    color: mouseArea.containsMouse ? "#c0392b" : "#e74c3c"

                    Text {
                        anchors.centerIn: parent
                        text: modelData
                        color: "white"
                        font.pixelSize: 12
                    }

                    MouseArea {
                        id: mouseArea
                        anchors.fill: parent
                        hoverEnabled: true
                        onClicked: console.log("Clicked:", modelData)
                    }
                }
            }
        }
    }

    // 蓝色内容区填满右侧
    Rectangle {
        color: "#3498db"
        anchors {
            top: parent.top
            bottom: parent.bottom
            left: sidebar.right
            right: parent.right
            margins: 10
        }

        Text {
            anchors.centerIn: parent
            text: "内容区域 / Content Area"
            color: "white"
            font.pixelSize: 18
        }
    }
}

锚点速查表 / Anchors Quick Reference

锚点 / Anchor说明 / Description
anchors.left左边对齐 / Align left
anchors.right右边对齐 / Align right
anchors.top顶部对齐 / Align top
anchors.bottom底部对齐 / Align bottom
anchors.centerIn居中 / Center in parent
anchors.fill填满 / Fill parent
anchors.margins外边距 / Outer margins
anchors.leftMargin左边距 / Left margin
anchors.horizontalCenter水平居中 / H-Center
anchors.verticalCenter垂直居中 / V-Center

4.4 动画系统 / Animation System

QML 的动画系统是其最大优势之一,支持声明式动画。 QML’s animation system is one of its key strengths.

动画类型 / Animation Types

动画 / Animation用途 / Purpose
NumberAnimation数值变化 / Numeric change
ColorAnimation颜色过渡 / Color transition
RotationAnimation旋转 / Rotation
ScaleAnimation缩放 / Scale
OpacityAnimation透明度 / Opacity
PropertyAnimation通用属性动画 / Generic property
ParallelAnimation并行动画 / Parallel
SequentialAnimation顺序动画 / Sequential
SpringAnimation弹簧动画 / Spring physics
PathAnimation路径动画 / Path-based

完整动画示例 / Complete Animation Example

// animation-demo.qml
import QtQuick
import QtQuick.Controls

ApplicationWindow {
    width: 500; height: 400
    visible: true
    title: "动画演示 / Animation Demo"

    // 旋转方块 / Rotating square
    Rectangle {
        id: square
        width: 80; height: 80
        radius: 10
        color: "#e74c3c"
        anchors.centerIn: parent

        Text {
            anchors.centerIn: parent
            text: "Qt"
            color: "white"
            font.bold: true
            font.pixelSize: 20
        }

        // 持续旋转动画 / Continuous rotation
        RotationAnimation on rotation {
            from: 0; to: 360
            duration: 3000
            loops: Animation.Infinite
        }
    }

    // 顺序动画:点击后变大再变小
    // Sequential: grow then shrink on click
    MouseArea {
        anchors.fill: square
        onClicked: bounceAnim.start()
    }

    SequentialAnimation {
        id: bounceAnim

        ScaleAnimator {
            target: square
            from: 1.0; to: 1.5
            duration: 200
            easing.type: Easing.OutQuad
        }

        // 颜色变化
        ColorAnimation {
            target: square
            property: "color"
            to: "#3498db"
            duration: 300
        }

        ScaleAnimator {
            target: square
            from: 1.5; to: 1.0
            duration: 200
            easing.type: Easing.InOutBounce
        }

        // 恢复颜色
        ColorAnimation {
            target: square
            property: "color"
            to: "#e74c3c"
            duration: 300
        }
    }

    // 进入动画示例 / Entry animation
    Rectangle {
        id: panel
        width: 200; height: 50
        color: "#2ecc71"
        radius: 8
        x: parent.width   // 初始位置在屏幕右侧外

        Text {
            anchors.centerIn: parent
            text: "滑入面板 / Slide-in Panel"
            color: "white"
        }

        // 组件加载完成后执行
        Component.onCompleted: slideIn.start()

        NumberAnimation on x {
            id: slideIn
            to: parent.width / 2 - 100
            duration: 800
            easing.type: Easing.OutCubic
        }
    }

    // 按钮组
    Row {
        anchors.bottom: parent.bottom
        anchors.bottomMargin: 20
        anchors.horizontalCenter: parent.horizontalCenter
        spacing: 10

        Button {
            text: "弹跳 / Bounce"
            onClicked: bounceAnim.start()
        }
        Button {
            text: "重置 / Reset"
            onClicked: {
                square.rotation = 0;
                square.scale = 1.0;
            }
        }
    }
}

缓动曲线 / Easing Curves

缓动类型 / Easing Type效果 / Effect适用场景
Easing.Linear匀速 / Linear旋转动画
Easing.InQuad先慢后快 / Slow start元素进入
Easing.OutQuad先快后慢 / Slow end元素退出
Easing.InOutQuad两端慢 / Slow both平滑过渡
Easing.OutBounce弹跳效果 / Bounce按钮反馈
Easing.OutElastic弹性效果 / Elastic强调动画
Easing.OutCubic平滑减速 / Smooth decel导航过渡

4.5 ListView 与 Model / ListView and Model

// listview-demo.qml - 列表示例
import QtQuick
import QtQuick.Controls

ApplicationWindow {
    width: 360; height: 500
    visible: true
    title: "列表视图 / ListView"

    // 数据模型
    ListModel {
        id: contactModel
        ListElement { name: "张三"; phone: "138-0000-0001"; online: true }
        ListElement { name: "李四"; phone: "139-0000-0002"; online: false }
        ListElement { name: "王五"; phone: "137-0000-0003"; online: true }
        ListElement { name: "赵六"; phone: "136-0000-0004"; online: false }
        ListElement { name: "孙七"; phone: "135-0000-0005"; online: true }
    }

    ListView {
        id: listView
        anchors.fill: parent
        anchors.margins: 10
        model: contactModel
        spacing: 8
        clip: true

        // 添加入场动画
        add: Transition {
            NumberAnimation {
                properties: "opacity, y"
                from: 0; to: 1
                duration: 300
            }
        }

        delegate: Rectangle {
            width: listView.width
            height: 70
            radius: 8
            color: mouse.containsMouse ? "#ecf0f1" : "white"
            border.color: "#dcdde1"

            Row {
                anchors.fill: parent
                anchors.margins: 12
                spacing: 12

                // 头像
                Rectangle {
                    width: 46; height: 46
                    radius: 23
                    color: online ? "#2ecc71" : "#bdc3c7"

                    Text {
                        anchors.centerIn: parent
                        text: name.charAt(0)
                        color: "white"
                        font.pixelSize: 18
                        font.bold: true
                    }
                }

                // 信息
                Column {
                    anchors.verticalCenter: parent.verticalCenter

                    Text {
                        text: name
                        font.pixelSize: 16
                        font.bold: true
                        color: "#2c3e50"
                    }
                    Text {
                        text: phone
                        font.pixelSize: 13
                        color: "#7f8c8d"
                    }
                }
            }

            MouseArea {
                id: mouse
                anchors.fill: parent
                hoverEnabled: true
                onClicked: console.log("Selected:", name)
            }
        }

        // 空状态
        Rectangle {
            anchors.centerIn: parent
            visible: contactModel.count === 0
            Text {
                anchors.centerIn: parent
                text: "暂无联系人\nNo contacts"
                color: "#95a5a6"
                font.pixelSize: 16
            }
        }
    }
}

4.6 页面导航 / Page Navigation

// navigation.qml - StackView 导航
import QtQuick
import QtQuick.Controls

ApplicationWindow {
    width: 360; height: 640
    visible: true

    StackView {
        id: stackView
        anchors.fill: parent
        initialItem: homePage
    }

    // 首页
    Component {
        id: homePage
        Page {
            title: "首页 / Home"
            header: ToolBar {
                Label {
                    text: "首页 / Home"
                    font.pixelSize: 18
                    anchors.centerIn: parent
                }
            }

            Column {
                anchors.centerIn: parent
                spacing: 20

                Button {
                    text: "个人资料 / Profile"
                    onClicked: stackView.push(profilePage)
                }

                Button {
                    text: "设置 / Settings"
                    onClicked: stackView.push(settingsPage)
                }
            }
        }
    }

    // 个人资料页
    Component {
        id: profilePage
        Page {
            title: "个人资料 / Profile"
            header: ToolBar {
                RowLayout {
                    anchors.fill: parent
                    ToolButton {
                        text: "← 返回"
                        onClicked: stackView.pop()
                    }
                    Label {
                        text: "个人资料"
                        Layout.fillWidth: true
                        horizontalAlignment: Qt.AlignHCenter
                    }
                }
            }

            Text {
                anchors.centerIn: parent
                text: "个人资料页面\nProfile Page"
                font.pixelSize: 20
            }
        }
    }

    // 设置页
    Component {
        id: settingsPage
        Page {
            title: "设置 / Settings"
            header: ToolBar {
                RowLayout {
                    anchors.fill: parent
                    ToolButton {
                        text: "← 返回"
                        onClicked: stackView.pop()
                    }
                    Label {
                        text: "设置"
                        Layout.fillWidth: true
                        horizontalAlignment: Qt.AlignHCenter
                    }
                }
            }

            Column {
                anchors.centerIn: parent
                spacing: 16

                Switch { text: "暗色模式 / Dark Mode" }
                Switch { text: "通知 / Notifications"; checked: true }
                Switch { text: "自动保存 / Auto-save"; checked: true }
            }
        }
    }
}

4.7 QML 与 C++ 交互 / QML-C++ Interaction

暴露 C++ 对象到 QML / Exposing C++ to QML

方法 / Method适用场景 / When to Use
setContextProperty()全局对象(运行时设置) / Runtime global objects
qmlRegisterType()注册类型(实例化) / Register type for instantiation
QML_ELEMENTQt6 推荐方式 / Qt6 recommended approach
qmlRegisterSingletonType()单例对象 / Singleton objects
// Qt6 推荐方式:使用 QML_ELEMENT 宏
// Qt6 recommended: QML_ELEMENT macro
#include <QObject>
#include <QQmlEngine>

class DataManager : public QObject {
    Q_OBJECT
    QML_ELEMENT              // 自动注册到 QML
    QML_SINGLETON            // 单例模式

    Q_PROPERTY(QStringList recentFiles READ recentFiles
               NOTIFY recentFilesChanged)

public:
    explicit DataManager(QObject *parent = nullptr) : QObject(parent) {}

    QStringList recentFiles() const { return m_recentFiles; }

    Q_INVOKABLE void addFile(const QString &path) {
        if (!m_recentFiles.contains(path)) {
            m_recentFiles.prepend(path);
            if (m_recentFiles.size() > 10) {
                m_recentFiles.removeLast();
            }
            emit recentFilesChanged();
        }
    }

signals:
    void recentFilesChanged();

private:
    QStringList m_recentFiles;
};
# CMakeLists.txt - 启用 QML 类型注册
qt_add_qml_module(myapp
    URI MyApp
    VERSION 1.0
    QML_FILES Main.qml
    SOURCES datamanager.h datamanager.cpp
)

注意事项 / Important Notes

⚠️ QML 性能 / QML Performance

  • 避免在 QML 中写复杂逻辑,应放入 C++ 后端
  • 使用 Loader 延迟加载非可见组件
  • 避免深层嵌套的 Repeater
  • 使用 QtQuick.Controls 而非自定义控件

Avoid complex logic in QML; use C++ backend. Use Loader for deferred loading.

⚠️ QML 调试 / QML Debugging

使用 QML_IMPORT_TRACE=1 环境变量查看导入。 使用 Qt Creator 的 QML Profiler 分析性能。

Use QML_IMPORT_TRACE=1 to trace imports. Use Qt Creator’s QML Profiler.

⚠️ 触控支持 / Touch Support

QML 原生支持多点触控,MouseArea 同时处理鼠标和触控事件。 使用 MultiPointTouchArea 处理复杂手势。

QML natively supports multi-touch. MouseArea handles both mouse and touch. Use MultiPointTouchArea for complex gestures.


业务场景 / Business Scenarios

场景 / Scenario推荐方案 / Recommended
移动端应用QML + StackView + SwipeView
智能家居 HMIQML + 自定义 Canvas + 动画
数据仪表盘QML + Charts + 自定义绘制
嵌入式触屏QML + 响应式布局 + 大按钮
多媒体播放器QML + Qt Multimedia
游戏 UIQML + 粒子系统 + ShaderEffect

扩展阅读 / Further Reading

资源 / Resource链接 / Link
QML 参考文档https://doc.qt.io/qt-6/qmlreference.html
Qt Quick Controlshttps://doc.qt.io/qt-6/qtquickcontrols-index.html
QML 与 C++ 交互https://doc.qt.io/qt-6/qtqml-cppintegration-overview.html
QML 性能建议https://doc.qt.io/qt-6/qtquick-performance.html
Qt Quick 示例https://doc.qt.io/qt-6/qtquick-index.html

03 - Qt Widgets | 05 - Qt 网络编程