第一节 QML 背景介绍

本章回答三个问题:QML 从哪来、是什么、为什么值得学。读完你将对 QML 在整个 Qt 生态中的位置、它与 Widgets/JavaScript/HTML 的关系、以及它背后的渲染机制有清晰认识。

1. QML 的历史与发展

时间 里程碑
2009 Nokia 收购 Trolltech(Qt 原厂)后提出 Qt Quick 构想
2010 Qt 4.7 引入 QML(Qt Quick 1),主打"快速开发富交互 UI"
2012 Qt 5.0 发布 Qt Quick 2,基于 GPU 场景图(Scene Graph)渲染,性能大幅提升
2017 Qt 5.9 LTS:Qt Quick Controls 2 成熟,成为新控件标准
2019 Qt 5.12 LTS:QML 支持 required、Qt.callLater 等现代语法
2021 Qt 6.0 发布:QML 引擎升级为 QJSEngine,移除 Controls 1 与 Qt Quick 1
2023~ Qt 6.5/6.8 LTS:QML 编译(qmltc)、QML 模块化、工具链成熟

QML 的诞生背景:传统 Qt Widgets 用 C++ 描述 UI,开发速度慢、动效能力弱;移动互联网时代需要一种"像 Web 一样快速迭代、又拥有原生性能"的方案。QML 就是 Qt 对这一需求的回答。

2. 什么是 QML

QML(Qt Meta-Object Language) 是 Qt 提供的一种声明式 UI 描述语言。它的语法基于 JSON 风格扩展而来,用来描述:

  • 界面由哪些对象组成(对象树);
  • 每个对象的属性是什么(颜色、尺寸、文本......);
  • 对象之间如何联动(属性绑定);
  • 用户交互如何响应(信号处理器)。

QML 的核心哲学:你描述"界面应该长什么样",而不是"一步一步怎么画出界面"。

javascript 复制代码
// 声明式:描述"是什么"
Rectangle {
    width: 200; height: 100
    color: "lightblue"
    Text {
        anchors.centerIn: parent
        text: "Hello QML"
    }
}

对应命令式(伪代码):

cpp 复制代码
auto rect = new QWidget();
rect->setGeometry(0, 0, 200, 100);
rect->setStyleSheet("background: lightblue;");
auto label = new QLabel("Hello QML", rect);
label->move(50, 40);

声明式的好处

  1. 代码即设计稿:阅读成本低,UI 结构一目了然。
  2. 自动联动:属性绑定让数据与界面自动同步,少写大量 updateUI() 样板代码。
  3. 易组合复用:任意 .qml 文件都能当组件用。
  4. 动效友好:动画、状态、过渡都是声明式语言的一部分。

3. QML 引擎如何工作(架构视角)

一个 QML 文件的生命周期:

复制代码

关键组件:

组件 作用
QQmlEngine QML 运行时核心:类型注册表、import 管理、JS 引擎持有
QQmlApplicationEngine 便捷封装:加载 qrc 中的 QML 并管理根对象
QQmlComponent 代表一个可实例化的 QML 类型(文件或内联 Component)
QQmlContext 对象的作用域上下文:提供 id 解析与 context property
Scene Graph 将 QML 对象树转换为 GPU 绘制指令(Qt Quick 2)

注意:QML 不是"解释执行"的简单脚本。Qt6 默认在构建时用 qmlcachegen 预编译,运行时大部分 QML 已编译为字节码/C++,性能接近原生。

4. QML 与 Qt Widgets 的对比

4.1 详细对比表

维度 Qt Widgets QML / Qt Quick
UI 描述语言 C++(代码式) QML(声明式)+ JavaScript
编程范式 命令式 + 对象树 声明式对象树 + 属性绑定
渲染 CPU(QPainter),标量绘制 GPU 场景图(OpenGL/Vulkan/D3D/Metal)
动画 需 QPropertyAnimation 等类,样板多 内建动画/Behavior/Transition,几行搞定
样式 QSS 样式表(类 CSS) 委托(delegate)体系 + Style 附加属性
控件生态 非常成熟(QTableView、QTreeView、QMainWindow...) Controls 2 较全,复杂表格需自定义
移动端支持 弱(桌面向) 官方支持 Android/iOS
嵌入式 支持但资源占用高 更轻量,Qt for MCU 使用 QML 子集
性能(大量数据表格) QTableView 万行级无压力 需要虚拟化/委托优化,行数过大吃力
学习曲线 C++ 门槛 语法简单,但绑定/作用域概念需时间
开发效率 高(尤其 UI 迭代与原型)
与 C++ 集成 天然 通过 Q_PROPERTY/Q_INVOKABLE/注册类型
热重载 需第三方/无 官方工具原生支持

4.2 怎么选?(决策建议)

项目特征 推荐
桌面工具、代码编辑器、复杂报表 Widgets(或 QML + 内嵌 C++ 表格)
移动 App、平板 QML
车载 HMI、嵌入式仪表 QML
现代 UI、强动效、品牌定制界面 QML
团队全是 C++ 老手、无 JS 经验 两者皆可,新项目建议学 QML
存量 Widgets 项目 不强制迁移;可局部用 QQuickWidget 嵌入 QML

重要:两者可以共存。在 QWidget 应用里嵌入 QQuickWidget,或在 QML 中通过 QWidget::createWindowContainer(不推荐)都可行;大型商业软件常见"Widgets 外壳 + QML 内页"混合架构。

5. QML 的适用场景

5.1 移动端应用

  • Qt Quick Controls 2 提供 iOS/Android 原生观感控件(Material 风格)。
  • 触摸事件、多点触控、屏幕旋转都原生支持。
  • 打包工具:Qt 官方提供 Android APK / iOS 的构建链。

5.2 嵌入式与汽车 HMI

  • 仪表盘、中控屏、后视镜、智能家居面板。
  • Qt for MCU 提供 QML 子集(QML for MCU)用于微控制器。
  • 场景图渲染对嵌入式 GPU 友好;也支持纯软件渲染兜底。

5.3 桌面现代 UI

  • 快速原型、可视化工具、设计工具、音视频播放器皮肤。
  • 与 C++ 后端(网络、数据库、算法)组合,UI 层用 QML。

5.4 数据可视化与仪表板

  • 与 Qt Charts(QML 版)或自定义 Canvas/Shapes 结合。
  • 实时刷新的监控面板:QML 属性绑定天然适合"数据变→界面变"。

5.5 不适合的场景

  • 需要原生级文本编辑(代码编辑器)------Qt 建议 QScintilla/QPlainTextEdit(Widgets)。
  • 百万行级表格------用 QTableView 或 QML 虚拟化方案。
  • 深度系统集成(托盘、全局快捷键、原生对话框)------需要 C++/平台代码。

6. QML / JavaScript / HTML 的关系(彻底讲清)

6.1 三者是什么

QML JavaScript (JS) HTML
本质 声明式 UI 语言 脚本编程语言 网页标记语言
语法来源 JSON 扩展 ECMAScript 标准 SGML/XML 家族
谁在运行 QML 引擎(C++ 对象系统) 内嵌 JS 引擎(Qt5 V4 / Qt6 QJSEngine) 浏览器
作用 描述对象树与属性 编写逻辑、表达式、函数 描述网页结构

6.2 QML 中的 JS 是什么地位

QML 中到处是 JS:

javascript 复制代码
Item {
    property int count: 0
    property string label: "共 " + count + " 个"   // JS 表达式

    function format(v) { return v.toFixed(2) }     // JS 函数

    MouseArea {
        onClicked: {                              // JS 代码块
            parent.count += 1
            console.log(parent.label)
        }
    }
}

JS 在 QML 中的角色:

  1. 表达式:属性绑定右侧几乎都是 JS 表达式。
  2. 函数:function 声明(文档级)与 .js 文件(import "utils.js" as Utils)。
  3. 信号处理器:onXxx: { ... } 里的语句就是 JS。
  4. 数据操作:数组、对象、字符串处理。

6.3 QML 中的 JS 限制(重要)

QML 的 JS 不是浏览器 JS

  • ❌ 没有 document / window / DOM API。
  • ❌ 没有 CSS。
  • ❌ 没有浏览器事件模型(addEventListener 不适用,用信号)。
  • ❌ Qt6 没有 setTimeout(Qt5 有但非标准);用 Timer。
  • ❌ Qt6 中 QML 内不再提供 XMLHttpRequest 便捷访问;用 C++ 网络层。
  • ✅ 支持大部分 ECMAScript(Qt6 支持 ES6+:箭头函数、模板字符串、let/const、Promise、class 等)。

6.4 与 HTML 的对比示例

html 复制代码
<!-- HTML -->
<div class="card" onclick="doSomething()">
  <p>Hello</p>
</div>
javascript 复制代码
// QML
Rectangle {
    id: card
    MouseArea { anchors.fill: parent; onClicked: doSomething() }
    Text { text: "Hello" }
}

相似点:层级结构、属性赋值、事件回调。不同点:QML 对象是强类型 C++ 对象(属性有类型、有信号),HTML 元素是文档节点;QML 没有"CSS 选择器"式全局样式,样式就是属性本身。

7. Qt QML / Qt Quick / QML 三者的精确关系

名称 是什么 例子
QML 语言本身(语法、对象、绑定) property int x: 0
Qt QML 提供语言运行时的 C++ 模块 QQmlEngine、QQmlComponent
Qt Quick 基于 QML 的 UI 元素库 Item、Rectangle、ListView

口语中"QML 程序"通常指"用 Qt Quick 写的 QML 程序"。

8. Qt Quick 模块结构(Qt6 视角)

模块 import 语句 用途
QtQuick import QtQuick 基础元素、定位器、动画、状态、模型视图
QtQuick.Window import QtQuick.Window Window、Screen、Dialog(窗口级)
QtQuick.Controls import QtQuick.Controls 现成控件(Button/TextField/ComboBox/TabBar...)
QtQuick.Layouts import QtQuick.Layouts RowLayout/ColumnLayout/GridLayout
QtQuick.Dialogs import QtQuick.Dialogs 文件/颜色/字体对话框
QtQuick.Shapes import QtQuick.Shapes 矢量路径绘制(SVG 式)
QtQuick.Particles import QtQuick.Particles 粒子系统
QtQuick.Effects(6.5+) import QtQuick.Effects 模糊、阴影等图像特效
QtQuick.Timeline(6.4+) import QtQuick.Timeline 时间轴动画(Design Studio 用)
QtQuick.XmlListModel import QtQuick.XmlListModel XML 数据模型(旧,慎用)

Qt 5 中:Window 属于 QtQuick.Window 2.x;Controls 1.x(旧控件)与 Controls 2.x(新控件)并存,Qt6 只保留 2.x 体系。

9. 渲染原理:Scene Graph 与 RHI

9.1 为什么 QML 动画流畅

QML 渲染分为两层:

  1. QML 对象树(逻辑层):属性、绑定、信号由 CPU 处理。
  2. Scene Graph 场景图 (渲染层):Qt Quick 把可视元素翻译成绘制节点(rectangle、texture、text 等),在渲染线程批量提交给 GPU。

动画时只更新"属性值 + 场景图节点参数",GPU 负责插值绘制,所以 60fps 很轻松。这是 Widgets(CPU 逐像素绘制)做不到的。

9.2 渲染后端

  • Qt5:默认 OpenGL(QSGRendererInterface 尚未抽象完整)。
  • Qt6:引入 RHI(Rendering Hardware Interface),自动选择 OpenGL / Vulkan / Direct3D 11 / Metal。
  • 软件渲染兜底:QT_QUICK_BACKEND=software(调试渲染问题时用)。

9.3 对开发者意味着什么

  • 不要手动逐像素绘图(除非用 Canvas/Shapes),交给 Scene Graph。
  • 大列表用虚拟化(ListView 只创建可见项)。
  • 图片用 sourceSize 限制解码尺寸,避免 GPU 显存浪费。

10. QML 开发工具链

工具 作用
Qt Creator 官方 IDE:编辑、调试、QML Profiler、Design Mode
qml (Qt6)/ qmlscene(Qt5) 命令行运行单个 QML 文件
qmlformat 自动格式化 QML 代码(团队风格统一利器)
qmllint 静态检查 QML 语法/绑定/类型问题
qmlcachegen / qmltc QML 预编译/编译到 C++(Qt6)
qmlprofiler 性能分析:绑定求值、JS 调用、渲染帧率
Qt Design Studio 面向设计师的 UI 设计工具(生成 QML)
Qt Quick Designer Creator 内置的可视化编辑器

建议:CI 中加 qmllint 检查;提交前跑 qmlformat,团队代码风格统一。

11. 学习路线图(再回顾)

复制代码

12. 官方资料(加长清单)

相关推荐
水饺编程1 小时前
第5章,[Win32 章节] :创建、选择和删除画笔
c语言·c++·windows·visual studio
wind100321 小时前
Outdated Visual C++ Redistributable 过时 VC++ 运行库报错修复
开发语言·c++
小小龙学IT1 小时前
Abseil 开源 C++ 公共库深度解析:从 SwissTable 到 Status 的 Google 工程实践
c++·开源
兔兔兔兔11 小时前
记录C++ 12
开发语言·c++
此陆昭昭2 小时前
qt,使用qml写一个简单的界面
c++·qt
丰锋ff2 小时前
基于 Qt 的智能门禁系统(三)
开发语言·qt
沫璃染墨2 小时前
《从零入门Linux系统篇(二十九):文件篇·二——深入文件描述符:从文件描述符表到重定向,再到Shell实现》
linux·运维·服务器·c++·驱动开发·系统架构
码匠许师傅3 小时前
【C++ 面试真题】35. 聊聊 C++ 的万能引用(T&&)和完美转发(std::forward)
java·c++·面试
OPEN-F3 小时前
C++入门教程:数组、字符串与指针入门
数据结构·c++·算法