Qt Quick:Import、ID 与 QML 属性详解
- [Qt Quick 学习笔记:Import、ID 与 QML 属性详解](#Qt Quick 学习笔记:Import、ID 与 QML 属性详解)
- 二、Import:导入模块与资源
-
- [2.1 Import 是什么](#2.1 Import 是什么)
- [2.2 Import 与 include 的区别](#2.2 Import 与 include 的区别)
-
- [C/C++ 的 include](#C/C++ 的 include)
- [QML 的 import](#QML 的 import)
- 二者对比
- [2.3 模块导入的基本语法](#2.3 模块导入的基本语法)
- [2.4 Qt 5 与 Qt 6 的版本号写法](#2.4 Qt 5 与 Qt 6 的版本号写法)
- [2.5 使用 as 指定命名空间](#2.5 使用 as 指定命名空间)
-
- [使用 as 的常见场景](#使用 as 的常见场景)
- [三、导入自定义 QML 文件](#三、导入自定义 QML 文件)
-
- [3.1 当前目录中的 QML 文件](#3.1 当前目录中的 QML 文件)
- [3.2 导入其他目录中的 QML 文件](#3.2 导入其他目录中的 QML 文件)
- [3.3 给目录设置别名](#3.3 给目录设置别名)
- [3.4 模块导入与目录导入对比](#3.4 模块导入与目录导入对比)
- [四、QML 中的 id](#四、QML 中的 id)
-
- [4.1 id 是什么](#4.1 id 是什么)
- [4.2 id 的主要作用](#4.2 id 的主要作用)
- [五、id 的四个重要特性](#五、id 的四个重要特性)
-
- [5.1 唯一性](#5.1 唯一性)
- [5.2 只读性](#5.2 只读性)
- [5.3 id 不是普通对象属性](#5.3 id 不是普通对象属性)
- [5.4 id 的命名规则](#5.4 id 的命名规则)
- 六、parent:引用父对象
- [七、QML 属性基础](#七、QML 属性基础)
-
- [7.1 什么是属性](#7.1 什么是属性)
- [7.2 自定义属性的声明格式](#7.2 自定义属性的声明格式)
- [7.3 常见属性类型](#7.3 常见属性类型)
- [7.4 属性命名规则](#7.4 属性命名规则)
- 八、属性修饰符
-
- [8.1 default 属性](#8.1 default 属性)
- [8.2 required 属性](#8.2 required 属性)
- [8.3 readonly 属性](#8.3 readonly 属性)
- 九、属性初始化与赋值
- 十、属性绑定
-
- [10.1 什么是属性绑定](#10.1 什么是属性绑定)
- [10.2 固定赋值与属性绑定的区别](#10.2 固定赋值与属性绑定的区别)
- [10.3 绑定到表达式](#10.3 绑定到表达式)
- [10.4 绑定到函数](#10.4 绑定到函数)
- [10.5 直接赋值可能破坏绑定](#10.5 直接赋值可能破坏绑定)
- 十一、属性变化信号
-
- [11.1 属性会自动生成变化信号](#11.1 属性会自动生成变化信号)
- [11.2 信号处理器的命名规则](#11.2 信号处理器的命名规则)
- [11.3 属性变化信号示例](#11.3 属性变化信号示例)
- [11.4 内置属性也有变化信号](#11.4 内置属性也有变化信号)
- [十二、对象列表 property list](#十二、对象列表 property list)
- 十三、属性组
- [十四、属性别名 property alias](#十四、属性别名 property alias)
-
- [14.1 什么是属性别名](#14.1 什么是属性别名)
- [14.2 属性别名示例](#14.2 属性别名示例)
- [14.3 属性别名的双向关系](#14.3 属性别名的双向关系)
- [14.4 属性别名与普通绑定的区别](#14.4 属性别名与普通绑定的区别)
- [14.5 使用 alias 暴露子组件属性](#14.5 使用 alias 暴露子组件属性)
- [十五、只读属性 readonly property](#十五、只读属性 readonly property)
-
- [15.1 定义只读属性](#15.1 定义只读属性)
- [15.2 只读属性不能重新赋值](#15.2 只读属性不能重新赋值)
- [15.3 只读属性可以绑定表达式](#15.3 只读属性可以绑定表达式)
- [15.4 readonly 与 C++ const 的理解对比](#15.4 readonly 与 C++ const 的理解对比)
- 十六、综合示例
-
- 十七、综合示例解析
-
- [1. 导入模块](#1. 导入模块)
- [2. 使用 id 标识窗口](#2. 使用 id 标识窗口)
- [3. 声明普通属性](#3. 声明普通属性)
- [4. 声明只读属性](#4. 声明只读属性)
- [5. 声明属性别名](#5. 声明属性别名)
- [6. 监听属性变化](#6. 监听属性变化)
- [7. 使用属性组](#7. 使用属性组)
- 十八、常见错误整理
-
- [18.1 重复使用相同 id](#18.1 重复使用相同 id)
- [18.2 id 使用引号](#18.2 id 使用引号)
- [18.3 id 以数字开头](#18.3 id 以数字开头)
- [18.4 在对象声明中使用等号](#18.4 在对象声明中使用等号)
- [18.5 属性类型不匹配](#18.5 属性类型不匹配)
- [18.6 修改只读属性](#18.6 修改只读属性)
- [18.7 混淆 alias 与普通属性绑定](#18.7 混淆 alias 与普通属性绑定)
- 十九、核心知识对比表
- 二十、快速复习口诀
- 二十一、复习问题
- 二十二、本节总结
Qt Quick 学习笔记:Import、ID 与 QML 属性详解
本文整理 Qt Quick 开发中的三个基础知识点:
import、id和 QML 属性。这三个部分虽然属于基础语法,但会直接影响后续的组件拆分、属性绑定、对象通信和项目结构设计。
一、本节内容概览
本文主要包含以下内容:
import的作用和基本语法import与 C/C++ 中include的区别- 模块导入与目录导入
as命名空间限定符- QML 中
id的作用 id的唯一性、只读性和命名规则- QML 自定义属性的声明
- 属性初始化、赋值与属性绑定
- 属性变化信号
- 对象列表
property list - 属性组
- 属性别名
property alias - 只读属性
readonly property
二、Import:导入模块与资源
2.1 Import 是什么
在 QML 中,import 用于告诉 QML 引擎:
当前 QML 文件需要使用哪些模块、组件、JavaScript 资源或其他目录中的 QML 文件。
例如:
qml
import QtQuick
import QtQuick.Controls
import QtQuick.Window
不同模块提供不同类型的组件。
| 模块 | 常见组件或作用 |
|---|---|
QtQuick |
Rectangle、Text、Image、MouseArea 等 |
QtQuick.Controls |
Button、Label、TextField 等控件 |
QtQuick.Window |
Window 等窗口类型 |
QtQuick.Layouts |
RowLayout、ColumnLayout、GridLayout 等 |
QtQml |
QML 核心语言和对象支持 |
例如,下面的代码同时使用了窗口、矩形和按钮:
qml
import QtQuick
import QtQuick.Controls
import QtQuick.Window
Window {
width: 800
height: 600
visible: true
Rectangle {
width: 200
height: 100
color: "lightblue"
Button {
text: "确定"
anchors.centerIn: parent
}
}
}
2.2 Import 与 include 的区别
QML 中的 import 和 C/C++ 中的 #include 看起来作用相似,但工作方式不同。
C/C++ 的 include
cpp
#include <iostream>
C/C++ 的 #include 会在预处理阶段,将头文件内容引入当前源文件。
可以简单理解为:
text
将头文件内容展开到当前位置
QML 的 import
qml
import QtQuick
QML 的 import 不会把模块源码复制到当前 QML 文件。
它的作用更接近:
text
告诉 QML 引擎去哪里寻找和加载需要的类型与资源
二者对比
| 对比项 | C/C++ #include |
QML import |
|---|---|---|
| 执行阶段 | 编译预处理阶段 | QML 引擎加载阶段 |
| 是否复制代码 | 会进行文本展开 | 不会复制模块代码 |
| 主要作用 | 引入声明或定义 | 注册并访问 QML 类型和资源 |
| 常见对象 | 头文件 | QML 模块、目录、JS 资源 |
可以用
include帮助理解import,但不要把二者完全等同。
2.3 模块导入的基本语法
传统的模块导入结构可以写成:
qml
import 模块名称 版本号 as 限定名称
例如:
qml
import QtQuick.Controls 2.15 as MyControls
其中:
| 组成部分 | 示例 | 说明 |
|---|---|---|
import |
import |
导入关键字 |
| 模块名称 | QtQuick.Controls |
Module Identifier |
| 版本号 | 2.15 |
Version Number |
| 限定名称 | MyControls |
Qualifier |
不过,版本号和 as 都可能省略。
最常见的写法是:
qml
import QtQuick
import QtQuick.Controls
2.4 Qt 5 与 Qt 6 的版本号写法
Qt 5 项目中经常看到:
qml
import QtQuick 2.15
import QtQuick.Controls 2.15
Qt 6 项目中通常可以使用无版本号写法:
qml
import QtQuick
import QtQuick.Controls
课程中建议,在将 Qt 5 项目迁移到 Qt 6 时,优先检查并调整带版本号的导入语句。
实际开发时,应以当前 Qt 版本、项目配置和构建工具的要求为准。
2.5 使用 as 指定命名空间
as 可以给导入的模块指定一个局部名称。
例如:
qml
import QtQuick.Controls as MyControls
使用组件时,需要加上限定名称:
qml
MyControls.Button {
text: "确定"
}
完整示例:
qml
import QtQuick
import QtQuick.Window
import QtQuick.Controls as MyControls
Window {
width: 800
height: 600
visible: true
MyControls.Button {
text: "确定"
anchors.centerIn: parent
}
}
使用 as 的常见场景
- 不同模块中存在同名类型;
- 希望明确组件来自哪个模块;
- 导入自定义组件目录;
- 避免污染当前文件的全局命名空间。
如果没有类型冲突,一般不需要使用 as。
三、导入自定义 QML 文件
3.1 当前目录中的 QML 文件
如果自定义组件和当前文件位于同一个目录,通常可以直接使用,不需要再次写 import。
例如,项目目录如下:
text
MyProject/
├── Main.qml
└── CustomButton.qml
CustomButton.qml:
qml
import QtQuick
import QtQuick.Controls
Button {
width: 140
height: 45
text: "自定义按钮"
}
在 Main.qml 中可以直接使用:
qml
import QtQuick
import QtQuick.Window
Window {
width: 800
height: 600
visible: true
CustomButton {
anchors.centerIn: parent
}
}
3.2 导入其他目录中的 QML 文件
假设项目目录如下:
text
MyProject/
├── Main.qml
└── components/
└── CustomRectangle.qml
可以在 Main.qml 中导入 components 目录:
qml
import QtQuick
import QtQuick.Window
import "components"
然后直接使用:
qml
CustomRectangle {
anchors.centerIn: parent
}
3.3 给目录设置别名
也可以使用 as 为目录指定别名:
qml
import "components" as Components
使用组件时:
qml
Components.CustomRectangle {
anchors.centerIn: parent
}
完整示例:
qml
import QtQuick
import QtQuick.Window
import "components" as Components
Window {
width: 800
height: 600
visible: true
Components.CustomRectangle {
anchors.centerIn: parent
}
}
这种方式可以清楚地看出组件来自哪个目录。
3.4 模块导入与目录导入对比
| 类型 | 示例 | 主要用途 |
|---|---|---|
| 模块导入 | import QtQuick.Controls |
导入已经注册的 QML 模块 |
| 目录导入 | import "components" |
导入本地目录中的 QML 文件 |
| 带别名导入 | import "components" as Components |
使用命名空间访问组件 |
四、QML 中的 id
4.1 id 是什么
id 是 QML 对象的唯一标识符。
例如:
qml
Rectangle {
id: redRectangle
width: 200
height: 100
color: "red"
}
其他对象可以通过 redRectangle 访问该对象的属性:
qml
Text {
text: "矩形宽度:" + redRectangle.width
}
还可以在按钮事件中修改它:
qml
Button {
text: "增加宽度"
onClicked: {
redRectangle.width += 10
}
}
4.2 id 的主要作用
id 最常见的作用有:
- 引用当前文件中的其他对象;
- 访问对象属性;
- 调用对象函数;
- 建立属性绑定;
- 在信号处理器中修改对象状态。
例如:
qml
Rectangle {
id: targetRectangle
width: 100
height: 100
color: "orange"
}
Button {
text: "变大"
onClicked: {
targetRectangle.width += 20
targetRectangle.height += 20
}
}
五、id 的四个重要特性
5.1 唯一性
在同一个 QML 作用域中,不能存在两个相同的 id。
错误示例:
qml
Rectangle {
id: box
}
Rectangle {
id: box
}
两个对象都使用了 box,QML 组件将无法正常加载。
正确写法:
qml
Rectangle {
id: redBox
}
Rectangle {
id: blueBox
}
可以将 id 理解为对象在当前作用域中的"身份证号码"。
5.2 只读性
id 在声明后不能被重新赋值。
错误示例:
qml
Rectangle {
id: redRectangle
Component.onCompleted: {
redRectangle.id = blueRectangle
}
}
不能在程序运行过程中修改一个对象的 id。
因此,id 应当在对象定义时确定。
5.3 id 不是普通对象属性
下面这些属于对象属性:
qml
width: 200
height: 100
color: "red"
visible: true
而 id 是 QML 语言提供的特殊标识符,不是普通属性。
因此,不能像修改 width 一样修改 id:
qml
redRectangle.width = 300 // 正确
redRectangle.id = newId // 错误
5.4 id 的命名规则
id 必须:
- 以小写字母开头;
- 或者以下划线开头;
- 后续可以包含字母、数字和下划线;
- 不能以数字开头;
- 不能包含空格;
- 不需要使用引号。
正确示例:
qml
id: redRectangle
id: button1
id: main_window
id: _privateObject
错误示例:
qml
id: 1button
id: "redRectangle"
id: red-rectangle
id: red rectangle
推荐使用小驼峰命名:
qml
id: mainWindow
id: loginButton
id: userNameLabel
id: contentRectangle
六、parent:引用父对象
QML 对象可以嵌套。
例如:
qml
Rectangle {
width: 400
height: 300
color: "lightgray"
Rectangle {
width: 100
height: 100
color: "blue"
}
}
内层矩形的父对象是外层矩形。
子对象可以使用 parent 引用父对象:
qml
Rectangle {
width: parent.width / 2
height: parent.height / 2
color: "blue"
}
也可以让子对象在父对象中居中:
qml
anchors.centerIn: parent
完整示例:
qml
Rectangle {
id: container
width: 400
height: 300
color: "lightgray"
Rectangle {
width: parent.width / 2
height: parent.height / 2
color: "skyblue"
anchors.centerIn: parent
}
}
七、QML 属性基础
7.1 什么是属性
属性用于保存或描述对象的状态。
例如:
qml
Rectangle {
width: 200
height: 100
color: "red"
visible: true
}
这里的:
widthheightcolorvisible
都是属性。
属性值可以是:
- 固定值;
- 表达式;
- 其他对象的属性;
- JavaScript 函数返回值;
- 动态绑定结果。
7.2 自定义属性的声明格式
QML 中可以通过 property 声明自定义属性。
基本格式:
qml
property 属性类型 属性名称: 初始值
例如:
qml
property int count: 0
property string title: "Qt Quick"
property bool running: false
property real scaleValue: 1.5
完整示例:
qml
Rectangle {
id: root
property int count: 10
property string title: "QML 属性学习"
property bool enabledState: true
property real opacityValue: 0.8
width: 300
height: 200
opacity: opacityValue
}
7.3 常见属性类型
| 属性类型 | 说明 | 示例 |
|---|---|---|
int |
整数 | property int count: 10 |
real |
小数 | property real ratio: 1.5 |
double |
双精度小数 | property double value: 3.14 |
bool |
布尔值 | property bool running: true |
string |
字符串 | property string name: "QML" |
color |
颜色 | property color themeColor: "blue" |
url |
URL 地址 | property url imageSource: "logo.png" |
var |
任意类型 | property var data: [] |
list |
对象列表 | property list<Rectangle> items |
alias |
属性别名 | property alias boxWidth: box.width |
7.4 属性命名规则
属性名称通常应当:
- 以小写字母开头;
- 使用字母、数字和下划线;
- 不使用空格;
- 不以数字开头;
- 不使用 JavaScript 保留关键字;
- 推荐使用小驼峰命名。
正确示例:
qml
property int userAge: 18
property string userName: "Tom"
property bool loginState: false
不推荐或错误示例:
qml
property int 1age: 18
property string user-name: "Tom"
property bool var: false
八、属性修饰符
QML 属性可以搭配不同修饰符。
常见修饰符包括:
qml
default
required
readonly
基本结构可以表示为:
qml
[default] [required] [readonly] property 类型 名称: 初始值
这些修饰符一般不会同时全部使用,而是根据需求选择。
8.1 default 属性
default 表示对象的默认属性。
当子对象没有明确写入某个属性时,会自动进入默认属性。
例如,某些 QML 容器类型可以将子对象自动加入其默认数据属性。
自定义组件中也可以声明默认属性:
qml
default property alias content: container.data
示例:
qml
Item {
default property alias content: container.data
Item {
id: container
anchors.fill: parent
}
}
使用时,子对象可以直接放在组件内部。
8.2 required 属性
required 表示该属性必须由组件使用者提供。
例如:
qml
Rectangle {
required property string title
required property color backgroundColor
color: backgroundColor
Text {
text: title
anchors.centerIn: parent
}
}
使用组件时,必须为这些属性赋值:
qml
CustomCard {
title: "用户信息"
backgroundColor: "lightblue"
}
如果没有设置 required 属性,组件可能无法正常创建。
8.3 readonly 属性
readonly 表示属性只能读取,不能在外部重新赋值。
例如:
qml
readonly property int maxCount: 100
错误写法:
qml
maxCount = 200
只读属性适合保存:
- 固定配置;
- 计算结果;
- 不允许外部修改的数据;
- 组件内部状态的只读接口。
九、属性初始化与赋值
9.1 使用冒号进行初始化
在对象声明中,一般使用冒号初始化属性:
qml
property int count: 10
width: 200
height: 100
color: "red"
推荐在冒号后添加一个空格:
qml
width: 200
不推荐:
qml
width:200
虽然两种写法通常都能运行,但第一种更易读。
9.2 在 JavaScript 代码块中使用等号赋值
在事件处理器或函数中,需要使用等号赋值:
qml
Button {
text: "增加"
onClicked: {
count = count + 1
}
}
也可以写成:
qml
count += 1
因此,需要区分两个场景。
声明和初始化
qml
property int count: 0
运行期间修改
qml
count = 10
9.3 类型需要匹配
属性值应当与属性类型匹配。
正确示例:
qml
property int count: 10
property string title: "QML"
property bool running: true
错误示例:
qml
property int count: "hello"
property bool running: 100
部分数值类型之间可能发生自动转换。
例如:
qml
property real sizeValue: 10
整数 10 可以作为 real 类型的值使用。
但实际开发中仍应尽量保证类型清晰,避免依赖隐式转换。
十、属性绑定
10.1 什么是属性绑定
当一个属性的值依赖另一个属性时,QML 会建立动态绑定关系。
例如:
qml
Rectangle {
id: redRectangle
width: 200
height: 100
color: "red"
}
Rectangle {
width: redRectangle.width / 2
height: 100
color: "blue"
}
蓝色矩形的宽度绑定到了红色矩形:
qml
width: redRectangle.width / 2
当红色矩形宽度变化时,蓝色矩形宽度会自动更新。
10.2 固定赋值与属性绑定的区别
固定值:
qml
width: 100
属性绑定:
qml
width: parent.width / 2
二者区别如下:
| 类型 | 示例 | 是否自动更新 |
|---|---|---|
| 固定值 | width: 100 |
否 |
| 属性绑定 | width: parent.width / 2 |
是 |
| 对象属性绑定 | width: redBox.width |
是 |
| 函数表达式绑定 | width: calculateWidth() |
依赖项变化时重新计算 |
10.3 绑定到表达式
属性可以绑定到一个表达式:
qml
width: parent.width - 40
height: width / 2
visible: parent.width > 500
也可以使用三元表达式:
qml
color: width > 200 ? "red" : "blue"
10.4 绑定到函数
qml
Rectangle {
id: box
property int baseWidth: 100
function calculateWidth() {
return baseWidth * 2
}
width: calculateWidth()
height: 100
}
当函数中依赖的 QML 属性发生变化时,绑定结果可能重新计算。
10.5 直接赋值可能破坏绑定
假设原来存在绑定:
qml
Rectangle {
id: blueBox
width: redBox.width / 2
}
之后执行:
qml
blueBox.width = 300
此时原来的绑定通常会被固定值 300 替代。
后续即使 redBox.width 改变,blueBox.width 也可能不再跟随变化。
因此需要区分:
qml
width: redBox.width / 2
和:
qml
blueBox.width = 300
前者是动态绑定,后者是直接赋值。
十一、属性变化信号
11.1 属性会自动生成变化信号
声明一个属性后,QML 会自动为它生成变化信号。
例如:
qml
property int count: 0
会自动产生类似下面的变化信号:
qml
countChanged
对应的信号处理器是:
qml
onCountChanged
11.2 信号处理器的命名规则
属性名称首字母大写,并在前面添加 on,后面添加 Changed。
| 属性名称 | 变化信号处理器 |
|---|---|
count |
onCountChanged |
userName |
onUserNameChanged |
currentValue |
onCurrentValueChanged |
backgroundColor |
onBackgroundColorChanged |
11.3 属性变化信号示例
qml
Rectangle {
property int count: 0
onCountChanged: {
console.log("count 发生变化,当前值:", count)
}
Button {
text: "增加"
onClicked: {
count += 1
}
}
}
每次按钮点击后,count 发生变化,onCountChanged 会自动执行。
11.4 内置属性也有变化信号
例如:
qml
Rectangle {
width: 100
onWidthChanged: {
console.log("矩形宽度变化:", width)
}
}
常见内置属性变化信号包括:
qml
onWidthChanged
onHeightChanged
onXChanged
onYChanged
onVisibleChanged
onOpacityChanged
十二、对象列表 property list
12.1 对象列表是什么
对象列表用于在一个属性中保存多个 QML 对象。
基本语法:
qml
property list<对象类型> 属性名称: [
对象1 {},
对象2 {},
对象3 {}
]
例如:
qml
property list<Rectangle> rectangles: [
Rectangle {
width: 100
height: 100
color: "red"
},
Rectangle {
width: 100
height: 100
color: "blue"
}
]
12.2 完整示例
qml
Item {
property list<Rectangle> rectangleList: [
Rectangle {
width: 100
height: 100
color: "red"
},
Rectangle {
width: 120
height: 100
color: "green"
},
Rectangle {
width: 140
height: 100
color: "blue"
}
]
Component.onCompleted: {
console.log("列表长度:", rectangleList.length)
console.log("第一个矩形颜色:", rectangleList[0].color)
console.log("第二个矩形宽度:", rectangleList[1].width)
}
}
12.3 length 与下标
对象列表常用两种操作:
获取列表长度
qml
rectangleList.length
根据下标获取对象
qml
rectangleList[0]
rectangleList[1]
rectangleList[2]
下标从 0 开始。
例如,一个长度为 3 的列表,其有效下标为:
text
0、1、2
12.4 列表书写注意事项
建议使用下面的格式:
qml
property list<Rectangle> items: [
Rectangle {
color: "red"
},
Rectangle {
color: "blue"
}
]
注意:
- 每个元素之间使用逗号分隔;
- 最后一个元素后通常不需要再写逗号;
- 通过
[下标]访问元素; - 使用
.length获取列表长度。
十三、属性组
13.1 什么是属性组
某些属性内部还包含多个子属性。
例如,font 属性中包含:
pixelSizebolditalicfamilyunderline
这些相关属性可以看作一个属性组。
13.2 点语法
可以通过点语法设置属性:
qml
Text {
text: "Qt Quick"
font.pixelSize: 24
font.bold: true
font.italic: false
}
13.3 属性组语法
也可以使用属性组的写法:
qml
Text {
text: "Qt Quick"
font {
pixelSize: 24
bold: true
italic: false
}
}
两种写法效果相同。
13.4 两种写法对比
点语法
qml
font.pixelSize: 24
font.bold: true
font.family: "Microsoft YaHei"
属性组语法
qml
font {
pixelSize: 24
bold: true
family: "Microsoft YaHei"
}
如果只修改一个子属性,点语法更加简洁。
如果同时修改多个相关属性,属性组语法通常更整齐。
十四、属性别名 property alias
14.1 什么是属性别名
属性别名是对已有属性的直接引用。
基本格式:
qml
property alias 别名名称: 对象id.属性名称
例如:
qml
property alias boxWidth: box.width
这里的 boxWidth 并没有创建新的独立存储值,而是直接引用 box.width。
14.2 属性别名示例
qml
Rectangle {
id: root
property alias contentWidth: contentRectangle.width
width: 400
height: 300
Rectangle {
id: contentRectangle
width: 200
height: 100
color: "lightblue"
}
}
此时:
qml
root.contentWidth
实际上就是:
qml
contentRectangle.width
14.3 属性别名的双向关系
修改原属性:
qml
contentRectangle.width = 300
别名读取到的值也会变成 300。
修改别名:
qml
root.contentWidth = 500
原属性也会变成:
qml
contentRectangle.width === 500
因此,属性别名具有双向访问效果。
14.4 属性别名与普通绑定的区别
普通属性绑定:
qml
property int copiedWidth: contentRectangle.width
属性别名:
qml
property alias contentWidth: contentRectangle.width
二者对比如下:
| 对比项 | 普通属性绑定 | 属性别名 |
|---|---|---|
| 是否创建新属性存储 | 是 | 否 |
| 是否直接引用原属性 | 否 | 是 |
| 修改原属性 | 绑定值会更新 | 别名值同步 |
| 修改当前属性 | 可能破坏绑定 | 会直接修改原属性 |
| 典型用途 | 计算或同步值 | 暴露内部对象属性 |
14.5 使用 alias 暴露子组件属性
property alias 经常用于自定义组件。
例如,创建 CustomLabel.qml:
qml
import QtQuick
Rectangle {
id: root
property alias text: label.text
property alias fontSize: label.font.pixelSize
width: 240
height: 60
color: "lightgray"
Text {
id: label
anchors.centerIn: parent
}
}
在外部使用时:
qml
CustomLabel {
text: "欢迎学习 Qt Quick"
fontSize: 20
}
外部虽然没有直接访问组件内部的 label,但可以通过别名控制它的属性。
十五、只读属性 readonly property
15.1 定义只读属性
只读属性通过 readonly property 声明:
qml
readonly property int maxCount: 100
完整示例:
qml
Rectangle {
readonly property int maxCount: 100
readonly property string appName: "Qt Quick Demo"
}
15.2 只读属性不能重新赋值
错误示例:
qml
Button {
text: "修改"
onClicked: {
maxCount = 200
}
}
程序会提示只读属性不能被修改。
15.3 只读属性可以绑定表达式
只读不代表只能保存固定值。
例如:
qml
Item {
property int itemWidth: 200
property int itemHeight: 100
readonly property int area: itemWidth * itemHeight
}
area 不能被外部直接赋值,但会根据 itemWidth 和 itemHeight 自动更新。
例如:
qml
itemWidth = 300
此时:
qml
area === 300 * itemHeight
这种写法适合暴露计算结果。
15.4 readonly 与 C++ const 的理解对比
可以把 readonly 暂时理解为类似 C++ 中的 const:
cpp
const int maxCount = 100;
不过二者不是完全相同的语言机制。
在学习阶段,可以先记住:
readonly property允许读取和绑定,但不允许从外部直接重新赋值。
十六、综合示例
下面通过一个完整案例,将本节知识点组合起来。
qml
import QtQuick
import QtQuick.Controls
import QtQuick.Window
Window {
id: mainWindow
width: 800
height: 600
visible: true
title: "Import、ID 与属性综合示例"
property int clickCount: 0
property string message: "尚未点击按钮"
readonly property int maxWidth: 500
property alias targetWidth: targetRectangle.width
onClickCountChanged: {
console.log("点击次数发生变化:", clickCount)
}
Rectangle {
id: targetRectangle
width: 200
height: 120
color: clickCount % 2 === 0 ? "lightblue" : "lightgreen"
radius: 10
anchors.centerIn: parent
Text {
text: message
anchors.centerIn: parent
font {
pixelSize: 18
bold: true
}
}
}
Button {
id: increaseButton
text: "增加宽度"
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.leftMargin: 20
anchors.bottomMargin: 20
onClicked: {
clickCount += 1
message = "已经点击 " + clickCount + " 次"
if (targetRectangle.width < maxWidth) {
targetWidth += 20
}
console.log("当前矩形宽度:", targetWidth)
}
}
Button {
id: resetButton
text: "恢复默认"
anchors.left: increaseButton.right
anchors.bottom: parent.bottom
anchors.leftMargin: 10
anchors.bottomMargin: 20
onClicked: {
clickCount = 0
targetWidth = 200
message = "尚未点击按钮"
console.log("界面已经恢复默认状态")
}
}
}
十七、综合示例解析
1. 导入模块
qml
import QtQuick
import QtQuick.Controls
import QtQuick.Window
分别用于基础类型、控件和窗口。
2. 使用 id 标识窗口
qml
id: mainWindow
当前文件中的其他对象可以通过 mainWindow 引用窗口。
3. 声明普通属性
qml
property int clickCount: 0
property string message: "尚未点击按钮"
用于保存点击次数和提示文字。
4. 声明只读属性
qml
readonly property int maxWidth: 500
用于限制矩形的最大宽度。
5. 声明属性别名
qml
property alias targetWidth: targetRectangle.width
让窗口可以通过 targetWidth 直接访问矩形宽度。
6. 监听属性变化
qml
onClickCountChanged: {
console.log("点击次数发生变化:", clickCount)
}
每次 clickCount 改变时自动执行。
7. 使用属性组
qml
font {
pixelSize: 18
bold: true
}
集中设置字体相关属性。
十八、常见错误整理
18.1 重复使用相同 id
错误:
qml
Rectangle {
id: box
}
Button {
id: box
}
正确:
qml
Rectangle {
id: box
}
Button {
id: actionButton
}
18.2 id 使用引号
错误:
qml
id: "mainWindow"
正确:
qml
id: mainWindow
18.3 id 以数字开头
错误:
qml
id: 1button
正确:
qml
id: button1
18.4 在对象声明中使用等号
错误:
qml
width = 200
正确:
qml
width: 200
在 JavaScript 事件中才使用等号:
qml
onClicked: {
width = 200
}
18.5 属性类型不匹配
错误:
qml
property int count: "hello"
正确:
qml
property int count: 10
18.6 修改只读属性
错误:
qml
readonly property int maxCount: 100
onClicked: {
maxCount = 200
}
只读属性只能读取,不能直接赋值。
18.7 混淆 alias 与普通属性绑定
普通绑定:
qml
property int targetWidth: rectangle.width
别名:
qml
property alias targetWidth: rectangle.width
普通绑定创建了一个新的属性,而别名是对原属性的直接引用。
十九、核心知识对比表
| 知识点 | 语法示例 | 核心作用 |
|---|---|---|
| 模块导入 | import QtQuick |
使用模块中的 QML 类型 |
| 目录导入 | import "components" |
使用目录中的自定义组件 |
| 导入别名 | import "components" as UI |
使用限定名称访问组件 |
| 对象 ID | id: mainWindow |
唯一标识当前作用域中的对象 |
| 自定义属性 | property int count: 0 |
保存组件状态 |
| 必填属性 | required property string title |
要求使用者提供属性 |
| 只读属性 | readonly property int max: 100 |
防止外部直接修改 |
| 属性别名 | property alias text: label.text |
暴露内部对象属性 |
| 属性绑定 | width: parent.width / 2 |
根据依赖项自动更新 |
| 属性变化处理 | onWidthChanged |
响应属性变化 |
| 对象列表 | property list<Item> items |
保存多个 QML 对象 |
| 属性组 | font { pixelSize: 20 } |
集中设置相关子属性 |
二十、快速复习口诀
可以使用下面的方式快速记忆:
text
import:告诉 QML 引擎去哪里找资源。
id:对象在当前作用域中的唯一标识。
property:保存和描述对象状态。
binding:依赖项变化,属性自动更新。
alias:不创建新值,直接引用原属性。
readonly:允许读取,不允许直接修改。
list:一个属性中保存多个对象。
onXxxChanged:属性变化时自动执行。
二十一、复习问题
学完本节后,可以尝试回答以下问题:
- QML 的
import与 C++ 的#include有什么区别? - 模块导入和目录导入有什么区别?
as关键字有什么作用?- 同一个 QML 作用域中能否出现两个相同的
id? id是普通对象属性吗?id能否在运行时修改?- 自定义属性的基本声明格式是什么?
- 冒号赋值和等号赋值分别在什么场景使用?
- 什么是属性绑定?
- 直接给绑定属性赋值会发生什么?
onCountChanged是如何产生的?property list如何获取列表长度?- 点语法和属性组语法有什么区别?
property alias和普通属性绑定有什么区别?readonly property能否绑定到表达式?
二十二、本节总结
本节最重要的内容可以概括为三部分。
Import
text
负责导入模块、目录和资源,让 QML 引擎能够找到所需类型。
ID
text
负责唯一标识当前作用域中的对象,方便其他对象引用和操作。
Property
text
负责保存对象状态、建立动态绑定,并对外暴露组件接口。
将三者组合起来,可以得到 QML 组件开发的基本结构:
qml
import QtQuick
Rectangle {
id: root
property int customWidth: 200
readonly property int maxWidth: 500
property alias labelText: label.text
width: customWidth
height: 100
Text {
id: label
anchors.centerIn: parent
}
}
一句话总结:
import决定可以使用什么,id决定如何找到对象,property决定对象保存什么以及如何联动。