QML对象属性
每个QML对象类型都有一组定义的属性。使用为该对象类型定义的属性集创建对象类型的每个实例。可以指定几种不同的属性,如下所述。
对象声明中的属性
QML文档中的对象声明(object declaration)定义了一种新类型。它还声明了一个对象层次结构,如果创建该新定义类型的实例,则将实例化该对象层次结构。
QML对象类型属性类型的集合如下:
- ID属性
- 属性属性
- 信号属性
- 信号处理程序属性
- 方法属性
- 附加属性和附加信号处理程序属性
- 枚举属性
这些属性将在下面详细讨论。
ID属性
每个QML对象类型都有一个唯一的id属性。此属性由语言本身提供,并且不能由任何QML对象类型重新定义或覆盖。
可以将值分配给对象实例的id属性,以允许该对象被其他对象标识和引用。它id
必须以小写字母或下划线开头,并且不能包含字母,数字和下划线以外的字符。
import QtQuick 2.0
Column {
width: 200; height: 200
TextInput { id: myTextInput; text: "Hello World" }
Text { text: myTextInput.text }
}
可以在声明对象id
的组件范围内的任何位置引用该对象。因此,id
值在其组成范围内必须始终是唯一的。有关更多信息,请参见作用域和命名分辨率。
创建对象实例后,无法更改其id属性的值。尽管它看起来像是普通属性,但该id
属性不是普通property
属性,并且特殊语义适用于此属性;例如,myTextInput.id
在上面的示例中无法访问。
属性Property
属性是可以分配静态值或绑定到动态表达式的对象的属性。一个属性的值可以被其他对象读取。通常,它也可以由另一个对象修改,除非特定的QML类型明确禁止特定属性使用。
定义Property属性
通过注册类的Q_PROPERTY,然后再向QML类型系统注册,可以在C ++中为类型定义属性。或者,可以使用以下语法在QML文档的对象声明中定义对象类型的自定义属性:
[default] property <propertyType> <propertyName>
这样,对象声明可以将特定的值暴露给外部对象,或者更容易维护一些内部状态。
属性名称必须以小写字母开头,并且只能包含字母,数字和下划线。JavaScript保留字不是有效的属性名称。该default
关键字是可选的,并修改所声明的属性的语义。有关属性修饰符的更多信息,请参见后面的默认属性部分default
。
声明自定义属性会隐式创建该属性的值更改信号,以及一个名为on <PropertyName> Changed的关联信号处理程序,其中<PropertyName>是属性的名称,首字母大写。
例如,以下对象声明定义了一个从Rectangle基本类型派生的新类型。它具有两个新属性,并为这些新属性之一实现了信号处理程序:
Rectangle {
property color previousColor
property color nextColor
onNextColorChanged: console.log("The next color will be: " + nextColor.toString())
}
自定义属性定义中的有效类型
除枚举类型外,任何QML基本类型都可用作自定义属性类型。例如,这些都是有效的属性声明:
Item {
property int someNumber
property string someString
property url someUrl
}
(枚举值只是整数值,可以用int类型来引用。)
QtQuick
模块提供了一些基本类型,因此除非导入模块,否则它们不能用作属性类型。有关更多详细信息,请参见QML基本类型文档。
请注意,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对象类型都可以用作属性类型。例如:
property Item someItem
property Rectangle someRectangle
这也适用于自定义QML类型。如果在名为ColorfulButton.qml
(在随后由客户端导入的目录中)文件中定义了QML类型,则type属性ColorfulButton
也将有效。
将值分配给属性
可以通过两种不同的方式指定对象实例的属性的值:
- 初始化时的值分配
- 必要的价值分配
无论哪种情况,该值都可以是静态值或绑定表达式值。
初始化时的值分配
在初始化时为属性分配值的语法是:
<propertyName> : <value>
如果需要,可以将初始化值分配与对象声明中的属性定义组合。在这种情况下,属性定义的语法变为:
[default] property <propertyType> <propertyName> : <value>
属性值初始化的示例如下:
import QtQuick 2.0
Rectangle {
color: "red"
property color nextColor: "blue" // combined property declaration and initialization
}
命令性价值分配
命令性值分配是将属性值(静态值或绑定表达式)从命令性JavaScript代码分配给属性的地方。强制性值赋值的语法只是JavaScript赋值运算符,如下所示:
[<objectId>.]<propertyName> = value
强制性值分配的示例如下:
import QtQuick 2.0
Rectangle {
id: rect
Component.onCompleted: {
rect.color = "red"
}
}
静态值和绑定表达式值
如前所述,可以为属性分配两种值:静态值和绑定表达式值。后者也称为属性绑定。
类 | 语义学 |
---|---|
静态值 | 一个不依赖于其他属性的常数值。 |
绑定表达 | 一个JavaScript表达式,用于描述属性与其他属性的关系。此表达式中的变量称为属性的依赖项。 |
QML引擎强制执行属性及其依赖项之间的关系。当任何依赖项的值发生变化时,QML引擎都会自动重新计算绑定表达式并将新结果分配给该属性。
|
这是一个示例,显示了两种分配给属性的值:
import QtQuick 2.0
Rectangle {
// both of these are static value assignments on initialization
width: 400
height: 200
Rectangle {
// both of these are binding expression value assignments on initialization
width: parent.width / 2
height: parent.height
}
}
注意:要强制分配绑定表达式,绑定表达式必须包含在传递给Qt.binding()的函数中,然后必须将Qt.binding()返回的值分配给该属性。相反,在初始化时分配绑定表达式时,不得使用Qt.binding()。有关更多信息,请参见属性绑定。
类型安全
属性是类型安全的。只能为属性分配与属性类型匹配的值。
例如,如果一个属性是一个实数,并且如果您尝试为其分配一个字符串,则会出现错误:
property int volume: "four" // generates an error; the property's object will not be loaded
同样,如果在运行时为属性分配了错误类型的值,则不会分配新值,并且会生成错误。
某些属性类型没有自然值表示形式,对于这些属性类型,QML引擎自动执行字符串到类型值的转换。因此,例如,即使color
类型的属性存储颜色而不是字符串,您也可以将字符串分配给"red"
color属性,而不会报告错误。
有关默认支持的属性类型的列表,请参见QML基本类型。另外,任何可用的QML对象类型也可以用作属性类型。
特殊财产类型
对象列表属性属性
甲列表类型属性可被分配QML对象类型值的列表。定义对象列表值的语法是用方括号括起来的逗号分隔列表:
[ <item 1>, <item 2>, ... ]
例如,Item类型具有一个states属性,该属性用于保存State类型对象的列表。下面的代码将该属性的值初始化为三个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" }
}
列表类型属性可以与下面的语法的对象声明中指定:
[default] property list<<objectType>> propertyName
并且,与其他属性声明一样,可以使用以下语法将属性初始化与属性声明结合使用:
[default] property list<<objectType>> propertyName: <value>
列表属性声明的示例如下:
import QtQuick 2.0
Rectangle {
// declaration without initialization
property list<Rectangle> siblingRects
// declaration with initialization
property list<Rectangle> childRects: [
Rectangle { color: "red" },
Rectangle { color: "blue"}
]
}
如果您希望声明一个属性来存储值列表,这些列表不一定是QML对象类型的值,则应该声明一个var属性。
分组属性
在某些情况下,属性包含一组逻辑的子属性属性。可以使用点符号或组符号将这些子属性属性分配给它们。
例如,“ 文本”类型具有字体组属性。下面,第一个Text对象font
使用点表示法初始化其值,而第二个对象使用组表示法:
Text {
//dot notation
font.pixelSize: 12
font.b: true
}
Text {
//group notation
font { pixelSize: 12; b: true }
}
分组属性类型是具有子属性的基本类型。这些基本类型中的一些是由QML语言提供的,而其他一些仅在导入Qt Quick模块时才可以使用。有关更多信息,请参见有关QML基本类型的文档。
属性别名(Property Aliases)
属性别名是保存对另一个属性的引用的属性。与为属性分配新的唯一存储空间的普通属性定义不同,属性别名将新声明的属性(称为别名属性)连接为对现有属性(别名属性)的直接引用。
属性别名声明看起来像普通的属性定义,只是它需要alias
关键字而不是属性类型,并且属性声明的右侧必须是有效的别名引用:
[default] property alias <name>: <alias reference>
与普通属性不同,别名具有以下限制:
- 它只能引用在声明别名的类型范围内的对象或对象的属性。
- 它不能包含任意JavaScript表达式
- 它不能引用在其类型范围之外声明的对象。
- 该别名引用是不可选的,不像普通的财产可选默认值; 首次声明别名时,必须提供别名引用。
- 它不能引用附加属性。
- 它不能引用深度为3或更大的层次结构内的属性。以下代码不起作用:
property alias color: myItem.myRect.border.color
Item {
id: myItem
property Rectangle myRect
}
但是,可以使用最多两层的属性别名。
property alias color: rectangle.border.color
Rectangle {
id: rectangle
}
例如,下面是Button
具有buttonText
别名属性的类型,该类型连接到Text子text
对象的对象:
// Button.qml
import QtQuick 2.0
Rectangle {
property alias buttonText: textItem.text
width: 100; height: 30; color: "yellow"
Text { id: textItem }
}
以下代码将为Button
子Text对象创建一个具有定义的文本字符串的:
Button { buttonText: "Click Me" }
在这里,修改buttonText
直接修改了textItem.text值;它不会更改其他值,然后更新textItem.text。如果buttonText
不是别名,则更改其值实际上根本不会更改显示的文本,因为属性绑定不是双向的:buttonText
如果更改了textItem.text,则值将已更改,但反之则没有。
属性别名的注意事项
仅在组件完全初始化后才能激活别名。当引用未初始化的别名时,将生成错误。同样,对别名属性进行别名也将导致错误。
property alias widgetLabel: label
//will generate an error
//widgetLabel.text: "Initial text"
//will generate an error
//property alias widgetLabelText: widgetLabel.text
Component.onCompleted: widgetLabel.text = "Alias completed Initialization"
但是,当在根对象中导入带有属性别名的QML对象类型时,该属性显示为常规Qt属性,因此可以在别名引用中使用。
别名属性可能与现有属性具有相同的名称,从而有效覆盖现有属性。例如,以下QML类型具有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"
}
}
使用此类型并引用其color
属性的任何对象都将引用别名,而不是普通的Rectangle :: color属性。但是,在内部,矩形可以正确设置其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 // fails
}
但是,如果您使用专用的.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
}
默认属性
对象定义可以具有一个默认属性。默认属性是如果在另一个对象的定义中声明一个对象但未将其声明为特定属性的值的情况下,为其分配值的属性。
使用optional default
关键字声明属性会将其标记为默认属性。例如,假设有一个具有默认属性的文件MyLabel.qml someText
:
// MyLabel.qml
import QtQuick 2.0
Text {
default property var someText
text: "Hello, " + someText.text
}
该someText
值可以在MyLabel
对象定义中分配给它,如下所示:
MyLabel {
Text { text: "world!" }
}
与以下内容完全相同:
MyLabel {
Text { text: "world!" }
}
但是,由于该someText
属性已被标记为默认属性,因此无需将Text对象显式分配给该属性。
您会注意到,可以将子对象添加到任何基于Item的类型,而无需将其显式添加到children属性。这是因为默认的属性项目是其data
财产,并添加到此列表中的任何项目项目会自动添加到其列表中的孩子。
默认属性对于重新分配项目的子项很有用。请参见TabWidget示例,该示例使用默认属性自动将TabWidget的子级重新分配为内部ListView的子级。另请参见扩展QML。
只读属性
对象声明可以使用readonly
关键字通过以下语法定义只读属性:
readonly property <propertyType> <propertyName> : <initialValue>
初始化时必须为只读属性分配一个值。初始化只读属性后,无论是从命令式代码还是其他方式,都不再可以为其赋予值。
例如,Component.onCompleted
以下块中的代码无效:
Item {
readonly property int someNumber: 10
Component.onCompleted: someNumber = 20
// doesn't work, causes an error
}
注意:只读属性也不能是默认属性。
属性修改器对象
属性可以具有与其关联的属性值修改器对象。声明与特定属性关联的属性修饰符类型的实例的语法如下:
<PropertyModifierTypeName> on <propertyName> {
// attributes of the object instance
}
重要的是要注意,以上语法实际上是一个对象声明,它将实例化作用于预先存在的属性的对象。
某些属性修饰符类型可能仅适用于特定的属性类型,但这不是语言所强制执行的。例如,所NumberAnimation
提供的类型QtQuick
将仅对数字类型的属性(例如int
或real
)进行动画处理。尝试使用NumberAnimation
具有非数字属性的不会导致错误,但是不会为非数字属性设置动画。属性修改器类型与特定属性类型相关联时的行为由其实现方式定义。
信号属性
信号是来自某个对象的通知,表明发生了某些事件:例如,属性已更改,动画已开始或停止或下载图像时。在鼠标区域类型,例如,有一个被点击时发射当鼠标区域内的用户点击信号。
每当发出特定信号时,可以通过信号处理程序通知对象。使用on <Signal>语法声明信号处理程序,其中<Signal>是信号名称,首字母大写。必须在发出信号的对象的定义内声明信号处理程序,并且该处理程序应包含在调用信号处理程序时要执行的JavaScript代码块。
例如,下面的onClicked信号处理程序在MouseArea对象定义中声明,并在单击MouseArea时被调用,从而导致控制台消息被打印:
import QtQuick 2.0
Item {
width: 100; height: 100
MouseArea {
anchors.fill: parent
onClicked: {
console.log("Click!")
}
}
}
定义信号属性
通过注册一个类的Q_SIGNAL,然后在QML类型系统中注册,可以为C ++中的类型定义一个信号。或者,可以使用以下语法在QML文档的对象声明中定义对象类型的自定义信号:
signal <signalName>[([<type> <parameter name>[, ...]])]
试图在同一类型块中声明两个具有相同名称的信号或方法是错误的。但是,新信号可能会在类型上重复使用现有信号的名称。(这应该谨慎行事,因为现有信号可能会被隐藏并变得难以访问。)
这是信号声明的三个示例:
import QtQuick 2.0
Item {
signal clicked
signal hovered()
signal actionPerformed(string action, var actionResult)
}
如果信号没有参数,则“()”括号是可选的。如果使用了参数,则必须声明参数类型,如上述信号的string
和var
参数一样actionPerformed
。允许的参数类型与此页面上的“ 定义属性属性”下列出的参数类型相同。
要发出信号,请将其作为方法调用。发出信号时,将调用任何相关的信号处理程序,并且处理程序可以使用定义的信号参数名称来访问相应的参数。
属性更改信号
QML类型还提供了内置的属性更改信号,每当属性值更改时都会发出该信号,如先前在属性属性部分中所述。有关这些信号为何有用以及如何使用它们的更多信息,请参见即将到来的关于属性更改信号处理程序的部分。
信号处理程序属性
信号处理程序是一种特殊的方法属性,只要发出关联的信号,QML引擎就会调用该方法的实现。在QML中向对象定义添加信号将自动向该对象定义添加关联的信号处理程序,该信号处理程序默认情况下为空实现。客户可以提供一种实现,以实现程序逻辑。
考虑以下SquareButton
类型,其定义在SquareButton.qml
文件中提供,如下所示,并带有信号activated
和deactivated
:
// SquareButton.qml
Rectangle {
id: root
signal activated(real xPosition, real yPosition)
signal deactivated
property int side: 100
width: side; height: side
MouseArea {
anchors.fill: parent
onPressed: root.activated(mouse.x, mouse.y)
onReleased: root.deactivated()
}
}
这些信号可由SquareButton
同一目录中另一个QML文件中的任何对象接收,其中信号处理程序的实现由客户端提供:
// myapplication.qml
SquareButton {
onActivated: console.log("Activated at " + xPosition + "," + yPosition)
onDeactivated: console.log("Deactivated!")
}
有关信号使用的更多详细信息,请参见信号和处理程序事件系统。
属性更改信号处理程序
属性更改信号的信号处理程序采用on <Property> Changed的语法形式,其中<Property>是属性的名称,首字母大写。例如,尽管TextInput类型文档没有记录textChanged
信号,但由于TextInput具有text属性,因此该信号是隐式可用的,因此onTextChanged
只要此属性发生更改,就可以编写要调用的信号处理程序:
import QtQuick 2.0
TextInput {
text: "Change this!"
onTextChanged: console.log("Text has changed to:", text)
}
方法属性
对象类型的方法是可以调用以执行某些处理或触发其他事件的功能。可以将一种方法连接到信号,以便在发出信号时自动调用该方法。有关更多详细信息,请参见信号和处理程序事件系统。
定义方法属性
可以通过在C ++中为类型定义一种方法,方法是:标记一个类的函数,然后使用Q_INVOKABLE在QML类型系统中注册该类,或者将其注册为该类的Q_SLOT。或者,可以使用以下语法将自定义方法添加到QML文档中的对象声明中:
function <functionName>([<parameterName>[, ...]]) { <body> }
可以将方法添加到QML类型,以定义独立的可重用的JavaScript代码块。这些方法可以在内部或外部对象中调用。
与信号不同,方法参数类型不必声明,因为它们默认为var
类型。
试图在同一类型块中声明两个具有相同名称的方法或信号是错误的。但是,新方法可以在类型上重用现有方法的名称。(这应该谨慎行事,因为现有方法可能会被隐藏并且变得无法访问。)
下面是一个带有分配值时调用的方法的Rectangle:calculateHeight()``height
import QtQuick 2.0
Rectangle {
id: rect function calculateHeight() {
return rect.width / 2;
}
width: 100
height: calculateHeight()
}
如果该方法具有参数,则可以在方法内按名称访问它们。在下面,当单击MouseArea时,它会调用该moveTo()
方法,然后该方法可以引用接收newX
到的newY
参数和参数来重新放置文本:
import QtQuick 2.0
Item {
width: 200; height: 200
MouseArea {
anchors.fill: parent
onClicked: label.moveTo(mouse.x, mouse.y)
}
Text {
id: label
function moveTo(newX, newY) {
label.x = newX;
label.y = newY;
}
text: "Move me!"
}
}
附加属性和附加信号处理程序
附加属性和附加信号处理程序是使对象能够使用其他属性或信号处理程序进行注释的机制,而附加属性或信号处理程序对于该对象是不可用的。特别是,它们允许对象访问与单个对象特别相关的属性或信号。
QML类型实现可以选择在C ++中创建具有特定属性和信号的附加类型。然后可以创建这种类型的实例,并在运行时将其附加到特定对象,从而允许那些对象访问附加类型的属性和信号。通过为属性和相应的信号处理程序添加前缀附加类型的名称来访问它们。
对附加属性和处理程序的引用采用以下语法形式:
<AttachingType>.<propertyName>
<AttachingType>.on<SignalName>
例如,ListView的类型有一个附加属性ListView.isCurrentItem,可用于在每个委托对象的ListView。每个单独的委托对象都可以使用它来确定它是否是视图中当前选中的项目:
import QtQuick 2.0
ListView {
width: 240; height: 320
model: 3 delegate: Rectangle {
width: 100; height: 30
color: ListView.isCurrentItem ? "red" : "yellow"
}
}
在这种情况下,附加类型的名称为ListView
,相关属性为isCurrentItem
,因此附加属性称为ListView.isCurrentItem
。
附加的信号处理程序以相同的方式引用。例如,组件的创建过程完成后,通常使用Component.onCompleted附加信号处理程序来执行一些JavaScript代码。在下面的示例中,一旦完全创建ListModel,Component.onCompleted
将自动调用其信号处理程序以填充模型:
import QtQuick 2.0
ListView {
width: 240; height: 320
model: ListModel {
id: listModel
Component.onCompleted: {
for (var i = 0; i < 10; i++)
listModel.append({"Name": "Item " + i})
}
}
delegate: Text { text: index }
}
由于附加类型的名称为Component
并且该类型具有完整的信号,因此附加信号处理程序称为Component.onCompleted
。
有关访问附加属性和信号处理程序的注释
一个常见的错误是假定附加属性和信号处理程序可从已附加这些属性的对象的子级直接访问。不是这种情况。附加类型的实例仅附加到特定对象,而不附加到对象及其所有子对象。
例如,下面是包含附件属性的先前示例的修改版本。这次,委托是项目,而彩色矩形是该项目的子项:
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" // WRONG! This won't work.
}
}
}
这不能按预期方式工作,因为ListView.isCurrentItem
它仅附加在根委托对象上,而不附加在其子对象上。由于Rectangle是委托的子代,而不是委托本身,因此它不能以形式访问isCurrentItem
附加属性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
委托的附加属性。
枚举属性(Enumeration Attributes)
枚举提供了一组固定的命名选项。可以使用enum
关键字在QML中声明它们:
// MyText.qml
Text {
enum TextType {
Normal, Heading
}
}
如上所示,枚举类型(例如TextType
)和值(例如Normal
)必须以大写字母开头。
通过<Type>.<EnumerationType>.<Value>
或引用值<Type>.<Value>
。
// MyText.qml
Text {
enum TextType {
Normal,
Heading
}
property int textType: MyText.TextType.Normal
font.bold: textType == MyText.TextType.Heading
font.pixelSize: textType == MyText.TextType.Heading ? 24 : 12
}
有关QML中枚举用法的更多信息,请参见QML基本类型 枚举文档。
Qt 5.10中引入了在QML中声明枚举的功能。
网友评论