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
| 特性 / Feature | QML / Qt Quick | Qt 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, url | width: 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_ELEMENT 宏 | Qt6 推荐方式 / 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
Loaderfor deferred loading.
⚠️ QML 调试 / QML Debugging
使用
QML_IMPORT_TRACE=1环境变量查看导入。 使用 Qt Creator 的 QML Profiler 分析性能。Use
QML_IMPORT_TRACE=1to trace imports. Use Qt Creator’s QML Profiler.
⚠️ 触控支持 / Touch Support
QML 原生支持多点触控,
MouseArea同时处理鼠标和触控事件。 使用MultiPointTouchArea处理复杂手势。QML natively supports multi-touch.
MouseAreahandles both mouse and touch. UseMultiPointTouchAreafor complex gestures.
业务场景 / Business Scenarios
| 场景 / Scenario | 推荐方案 / Recommended |
|---|---|
| 移动端应用 | QML + StackView + SwipeView |
| 智能家居 HMI | QML + 自定义 Canvas + 动画 |
| 数据仪表盘 | QML + Charts + 自定义绘制 |
| 嵌入式触屏 | QML + 响应式布局 + 大按钮 |
| 多媒体播放器 | QML + Qt Multimedia |
| 游戏 UI | QML + 粒子系统 + ShaderEffect |
扩展阅读 / Further Reading
| 资源 / Resource | 链接 / Link |
|---|---|
| QML 参考文档 | https://doc.qt.io/qt-6/qmlreference.html |
| Qt Quick Controls | https://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 网络编程 →