第二节 QML基础知识:语法、基本元素与属性绑定

发布时间:2026/8/28 5:57:12
第二节 QML基础知识:语法、基本元素与属性绑定 本章是 QML 入门的核心章。读完你应能独立写出带交互、带样式的简单界面。每个知识点都配有完整示例与易错提示。1. QML 文件的结构一个 .qml 文件就是一个 QML 文档。基本结构如下import QtQuick // 1. 导入模块Qt6 风格Qt5 写 import QtQuick 2.12 // 2. 根对象文件的根元素也决定了文件暴露出来的类型名文件名 Rectangle { id: root // 3. id当前文档内的唯一标识可选但强烈建议 width: 400 // 4. 属性赋值 height: 300 color: #3d5a80 // 十六进制颜色 // 5. 子对象 Text { anchors.centerIn: parent text: Hello QML color: white } // 6. 信号处理器 MouseArea { anchors.fill: parent onClicked: console.log(clicked!) } }1.1 硬性规则规则说明违反后果一个文件只有一个根对象根对象外的顶层只能有 import / pragma / 注释语法错误文件名首字母必须大写Main.qml ✅、main.qml ❌无法作为组件 importid 首字母必须小写myRect ✅、MyRect ❌解析错误对象类型名与文件名一致MyButton.qml 里根元素可以是任何类型但文件作为组件使用时叫 MyButton类型找不到注释// 单行、/* */ 多行、/// 文档注释—1.2 推荐书写顺序import 语句pragma如 pragma Singleton根对象的 id自定义属性property ...信号声明signal ...尺寸/外观属性子对象信号处理器 / 函数Component.onCompleted 等生命周期处理2. QML 语法基础2.1 对象Object对象用 Type { ... } 表示Type 可以是内建类型Item、Rectangle、Text、Image、ListView……自定义组件其他 .qml 文件的文件名MyButton {}。C 注册类型见第 04 章。Item { Rectangle { width: 100; height: 100 } // 分号可在一行写多个属性 Rectangle { width: 50; height: 50 } }2.2 属性Property属性语法属性名: 值。值的类型会自动转换/检查Rectangle { width: 200 height: width * 0.5 // 表达式高随宽变化 color: mouse.containsMouse ? #ff6666 : #6666ff // 三元表达式 visible: count 0 // 布尔表达式 }自定义属性Item { property int count: 0 // 类型int / real / double / bool / string / color / url / var / listT property string title: 默认标题 property bool isReady: false property color bgColor: #ffffff property url iconUrl: qrc:/icons/x.png property var anything: ({a: 1, b: [2,3]}) // var任意 JS 值 property listItem myChildren // 对象列表属性 readonly property int fixedValue: 42 // 只读属性只能在声明处赋值 default property var data2 // default 关键字子对象默认归属高级慎用 }要点自定义属性首字母小写驼峰property alias 见 03。readonly 属性在声明后不可再赋值适合常量。类型不匹配时引擎会告警并尝试转换int 赋值 abc 会失败并给默认值。2.3 属性类型速查表内建类型类型示例说明int42整数-2^31~2^31-1real / double3.14浮点数booltrue / false布尔stringhello字符串支持单双引号、模板字符串colorred / #ff0000 / #80ff0000颜色名称/十六进制/ARGBurlqrc:/a.pngURL字符串自动转fontfont { pixelSize: 14 }字体分组属性var[1,2] / {a:1}任意 JS 值listT—对象列表point / size / rectQt.point(1,2)Qt 值类型enum类型内定义枚举如 Text.AlignHCenter2.4 分组属性Grouped Property点分形式访问子对象属性Text { font.pixelSize: 24 font.bold: true font.family: Microsoft YaHei } // 等价写法 Text { font { pixelSize: 24 bold: true family: Microsoft YaHei } }常见分组属性font.*、anchors.*、Layout.*、text.*TextField、border.*Rectangle。2.5 附加属性Attached Property由模块给所有对象提供额外属性的机制写法 模块名.属性名。最常用Item { focus: true Keys.onPressed: (event) console.log(pressed, event.key) // Keys 附加 } import QtQuick.Layouts ColumnLayout { Rectangle { Layout.fillWidth: true // Layout 附加属性 } }其他Component.onCompleted、Drag.*、FontLoader 等。附加属性挂在任何对象上但只有对应模块被 import 才可用。2.6 信号处理器Signal Handler任何对象信号都能用 on信号名 连接Button { onClicked: console.log(clicked) // 单条语句 onPressedChanged: { // 多语句用花括号 if (pressed) console.log(down) else console.log(up) } }自定义信号Item { signal userClicked(string name, int times) // 自定义信号 参数 MouseArea { anchors.fill: parent onClicked: { parent.userClicked(alice, 3) // 发信号 } } // 在声明处接收通常由使用方连接更常见 onUserClicked: (name, times) console.log(name, times) }参数访问处理器中直接按参数名使用参数多时用箭头函数 (a, b) ... 更清晰Qt 5.15/Qt6 推荐。2.7 id 与作用域id 是文档内唯一标识首字母小写不能是关键字不能含 -。id 不跨文件见 03 的 id 陷阱。通过 id 可访问同级/子级对象label.text x。3. 基本元素Basic Elements详解3.1 Item —— 一切可视元素的基类Item 不绘制任何东西是容器/逻辑节点。常用属性属性说明x / y相对父对象左上角的坐标width / height尺寸默认 0z层叠顺序越大越上opacity不透明度 0~1visible是否可见false 时不参与布局/交互enabled是否可用false 时不可交互但可见rotation / scale旋转 / 缩放focus请求键盘焦点clip裁剪子内容到自身边界anchors.*锚点布局state / states / transitions状态与过渡children / childrenRect子对象Item { width: 100; height: 100 visible: true opacity: 0.5 z: 10 rotation: 45 }3.2 Rectangle —— 矩形Rectangle { width: 200; height: 120 color: steelblue // 颜色名 / #RRGGBB / #AARRGGBB radius: 12 // 圆角半径像素 border.color: white border.width: 2 gradient: Gradient { // 渐变设置后 color 失效 GradientStop { position: 0.0; color: white } GradientStop { position: 1.0; color: steelblue } } }要点默认 color 是白色white不是透明。border.width 0 时才绘制边框边框绘制在边界内部。radius 为矩形宽度一半时变成圆形/胶囊形。渐变与 color 互斥设置 gradient 后 color 无效。设置 antialiasing: true 可改善圆角锯齿少量性能开销。3.3 Text —— 文本Text { text: Hello font.pixelSize: 20 font.bold: true font.family: Microsoft YaHei font.italic: true font.underline: true color: #333333 horizontalAlignment: Text.AlignHCenter // 左/中/右 verticalAlignment: Text.AlignVCenter wrapMode: Text.Wrap // NoWrap / WordWrap / Wrap / WrapAnywhere elide: Text.ElideRight // 超出省略ElideLeft/Right/Middle/None maximumLineCount: 2 lineHeight: 1.2 // 行高倍率 textFormat: Text.AutoText // AutoText / PlainText / RichText支持 HTML 子集 renderType: Text.NativeRendering // 与 QPainter 渲染兼容可省 }RichText 子集textFormat: Text.RichText 时支持少量 HTML 标签b、i、a href 等Text { text: b加粗/b 和 a hrefhttps://qt.io链接/a textFormat: Text.RichText onLinkActivated: (link) console.log(open, link) }要点Text 默认按内容自动计算 implicitWidth/implicitHeight但显式 width/height 会优先。超出不换行会直接溢出绘制不裁剪、不省略需要时设置 wrapMode/elide。中文字体Windows 用 Microsoft YaHeimacOS 用 PingFang SCLinux 看系统。3.4 Image —— 图片Image { source: qrc:/images/logo.png // 资源路径推荐相对路径/绝对路径/file:// 亦可 sourceSize: Qt.size(64, 64) // 解码尺寸省内存 fillMode: Image.PreserveAspectFit // 见下方枚举 asynchronous: true // 异步解码避免卡 UI cache: true // 是否缓存解码结果 smooth: true // 平滑缩放 mipmap: true // 缩小显示时更清晰 mirror: false // 水平翻转 autoTransform: true // 按 EXIF 自动旋转 }fillMode 枚举模式行为Image.Stretch默认拉伸填满可能变形Image.PreserveAspectFit保持宽高比完整显示可能留白Image.PreserveAspectCrop保持宽高比裁剪溢出填满Image.Tile平铺仅当 sourceSize 未限制时可用Image.TileHorizontally / TileVertically单向平铺Image.Pad不缩放保持原始大小// 监听加载状态 Image { source: qrc:/big.png onStatusChanged: { if (status Image.Ready) console.log(loaded) else if (status Image.Error) console.log(error:, source) } }内存教训Image { source: ...; width: 40; height: 40 } 会把原图可能 4000x3000整张解码再缩放非常浪费内存。务必配合 sourceSize 限制解码尺寸。3.5 MouseArea —— 鼠标/触摸交互Rectangle { width: 200; height: 100 color: lightgray MouseArea { anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton // 接收哪些键 hoverEnabled: true // 需要悬停事件 propagateComposedEvents: false // 完整信号列表常用 onClicked: (mouse) console.log(click, mouse.x, mouse.y, mouse.button) onDoubleClicked: console.log(double) onPressed: console.log(pressed) onReleased: console.log(released) onPressedChanged: console.log(pressed) onPositionChanged: (mouse) console.log(move, mouse.x, mouse.y) onEntered: parent.color yellow onExited: parent.color lightgray onWheel: (wheel) console.log(wheel, wheel.angleDelta) // 拖拽 drag.target: parent drag.axis: Drag.XAndYAxis drag.minimumX: 0 drag.maximumX: 300 drag.threshold: 8 } }要点MouseArea 本身不绘制需要铺在可视元素上anchors.fill: parent。事件冒泡MouseArea 默认吃掉事件多层嵌套时最上层先处理可通过 mouse.accepted false 放行。触摸设备上同样触发 clickedQML 自动合并。hoverEnabled 为 false 时不会有 onEntered/onExited移动端默认关闭省电。3.6 Controls —— 现成控件import QtQuick.ControlsQt5 中为 import QtQuick.Controls 2.15import QtQuick import QtQuick.Controls ApplicationWindow { width: 400; height: 300 visible: true title: Controls 示例 Column { anchors.centerIn: parent spacing: 8 Button { text: 按钮; onClicked: console.log(btn) } TextField { placeholderText: 请输入…; width: 200 } TextArea { placeholderText: 多行…; width: 200; height: 60 } CheckBox { text: 勾选 } RadioButton { text: 单选 A } Slider { from: 0; to: 100; value: 50; width: 200 } ComboBox { model: [A, B, C]; width: 200 } ProgressBar { from: 0; to: 100; value: 50; width: 200 } Switch { text: 开关 } SpinBox { from: 0; to: 100; value: 5; width: 120 } } }控件名速查均属 ControlsButton / ToolButton / TextField / TextArea / ComboBox / CheckBox / RadioButton / Switch / Slider / ProgressBar / SpinBox / Dial / TabBar / StackView / SwipeView / Drawer / Dialog / Menu / ToolBar / StatusBar / SplitView / TableView / ScrollView / BusyIndicator / Label / GroupBox / Frame / Page。4. 属性绑定Property Binding—— QML 的灵魂4.1 什么是属性绑定当属性值是一个JS 表达式而非字面量时QML 引擎会记录表达式引用的所有属性任一被引用属性变化时表达式自动重新求值并更新属性Rectangle { width: 400; height: 300 Rectangle { id: dot width: 50; height: 50 radius: 25 x: parent.width / 2 - width / 2 // 依赖 parent.width 和自身 width y: parent.height / 2 - height / 2 } }窗口变化时圆点始终居中——无需任何手动刷新代码。4.2 绑定机制原理建立依赖解析表达式时引擎扫描出所有被访问的属性通过属性访问拦截。依赖图被依赖属性变化时通知依赖它的属性重新求值。传播重新求值如果导致该属性变化继续通知下一层级联更新。property int a: 10 property int b: 20 property int sum: a b // sum → 依赖 {a, b} // 修改 a 或 b → sum 自动更新 → 依赖 sum 的属性继续更新所以可以写出数据→中间量→UI的链式绑定形成数据流。4.3 绑定会被赋值破坏第一大坑在 JS 中写 sum 100 会解除绑定之后 sum 变成普通静态值Button { onClicked: { sum 100 // ❌ 绑定被破坏此后 a/b 变化 sum 不再更新 } }正确做法// 方案 1改源头属性 onClicked: a 50 // sum 仍绑定 ab自动更新 // 方案 2用 Qt.binding() 重建绑定 onClicked: sum Qt.binding(function() { return a * 2; }) // 方案 3用 Binding 元素声明式推荐用于条件绑定 Binding { target: someItem property: width value: someItem.enabled ? 200 : 100 when: someItem.enabled }判定技巧属性值如果是在声明块里写的表达式→ 是绑定如果是在函数/处理器里赋值→ 是一次性赋值。事件处理器里不要直接给绑定属性赋值除非你确实要破坏它。4.4 绑定中引用其他对象Rectangle { id: container width: 300; height: 200 Text { id: label text: width container.width // 通过 id 引用兄弟对象 } }绑定可以引用id、parent、children[i]、顶层属性、其他组件实例的属性、context property见 04、甚至 model 数据。4.5 常用绑定模式// 尺寸跟随 Rectangle { width: parent.width * 0.8 } // 条件变化 Rectangle { color: switch.enabled ? green : gray } // 字符串拼接 Text { text: count 0 ? 有 count 条 : 空 color: count 0 ? black : gray } // 调用函数函数内引用的属性也会被追踪依赖 function formatPercent(v) { return (v * 100).toFixed(1) %; } Text { text: formatPercent(progress) }⚠️ 在绑定表达式中调用 JS 函数时函数体内访问的属性会被引擎追踪QML 会静态扫描函数体。但不要在函数里做副作用修改其他属性、启动 IO否则难以排查。4.6 绑定 vs 赋值 决策表场景写法属性随数据变化绑定声明式一次性初始化Component.onCompleted: x 10事件响应里临时改值赋值注意是否破坏绑定两个属性联动绑定 明确源属性条件性绑定Binding { when: ... }经验法则能在声明处写绑定就不要在事件里赋值。4.7 循环绑定与自引用property int a: b 1 // ❌ a → b property int b: a 1 // ❌ b → a循环引擎会打印类似 Binding loop detected for property a 的告警并可能导致未定义行为。出现时重构明确单向数据流。5. JS 表达式与类型转换细节5.1 字符串property string s1: 单引号 property string s2: 双引号 property string s3: 转义 \ 引号 // Qt6 支持模板字符串 property string s4: count ${count} // Qt 5.12 部分支持Qt6 完整5.2 类型自动转换property int n: 42 1 // 结果是 421字符串拼接 property int n2: Number(42) 1 // 43 property int n3: parseInt(42px) // 42 遇字符串是拼接数字运算请确保两边都是数字。QML 属性赋值时会做类型转换width: 100 也能工作但不要依赖。5.3 数组与对象property var arr: [1, 2, 3] property var obj: { name: Alice, age: 30 } onClicked: { console.log(arr.length) // 3 console.log(obj[name]) // Alice arr.push(4) // 数组操作var 支持完整 JS 数组 API }5.4 Qt 全局对象常用函数函数用途Qt.binding(fn)重建属性绑定Qt.callLater(fn)延迟到下一事件循环执行去抖Qt.createQmlObject(str, parent)运行时创建 QML 对象Qt.createComponent(url)创建组件Qt.resolvedUrl(url)解析相对路径为绝对 URLQt.point/size/rect/color构造值类型Qt.formatDate/formatDateTime日期格式化Qt.md5(str)哈希调试用Qt.platform.os平台判断windows/linux/osx/android…Qt.openUrlExternally(url)用系统浏览器打开6. 综合示例可点击变色的计数器完整版import QtQuick import QtQuick.Controls Rectangle { id: root width: 340 height: 260 color: #f0f4f8 radius: 12 property int count: 0 property int step: 1 Rectangle { id: card anchors.centerIn: parent width: 280 height: 180 radius: 12 color: mouseArea.containsMouse ? #e6f2ff : #ffffff border.color: #d0d7de border.width: 1 Column { anchors.centerIn: parent spacing: 14 Text { anchors.horizontalCenter: parent.horizontalCenter text: root.count font.pixelSize: 56 font.bold: true color: root.count 10 ? #d64545 : #2c3e50 // 条件绑定 } Text { anchors.horizontalCenter: parent.horizontalCenter text: root.count 10 ? 达到上限 : 当前步长 root.step font.pixelSize: 14 color: gray } Row { anchors.horizontalCenter: parent.horizontalCenter spacing: 10 Button { text: - root.step onClicked: if (root.count 0) root.count - root.step // 注意此处是直接改 countcount 无绑定安全 } Button { text: root.step onClicked: if (root.count 20) root.count root.step } } } MouseArea { id: mouseArea anchors.fill: parent hoverEnabled: true } } // 右下角切换步长 Row { anchors.bottom: parent.bottom anchors.right: parent.right anchors.margins: 12 spacing: 6 Repeater { model: [1, 5, 10] delegate: Button { text: 步长 modelData highlighted: root.step modelData onClicked: root.step modelData } } } }说明count 用绑定驱动 UI文本、颜色修改只通过按钮事件不破坏任何绑定。step 用于演示数据源思想UI 只读 step改 step 由事件完成。Repeater 生成三个按钮02-3 详述。7. 本章小结QML 是声明式语言描述对象树与属性而非命令序列。三大语法支柱对象树、属性、信号处理器。主力元素Item / Rectangle / Text / Image / MouseArea / Controls。属性绑定是核心机制数据变、界面自动变小心 JS 赋值破坏绑定。类型自动转换与 JS 表达式是双刃剑注意字符串拼接陷阱。8. 自测题为什么 Rectangle {} 没有宽高就看不见说出三种会破坏属性绑定的写法。Text { text: a 1 } 显示什么Image 加载大图为何要设置 sourceSizevisible: false 和 opacity: 0 有什么区别

相关新闻