第一节 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. 官方资料(加长清单)

相关推荐
fpcc3 小时前
计算机原理—构建过程中的分段分析
c++
xxwxx__3 小时前
C++ AVL 树深度剖析:平衡二叉搜索树原理、源码与面试考点
数据结构·c++
Escalating_xu3 小时前
【C语言常见概念】从第一行代码到字符串结束标志:一次打通编译、main、ASCII、转义字符与注释
c语言·c++
bnmoel4 小时前
C++ 基础入门篇(三):内联函数与空指针
c++·内联函数·nullptr·低层
码匠许师傅4 小时前
【C++三方组件】TinyXML2:两个文件的极简 XML 解析器
xml·c++
free-elcmacom4 小时前
C++学习<1>程序分区
开发语言·c++
xxwxx__7 小时前
C++ STL set 与 map 全套详解:关联式容器、红黑树底层、代码坑点、刷题实战
数据结构·c++
j7~8 小时前
【Linux网络加餐】(篇六)网络版计算器(上):模板方法模式、序列化与 JSON
linux·c++·json·序列化·反序列化·网络版计算机
weixin_307779138 小时前
C++代码实现MATLAB中的ode45函数功能
开发语言·c++·算法·matlab
张小姐的猫8 小时前
【AI大模型接入SDK】 —— 数据管理 & 与Session模块进行联动
数据结构·数据库·c++·人工智能·python·chatgpt