每个 QML 对象类型都有一组已定义的属性:
- id 属性
- Property 属性
- Signal 属性
- Signal Handler 属性
- Method 属性
- 附加的 Property、Signal handler
- Enumeration 属性
2.1 id 属性
每个类型只有一个 id 属性。可以给对象的 id 属性赋值,其他对象就可以引用该对象。一旦创建了对象实例,id 属性值就无法更改。id 必须是字母、数字和下划线,必须以小写字母、下划线开头。id 属性有特殊的语义,例如,下述代码无法访问myTextInput.id
: ```javascript import QtQuick 2.0
Column { width: 200; height: 200
TextInput {
id: myTextInput //TextInput对象的id设为 myTextInput
text: "Hello World"
}
Text {
text: myTextInput.text //Text对象使用myTextInput引用TextInput对象。此时,两个对象显示相同文本。
}
}
更多信息,请参阅 [Scope and Naming Resolution](https://doc.qt.io/qt-6/qtqml-documents-scope.html)。
<a name="DZ41d"></a>
# 2.2 Property 属性
一个 Property 是对象的属性,可以分配静态值或绑定到动态表达式。其他对象可以读写 Property 值。
<a name="i5t9B"></a>
## 2.2.1 定义 Property 属性
C++类使用 [Q_PROPERTY](https://doc.qt.io/qt-6/qobject.html#Q_PROPERTY) 定义并注册一个 Property;QML 使用如下格式定义一个 Property,其中,Property 名必须以小写字母开头且只能包含字母、数字和下划线。JavaScript 保留字不是有效的属性名称。 `_default_`、`_required _`和 `_readonly _`关键字是可选的:
```javascript
[default] [required] [readonly] property <propertyType> <propertyName>
QML 定义一个 Property 的同时会自动创建一个 signal 和名为 on<PropertyName>Changed
的 signal handler,其中 <PropertyName>
是属性的名称,首字母大写。
例如,以下对象声明定义了一个 Rectangle 的派生类,并且实现了 signal handler 程序:
Rectangle {
property color nextColor
onNextColorChanged: console.log("The next color will be: " + nextColor.toString())
}
2.2.2 自定义 Property 中的有效类型
除了 enumeration 类型之外的任何 QML Basic Types 都可以用作自定义属性类型。例如:
Item {
property int someNumber
property string someString
property url someUrl
}
(枚举值只是整数值,可以用 int 类型代替。)
QtQuick 模块提供了一些基本类型,请参阅 QML Basic Types。注意 var 基本类型是一个通用的占位符类型,可以保存任何类型的值:
property var someNumber: 1.5 //整数
property var someString: "abc" //字符串
property var someBool: true //布尔值
property var someList: [1, 2, "three", "four"] //列表
property var someObject: Rectangle { width: 100; height: 100; color: "red" } //对象
此外,任何 QML object type 都可以用作属性类型:
property Item someItem
property Rectangle someRectangle
这也适用于 custom QML types。如果 QML 类型是在一个名为 ColorfulButton.qml
的文件中定义的,那么 ColorfulButton
类型的属性也将是有效的。
2.2.3 为 Property 属性赋值
有两种方式:初始化时的赋值、命令式赋值。
- 初始化时赋值
```javascript
//语法
: //初始化时赋值 [default] property : //定义时赋值
//示例 import QtQuick 2.0 Rectangle { color: “red” property color nextColor: “blue” // combined property declaration and initialization }
2. 命令式赋值
```javascript
//语法
[<objectId>.]<propertyName> = value
//示例
import QtQuick 2.0
Rectangle {
id: rect
Component.onCompleted: {
rect.color = "red"
}
}
2.2.4 静态值和绑定表达式值
如前所述,可以分配给属性的值有两种:静态值和绑定表达式值(称为 property bindings 属性绑定)。
类型 | 语义 |
---|---|
静态值 | 常量值(不依赖其他 Property)。 |
绑定表达式值 | 一个 JavaScript 表达式,依赖其他 Property 值。 |
示例:
import QtQuick 2.0
Rectangle {
width: 400 //静态值
height: 200 //静态值
Rectangle {
width: parent.width / 2 //绑定表达式值
height: parent.height //绑定表达式值
}
}
注意:要强制分配绑定表达式,绑定表达式必须包含在传递给 Qt.binding() 的函数中,然后必须将 Qt.binding() 返回的值分配给属性。相反,在初始化时分配绑定表达式时,不得使用 Qt.binding()。更多信息,请参阅 Property Binding。
2.2.5 类型安全
赋值时必须类型匹配,因此属性是类型安全的。某些属性类型没有自然值表示,QML 引擎会自动执行字符串到类型值的转换。例如,可以将字符串“red”分配给颜色属性,而不会报告错误。有关默认支持的属性类型列表,请参阅 QML Basic Types。此外,任何可用的 QML 对象类型也可以用作属性类型。
2.2.6 指定 Property 类型
列表类型
//语法(逗号分隔)
[ <item 1>, <item 2>, ... ]
//示例(Item 类型有一个 states 属性,用于保存 State 类型对象的列表)
import QtQuick 2.0
Item {
states: [
State { name: "loading" },
State { name: "running" },
State { name: "stopped" }
]
}
//示例(仅有一个项目,方括号可省略)
import QtQuick 2.0
Item {
states: State { name: "running" }
}
指定列表类型的 Property:
//语法
[default] property list<<objectType>> propertyName
[default] property list<<objectType>> propertyName: <value>
//示例
import QtQuick 2.0
Rectangle {
property list<Rectangle> siblingRects //声明列表类型的 Property 属性
property list<Rectangle> childRects: [ //声明列表类型的 Property 属性,并初始化
Rectangle { color: "red" },
Rectangle { color: "blue"}
]
}
如果项目不是 QML 对象类型,应该声明一个 var 属性。
分组类型
可以使用点表示法或组表示法来访问子属性。例如,Text 有一个 font 分组属性:
Text {
//点表示法
font.pixelSize: 12
font.b: true
}
Text {
//组表示法
font { pixelSize: 12; b: true }
}
分组类型的 Property 是具有子 Property 的基本类型。有些是 QML 语言提供的基本类型,有些只能在导入 Qt Quick 模块时才能使用。
2.2.7 属性别名
别名属性(aliasing property)是对另一个属性的引用。声明的右侧必须是有效的别名引用:
[default] property alias <name>: <alias reference>
与普通属性不同,别名具有以下限制:
- 只能引用声明别名的作用域内的对象或对象的属性。
- 不能是 JavaScript 表达式。
- 不能引用所引用的对象之外的对象。
- 首次声明别名时必须提供别名引用。
- 不能引用 attached properties。
- 引用深度小于3,例如: ```javascript property alias color: myItem.myRect.border.color //不起作用 Item { id: myItem property Rectangle myRect }
property alias color: rectangle.border.color //2层可以 Rectangle { id: rectangle }
以下代码是一个 `Button` 类型,有一个 `buttonText` 别名属性,引用 [Text](https://doc.qt.io/qt-6/qml-qtquick-text.html) 子对象的 `text`:
```javascript
// Button.qml
import QtQuick 2.0
Rectangle {
property alias buttonText: textItem.text //buttonText 引用 textItem.text
width: 100; height: 30; color: "yellow"
Text { id: textItem }
}
//创建一个Button,并为Text子对象设置字符串
Button { buttonText: "Click Me" }
修改 buttonText
别名属性等同于修改 textItem.text
。属性绑定不是双向的,如果 buttonText
不是别名属性,那么修改它不会改变 textItem.text
,但反过来会。
注意事项
一个组件完全初始化后才会激活别名,否则会生成错误。同样,不能为别名属性再次进行别名操作:
property alias widgetLabel: label //定义label的别名为widgetLabel
widgetLabel.text: "Initial text" //错误,没有完全初始化
property alias widgetLabelText: widgetLabel.text //错误,不能进行双重别名操作
Component.onCompleted: widgetLabel.text = "Alias completed Initialization"
但是,当在根对象中导入具有属性别名的 QML 对象类型时,该属性显示为常规 Qt 属性,因此可以在别名引用中使用。
别名属性如果与现有属性同名,则覆盖现有属性。例如,color
别名属性与内置的 Rectangle::color
同名:
Rectangle {
id: coloredrectangle
property alias color: bluerectangle.color
color: "red"
Rectangle {
id: bluerectangle
color: "#1234ff"
}
Component.onCompleted: {
console.log (coloredrectangle.color) //prints "#1234ff"
setInternalColor()
console.log (coloredrectangle.color) //prints "#111111"
coloredrectangle.color = "#884646"
console.log (coloredrectangle.color) //prints #884646
}
//internal function that has access to internal properties
function setInternalColor() {
color = "#111111"
}
}
任何使用此类型并引用其颜色属性的对象都将引用别名而不是普通的 Rectangle::color 属性。然而在内部,矩形可以正确设置其颜色属性并引用实际定义的属性而不是别名。
属性别名和类型
属性别名不能有明确的类型规范。属性别名的类型是它所引用的属性或对象的声明类型。因此,如果您为通过 id 引用的对象创建别名,并使用内联声明的额外属性,则无法通过别名访问额外的属性:
// MyItem.qml
Item {
property alias inner: innerItem
Item {
id: innerItem
property int extraProperty
}
}
不能从该组件的外部初始化 inner.extraProperty,因为 inner 只是一个 Item:
// main.qml
MyItem {
inner.extraProperty: 5 // 失败
}
但是,如果您将内部对象提取到具有专用 .qml 文件的单独组件中,则可以改为实例化该组件,并通过别名获得其所有属性:
// MainItem.qml
Item {
// Now you can access inner.extraProperty, as inner is now an ExtraItem
property alias inner: innerItem
ExtraItem {
id: innerItem
}
}
// ExtraItem.qml
Item {
property int extraProperty
}
2.2.8 Default 属性
在对象定义中可以有一个默认属性,自动为其分配一个值。使用 default 关键字声明为默认属性。例如,假设有一个带有默认属性 someText 的文件 MyLabel.qml:
// MyLabel.qml
import QtQuick 2.0
Text {
default property var someText
text: "Hello, " + someText.text
}
可以在 MyLabel 对象定义中分配 someText 值,如下所示:
MyLabel {
Text { text: "world!" }
}
这与以下效果完全相同:
MyLabel {
someText: Text { text: "world!" }
}
但是,由于 someText 属性已被标记为默认属性,因此没有必要将 Text 对象显式分配给该属性。
您会注意到子对象可以添加到任何基于 Item 的类型,而无需将它们显式添加到 children 属性。这是因为 Item 的默认属性是它的 data 属性,并且为 Item 添加到此列表中的任何项都会自动添加到其子项列表中。
默认属性可用于重新分配项目的子项。请参阅 TabWidget Example,它使用默认属性自动将 TabWidget 的子项重新分配为内部 ListView 的子项。 另请参阅 Extending QML。
2.2.9 Required 属性
Required 属性是创建实例时必须设置的属性,使用 required 关键字定义属性:
//语法
required property <propertyType> <propertyName>
required <propertyName>
//示例
Rectangle {
required color
}
注意:不能为 required 属性设置初始值,因为这会直接违背必需属性的预期用途。
Required 属性在 Model-View-Delegate 中起着特殊的作用:如果 delegate 有 required 属性,且其名称和 model 的角色名匹配,那么这些属性将使用 model 的相应值进行初始化。更多信息,请参阅 Models and Views in Qt Quick,QQuickView::setInitialProperties。
2.2.10 Read-Only 属性
使用 readonly 关键字定义只读属性,语法:
//语法
readonly property <propertyType> <propertyName> : <initialValue>
//示例
Item {
readonly property int someNumber: 10
Component.onCompleted: someNumber = 20 //错误,不能修改
}
必须在初始化时为只读属性分配一个值。注意:只读属性不能同时是默认属性。
2.2.11 属性修改对象
属性可以具有与其关联的 property value modifier objects。声明与特定属性关联的属性修饰符类型的实例的语法如下:
<PropertyModifierTypeName> on <propertyName> {
// attributes of the object instance
}
这通常称为“on”语法。
需要注意的是,上述语法实际上是一个 object declaration,它将实例化一个对象,该对象作用于预先存在的属性。
某些属性修饰符类型可能仅适用于特定的属性类型,但这不是语言强制执行的。例如,QtQuick 提供的 NumberAnimation 类型只会对数字类型(例如 int 或 real)属性进行动画处理。尝试使用具有非数字属性的 NumberAnimation 不会导致错误,但不会对非数字属性进行动画处理。属性修饰符类型与特定属性类型关联时的行为由其实现定义。
2.3 Signal属性
对象发生某个事件时会发射信号。对象使用 signal handler 处理信号,槽函数格式是 on<Signal>
,Signal 是信号名,首字母大写。signale handler 必须在发出信号的对象的定义中声明:
import QtQuick 2.0
Item {
width: 100; height: 100
MouseArea {
anchors.fill: parent
onClicked: {
console.log("Click!")
}
}
}
2.3.1 定义 Signal 属性
C++类使用 Q_SIGNAL 注册信号,QML 使用以下语法:
//语法
signal <signalName>[([<type> <parameter name>[, ...]])]
//示例
import QtQuick 2.0
Item {
signal clicked //无参数信号,括号可省略
signal hovered() //无参数信号
signal actionPerformed(string action, var actionResult)
}
2.3.2 属性变化信号
QML 类型还提供了内置的属性更改信号。为什么这些信号有用以及如何使用它们?请参阅下文 property change signal handlers。
2.4 Signal Handler 属性
Signal handler 是一种特殊的 method attribute,发射信号时自动调用。在 QML 中添加信号时会自动添加对应的 Signal Handler,默认什么都不实现。示例:
Rectangle {
id: root
signal activated(real xPosition, real yPosition) //定义activated信号
signal deactivated //定义deactivated信号
property int side: 100
width: side; height: side
MouseArea {
anchors.fill: parent
onReleased: root.deactivated()
onPressed: (mouse)=> root.activated(mouse.x, mouse.y)
}
}
//同目录的QML文件中的SquareButton对象都可以接收这两个信号
// myapplication.qml
SquareButton {
onDeactivated: console.log("Deactivated!")
onActivated: (xPosition, yPosition)=> console.log("Activated at " + xPosition + "," + yPosition)
}
更多信号的使用,请参阅 Signal and Handler Event System。
2.4.1 属性改变 Signal Handler
如果属性值变化,其对应的 signal handler 格式为 ontextChanged
信号,但可以编写一个 onTextChanged
槽函数:
import QtQuick 2.0
TextInput {
text: "Change this!"
onTextChanged: console.log("Text has changed to:", text) //每个属性都会有个隐式的处理函数
}
2.5 Method 属性
一个对象类型的方法是一个函数,可以连接到信号。更多信息,请参阅 Signal and Handler Event System。
2.5.1 定义 Method 属性
C++类使用 Q_INVOKABLE、Q_SLOT 注册函数,QML 使用如下语法:
//语法
function <functionName>([<parameterName>[, ...]]) { <body> }
//示例(无参数)
import QtQuick 2.0
Rectangle {
id: rect
function calculateHeight() {
return rect.width / 2;
}
width: 100
height: calculateHeight()
}
//示例(有参数)
import QtQuick 2.0
Item {
width: 200; height: 200
MouseArea {
anchors.fill: parent
onClicked: (mouse)=> label.moveTo(mouse.x, mouse.y)
}
Text {
id: label
function moveTo(newX, newY) {
label.x = newX;
label.y = newY;
}
text: "Move me!"
}
}
2.6 附加的 Properties、Signal Handlers
Attached properties 和 attached signal handlers 是一种机制,使对象能够使用对象无法使用的额外属性或信号处理程序进行注释。特别是,它们允许对象访问与单个对象特别相关的属性或信号。
QML 类型实现可以选择在 C++ 中创建具有特定属性和信号的附加类型。然后可以在运行时创建此类型的实例并将其附加到特定对象,从而允许这些对象访问附加类型的属性和信号。这些是通过使用附加类型的名称为属性和相应的信号处理程序添加前缀来访问的。
语法如下:
<AttachingType>.<propertyName>
<AttachingType>.on<SignalName>
附加的 Property 示例:
//attaching type 是 ListView,相关属性是 isCurrentItem,因此附加的 property 称为 ListView.isCurrentItem
import QtQuick 2.0
ListView {
width: 240; height: 320
model: 3
delegate: Rectangle {
width: 100; height: 30
color: ListView.isCurrentItem ? "red" : "yellow" //附加的Property
}
}
附加的 Signal handler 示例:
//attaching type 是 Component,相关信号是 completed,因此附加的 signal handler 称为 Component.onCompleted:
import QtQuick 2.0
ListView {
width: 240; height: 320
model: ListModel {
id: listModel
Component.onCompleted: { //附加的 Signal handler
for (var i = 0; i < 10; i++)
listModel.append({"Name": "Item " + i})
}
}
delegate: Text { text: index }
}
2.6.1 注意事项
一个常见错误是:假设附加的 Property、Signal handler 可以从这些属性所附加到的对象的子代直接访问。不是这种情况。附加类型的实例仅附加到特定对象,而不附加到对象及其所有子对象。
例如,委托是一个 Item ,Rectangle 是该项目的子项:
import QtQuick 2.0
ListView {
width: 240; height: 320
model: 3
delegate: Item {
width: 100; height: 30
Rectangle {
width: 100; height: 30
color: ListView.isCurrentItem ? "red" : "yellow" //错误
}
}
}
这不会按预期工作,因为 ListView.isCurrentItem 仅附加到根委托对象,而不是其子对象。矩形应该通过根委托访问 isCurrentItem:
ListView {
//....
delegate: Item {
id: delegateItem
width: 100; height: 30
Rectangle {
width: 100; height: 30
color: delegateItem.ListView.isCurrentItem ? "red" : "yellow" // correct
}
}
}
现在 delegateItem.ListView.isCurrentItem 正确地引用了委托的 isCurrentItem 附加属性。
2.7 Enumeration 属性
在 QML 中使用 enum 关键字,枚举值必须以大写字母开头。通过 <Type>.<EnumerationType>.<Value>
或 <Type>.<Value>
引用值:
// MyText.qml
Text {
enum TextType { //定义Enumeration属性
Normal, //大写字母开头
Heading //大写字母开头
}
property int textType: MyText.TextType.Normal
font.bold: textType == MyText.TextType.Heading
font.pixelSize: textType == MyText.TextType.Heading ? 24 : 12
}
更多枚举用法,请参阅 QML Basic Types enumeration。Qt 5.10 中引入了在 QML 中声明枚举的能力。