4. QML语法基础
QML 是 Qt Quick 的声明式 UI 语言,它把界面拆成“对象树 + 属性 + 表达式 + 事件”四件事。掌握这四件事,就能从“能跑”进入“可维护、可交付”的阶段。本节围绕元素、属性、绑定、信号与槽、组件化与模块导入六个关键词展开,并用一张结构关系图把它们的依赖关系串起来。
一、元素与对象树:QML 的“骨架”
QML 文件本质上是一棵对象树。根元素通常是 Item 或 Window,子元素通过缩进或花括号嵌套成为其 children。每个元素都有类型名、可选的 id、一组属性以及可选的子元素。例如:
import QtQuick 2.15
Rectangle {
id: root
width: 320; height: 200
color: "#f0f4f8"
Text {
id: title
anchors.centerIn: parent
text: "交付看板"
font.pixelSize: 20
}
}
这里 root 是根元素,title 是子元素。父元素负责布局与生命周期,子元素通过 parent 访问父级。交付项目中,建议给需要被外部引用的元素都加 id,但不要滥用全局 id,避免命名冲突。
二、属性与绑定:让界面“活”起来
属性是元素的静态特征,如 width、color、text。绑定则是把属性值写成表达式,QML 引擎会自动追踪表达式依赖的属性,一旦依赖变化就重新求值。这是 QML 区别于传统命令式 UI 的核心机制。
Rectangle {
id: card
width: 200; height: 120
Rectangle {
width: parent.width * 0.8
height: parent.height * 0.5
color: card.width > 180 ? "#3aa87a" : "#e0a63a"
}
}
绑定是单向的:表达式 → 属性。若在 JavaScript 中直接给属性赋值,会破坏绑定。交付中常见做法是:展示型属性用绑定,交互型属性用信号槽显式赋值,二者边界清晰。
三、信号与槽:事件驱动的入口
信号是元素发出的通知,槽是响应函数。QML 中既可以使用内置信号(如 MouseArea.clicked),也可以自定义信号。槽函数通常写成 on<SignalName>: { ... } 形式。
Rectangle {
id: btn
width: 120; height: 40
color: "#4a90d9"
signal submitted(string payload)
MouseArea {
anchors.fill: parent
onClicked: btn.submitted("confirm")
}
onSubmit: console.log("收到:", payload)
}
交付项目中,信号槽常用于:按钮点击、列表项选中、异步任务完成回调。注意槽函数应短小,耗时逻辑交给 C++ 或 WorkerScript,避免阻塞 UI 线程。
四、组件化与模块导入:从单文件到工程
当界面重复出现时,应抽成独立 .qml 文件,即组件。组件文件名首字母大写,内部根元素即组件类型。使用时直接写组件名,并通过属性暴露配置项。
// MyButton.qml
import QtQuick 2.15
Rectangle {
property alias text: label.text
signal clicked()
width: 120; height: 40
color: "#3aa87a"
Text { id: label; anchors.centerIn: parent; color: "white" }
MouseArea { anchors.fill: parent; onClicked: parent.clicked() }
}
// main.qml
import QtQuick 2.15
import "./components"
MyButton {
text: "提交"
onClicked: console.log("提交成功")
}
模块导入使用 import,可以是 Qt 官方模块(QtQuick 2.15)、本地目录("./components")或自定义 URI。导入顺序决定同名类型的解析优先级,后导入的会覆盖先导入的。交付时建议把组件目录统一放在 qml/components,并在 qmldir 中声明模块,便于版本管理。
| 语法要素 | 作用 | 交付注意点 |
|---|---|---|
| 元素 Element | 构成对象树,决定层级与生命周期 | id 命名统一前缀,避免全局污染 |
| 属性 Property | 描述静态特征与可配置项 | 对外暴露的属性加 property alias |
| 绑定 Binding | 依赖追踪,自动更新 | 保持纯表达式,避免副作用 |
| 信号与槽 | 事件响应与解耦 | 槽函数短小,耗时逻辑下沉 |
| 组件化 | 复用 UI 单元 | 文件名大写,接口稳定 |
| 模块导入 | 组织代码与依赖 | qmldir 声明,路径统一 |
Math.random() 或修改其他对象属性,会导致绑定失效或无限循环;组件化后仍用绝对路径导入,换机器后编译失败。交付前应统一检查 import 路径与 qmldir 配置。五、复盘:把语法变成交付能力
语法本身不难,难的是在多人协作和长期维护中保持一致性。建议在项目初期就约定:id 命名规范、组件目录结构、属性暴露方式、信号命名风格。把本节六个要素当成检查清单,每新增一个 QML 文件都过一遍,能显著降低后期联调成本。