QML对象属性链接到本节
QML对象类型属性描述
每个QML对象类型都有一个定义的属性集。每个对象类型的实例都是使用为该对象类型定义的属性集创建的。可以指定几种不同类型的属性,以下将对其进行描述。
对象声明中的属性链接到本节
在QML文档中的一个对象声明定义了一个新的类型。它还声明了一个将实例化的对象层次结构,如果有该新定义类型的实例被创建。
QML对象类型属性类型集如下
id属性
属性属性
信号属性
信号处理器属性
方法属性
附加属性和附加信号处理器属性
枚举属性
以下将详细讨论这些属性。
星号 id
属性链接到本节
每个QML对象类型恰好有一个id属性。该属性由语言本身提供,任何QML对象类型都不能重新定义或覆盖。
可以为对象实例的id属性赋值,以便其他对象能够识别和引用该对象。此id
必须以小写字母或下划线开头,并且不能包含除字母、数字和下划线之外的其他字符。
以下是一个TextInput对象和一个Text对象。TextInput对象的id
值被设置为“myTextInput”。Text对象将其text
属性设置为具有与TextInput相同的值,通过引用myTextInput.text
。现在,这两个项目都将显示相同的文本
可以从声明的组件范围内的任何地方通过对象的id
来引用对象。因此,id
值必须在其组件范围内始终保持唯一。有关更多信息,请参阅作用域和命名解析。
一旦创建了一个对象实例,其id属性的值就不能更改。尽管它看起来像是一个普通的属性,但id
属性不是一个普通的property
属性,且对其有特殊的语义;例如,在上述示例中不可能访问myTextInput.id
。
属性属性链接到本节
属性是可以被赋予静态值或绑定到动态表达式的对象属性。属性的值可以被其他对象读取。通常,也可以由另一个对象修改,除非特定的QML类型明确禁止针对该特定属性进行此操作。
定义属性属性#
C++中可以通过注册类的Q_PROPERTY来对一种类型进行定义,然后再将该类型注册到QML类型系统中。或者,可以使用以下语法在QML文档的对象声明中定义对象类型的自定义属性
[default] [required] [readonly] property <propertyType> <propertyName>
这样,对象声明可以向外部对象公开特定值或更轻松地维护某些内部状态。
属性名称必须以小写字母开头,只能包含字母、数字和下划线。JavaScript保留字不是有效的属性名称。"default"(默认),"required"(必需)和"readonly"(只读)关键字是可选的,并且修改所声明属性的语义。关于它们各自含义的更多信息,请参阅以下各节:默认属性、必需属性和只读属性。
声明自定义属性会隐式为该属性创建一个值变化的信号,以及一个名为on
例如,以下对象声明定义了一个继承自Rectangle基类型的新类型。它有两个新属性,其中为一个新属性实现了信号处理器。
自定义属性定义中的有效类型#
除了枚举类型以外的所有QML值类型都可以用作自定义属性类型。例如,以下所有的属性声明都是有效的:
(枚举值是简单的整数,可以用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类型。如果一个QML类型定义在名为ColorfulButton.qml
的文件中(该文件位于随后被客户端导入的目录中),则类型为ColorfulButton
的属性也是有效的。
为属性属性赋值#
对象实例属性值的指定有两种独立的方式
初始化时的值赋值
强制赋值
在任意一种情况下,值可以是静态值或绑定表达式值。
初始化时值赋值#
属性初始化时值赋的语法是
<propertyName> : <value>
如果需要,初始化值赋值可以与对象声明中的属性定义结合。在这种情况下,属性定义的语法变为
[default] property <propertyType> <propertyName> : <value>
以下是一个属性值初始化的例子
命令值赋值#
命令值赋值是指从命令式 JavaScript 代码中将一个属性值(静态值或绑定表达式)赋值给一个属性。命令值赋值的语法就像下面的 JavaScript 赋值运算符
[<objectId>.]<propertyName> = value
以下是一个命令值赋值的例子
静态值和绑定表达式值#
如前所述,可以分配给属性的值有两种类型:静态值和绑定表达式值。后者也被称为属性绑定。
类型
语义
静态值
一个不依赖于其他属性的常量值。
绑定表达式
一个描述属性与其他属性之间关系的 JavaScript 表达式。这个表达式中的变量被称为属性的相关依赖。
QML 引擎强制属性及其依赖之间的关系。当依赖的任何值发生变化时,QML 引擎会自动重新评估绑定表达式并将新的结果赋值给属性。
以下是一个展示了将两种值分配给属性的例子
注意
要强制赋值绑定表达式,必须将绑定表达式包含在一个传递给 binding()
函数中的函数内,然后必须将 Qt.binding() 返回的值赋给属性。相比之下,初始化时 assignments a binding expression 必须使用 Qt(binding()。有关更多信息,请参阅属性绑定。
类型安全#
属性是类型安全的。一个属性只能分配一个匹配属性类型的值。
例如,如果属性是实数,并且尝试将其分配一个字符串,将会得到一个错误
property int volume: "four" // generates an error; the property's object will not be loaded
同样,如果运行时将属性赋值为错误的类型,则不会分配新的值,并且将生成一个错误。
某些属性类型没有自然的价值表示,并针对这些属性类型,QML 引擎将自动执行字符串到类型值的转换。因此,例如,即使 color
类型的属性存储颜色而不是字符串,仍然可以将字符串 "red"
分配给颜色属性,而不会报告错误。
有关默认支持的属性类型的列表,请参阅QML 值类型。此外,任何可用的 QML 对象类型 也可用作属性类型。
特殊属性类型#
对象列表属性属性#
一个列表类型的属性可以分配一个包含 QML 对象类型值的列表。定义对象列表值是逗号分隔的列表,用方括号包围
[ <item 1>, <item 2>, ... ]
例如,项类型有一个包含状态对象列表的状态属性。下面的代码将此属性的值初始化为三个状态对象列表
如果列表中只有一个项目,则方括号可以省略
可以通过以下语法在对象声明中指定列表类型的属性
[default] property list<<ObjectType>> propertyName
而且,与其他属性声明一样,属性初始化可以与属性声明结合使用,如下所示
[default] property list<<ObjectType>> propertyName: <value>
以下是一个列表属性声明的示例
如果您想声明一个属性来存储列表中可能不是 QML 类型值的值,则应声明一个var 属性。
分组属性#
在某些情况下,属性包含一个逻辑上的子属性分组。这些子属性可以分别使用点符号或分组符号进行分配。
例如,文本类型有一个字体分组属性。以下,第一个文本对象使用点符号初始化其 font
值,而第二个使用分组符号
Text { //dot notation font.pixelSize: 12 font.b: true } Text { //group notation font { pixelSize: 12; b: true } }
分组属性类型是具有子属性的类型。如果分组属性类型是对象类型(而不是值类型),则包含它的属性必须是只读的。这是为了防止您替换属于子属性的子对象。
属性别名#
属性别名是存储对另一个属性引用的属性。与分配新、唯一的存储空间的普通属性定义不同,属性别名将新声明的属性(称为别名属性)连接为对现有属性(称为被别名属性)的直接引用。
属性别名声明看起来像一个普通的属性定义,但它需要 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 }
例如,以下是一个带有 buttonText
别名属性的 Button
类型,该别名属性连接到 Text 子对象的 text
对象
以下代码将为子 Text 对象创建一个具有定义文本字符串的 Button
在这里,直接修改buttonText
会修改textItem.text的值;它不会改变其他更新textItem.text的值。如果buttonText
不是一个别名,更改其值实际上并不会改变显示的文本,因为属性绑定不是双向的:如果textItem.text被更改,则buttonText
的值会发生变化,但反之则不然。
属性别名的考虑因素#
一个别名的属性可以和现有的属性同名,从而覆盖现有的属性。例如,以下qml类型有一个与内置Rectangle::color属性同名的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引用的对象的别名,并声明了额外的内联属性,则这些额外的属性无法通过别名访问
由于inner仅是一个Item
,因此不能从该组件外部初始化inner.extraProperty
然而,如果您将inner对象抽取成拥有专用.qml文件的单独组件,则可以实例化该组件,并通过别名访问到所有属性
默认属性#
对象定义可以有一个单一的默认属性。默认属性是在没有将其指定为特定属性的值的情况下,在另一个对象的定义中声明对象时分配值的属性。
使用可选的default
关键字声明属性将其标记为默认属性。例如,假设有一个文件MyLabel.qml,其默认属性为someText
someText
值可以像这样在MyLabel
对象定义中赋予
这和以下有什么相同的效果
然而,由于someText
属性已被标记为默认属性,因此不需要显式地将Text对象分配给此属性。
您会注意到,可以在不明确将这些子对象添加到children属性的情况下,将子对象添加到任何基于Item的类型。这是因为Item的默认属性是其data
属性,并将任何添加到列表中的Item自动添加到其后代列表中。
默认属性可以用于重新分配项的后代。例如
通过将默认属性别名设置为inner.children
,将任何分配给外部项的对象自动重新分配为内部项的后代。
警告
一个元素的默认列表属性的值可以通过隐式或显式设置。在一个元素的定义中,这两种方法不能混合使用,因为这会导致列表中元素的顺序未定义。
必需属性[#
对象声明可以使用 required
关键字定义一个属性。其语法如下
required property <propertyType> <propertyName>
正如其名称所暗示的,必需属性在创建对象的实例时必须设置。违反此规则将导致如果能够静态检测到 QML 应用程序无法启动。在动态实例化的 QML 组件(例如通过 createComponent()
)的情况下,违反此规则会导致警告并返回空值。
可以使用以下方式将现有属性设置为必需:
required <propertyName>
以下示例展示了如何创建一个自定义的 Rectangle 组件,其中颜色属性始终需要指定。
注意
您无法从 QML 中为一个必需属性分配一个初始值,因为这会直接违反必需属性的使用意图。
必需属性在模型-视图-委托代码中扮演着特殊的角色:如果视图的委托具有具有与视图的模型的角色名称匹配的必需属性,则这些属性将使用模型的相应值进行初始化。有关更多信息,请访问 Qt Quick 中的模型和视图页面。
请参阅 createWithInitialProperties
、setInitialProperties
和 QQuickView::setInitialProperties,了解从 C++ 初始化必需属性的方法。
只读属性[#
对象声明可以使用 readonly
关键字定义一个只读属性,其语法如下
readonly property <propertyType> <propertyName> : <value>
只读属性必须在初始化时分配一个静态值或一个绑定表达式。初始化只读属性后,您不能更改其静态值或绑定表达式。
例如,以下 Component.onCompleted
块中的代码是无效的
注意
只读属性也不能是 默认 属性。
属性修饰符对象[#
属性可以与 属性值修饰符对象 关联。在特定属性上声明与属性修饰符类型相关联的实例的语法如下
<PropertyModifierTypeName> on <propertyName> { // attributes of the object instance }
这通常被称为“on”语法。
重要的是要注意,上述语法实际上是一个 对象声明,它将实例化一个作用于现有属性的对象。
某些属性修饰符类型可能只能适用于特定的属性类型,但语言不强制执行这一点。例如, QtQuick
提供的 NumberAnimation
类型只能用于数字类型(例如 int
或 real
)的属性。尝试使用非数字属性使用 NumberAnimation
不会导致错误,但非数字属性将不会被动画化。属性修饰符类型与特定属性类型关联时的行为由其实现定义。
信号属性[#
信号是由对象发出的一种通知,表示某个事件已发生:例如,属性已更改,动画已开始或停止,或者图像已下载。例如,MouseArea 类型有一个点击信号,当用户在鼠标区域内点击时将发出该信号。
对象可以在特定信号发出时通过信号处理器来通知。信号处理器使用on
例如,下面的onClicked信号处理器是在MouseArea对象定义中声明的,当MouseArea被点击时,将调用它以打印控制台消息。
定义信号属性#
可以通过在C++中注册类的Q_SIGNAL并在QML类型系统中注册来实现对类型的信号的定义。或者,可以按照以下语法在QML文档的对象声明中定义一个对象类型的自定义信号:
signal <signalName>[([<parameterName>: <parameterType>[, ...]])]
在同一类型块中尝试声明具有相同名称的两位信号或方法是一个错误。但是,一个新的信号可以重复使用类型中现有信号的名字。(应谨慎进行,因为现有的信号可能被隐藏并且变得不可访问。)
以下是有三个信号声明的示例:
您还可以使用属性样式语法指定信号参数。
为了与方法声明保持一致,您应首选使用冒号进行类型声明。
如果信号没有参数,则“()”括号是可选的。如果使用参数,则必须声明参数类型,就像上面actionPerformed
信号中的string
和int
参数一样。允许的参数类型与在“定义属性属性”下列出的相同。
要发出信号,请将其作为方法调用。当发出信号时,任何相关的信号处理器将被调用,处理器可以使用定义的信号参数名来访问相应的参数。
属性更改信号#
QML类型还提供了内置的属性更改信号,如下文在“属性属性”部分所述,每当属性值更改时都会发出这些信号。有关这些信号为什么有用以及如何使用的更多信息,请参阅下面关于property change signal handlers
的部分。
信号处理器属性#
信号处理器是一种特殊的方法属性,其中方法的实现由QML引擎在关联的信号发出时调用。向QML对象定义中添加信号会自动将相关的信号处理器添加到对象定义中,默认情况下,该处理器具有空实现。客户端可以提供实现来执行程序逻辑。
考虑下面的 SquareButton
类型,其定义在下面的 SquareButton.qml
文件中,具有 activated
和 deactivated
信号
这些信号可以被同一目录下另一个 QML 文件中的任何 SquareButton
对象接收,客户端提供信号处理器的实现
信号处理器不需要声明它们的参数类型,因为信号已经指定了。上面的箭头函数语法不支持类型注解。
有关信号的使用细节,请参阅 信号和处理器事件系统
属性变化信号处理器#
属性变化信号的处理器采用 on<属性>Changed 语法,其中 <属性> 是属性名称,首字母大写。例如,尽管 TextInput 类型的文档没有记录 textChanged
信号,但该信号通过 TextInput 具有文本属性的事实而隐式可用,因此可以编写 onTextChanged
信号处理器,以便在属性更改时调用
方法属性#
对象类型的方徽号是一个可以调用以执行某些处理或触发其他事件的函数。方法可以通过将函数连接到信号来自动调用。有关详细信息,请参阅 信号和处理器事件系统
定义方法属性#
可以通过以下方式在 C++ 中为类型定义方法:通过标记一个类的函数并在用 Q_INVOKABLE 注册后注册到 QML 类型系统,或者注册为类的 Q_SLOT。或者,可以通过以下语法在 QML 文档中对对象声明添加自定义方法
function <functionName>([<parameterName>[: <parameterType>][, ...]]) [: <returnType>] { <body> }
可以将方法添加到 QML 类型中,以定义独立的、可重用的 JavaScript 代码块。这些方法可以从内部或外部对象调用。
与信号不同,方法参数类型不需要声明,默认为 var
类型。但是,您应该声明它们,以帮助 qmlcachegen 生成更高效的代码,并提高可维护性。
在相同的类型块中尝试声明两个同名的方法或信号是错误的。但是,新方法可以可以重用类型的现有方法的名称。(应该谨慎进行此操作,因为现有方法可能会被隐藏并变得不可访问。)
以下是一个带有 calculateHeight()
方法的矩形,当分配 height
值时调用该方法
如果方法有参数,它们可以通过名称在方法内访问。下面,当 MouseArea 被点击时,它将调用 moveTo()
方法,然后可以引用接收到的 newX
和 newY
参数来重新定位文本
附加属性和附加信号处理器#
附加属性 和 附加信号处理器 是使得对象能够注释额外的属性或信号处理器的机制,这些属性或信号处理器在其他情况下对于对象是不可用的。特别地,它们允许对象访问与特定对象相关的特定属性或信号。
QML类型实现可以选择在C++中采用具有特定属性和信号的附加类型:ref:`<Defining-QML-Types-from-C–>`。这种类型的实例可以在运行时创建并附加到特定的对象上,使得这些对象能够访问附加类型的属性和信号。这些属性和信号处理程序通过在前缀中添加附加类型的名称来访问。
附加属性和处理器引用的语法如下:
<AttachingType>.<propertyName> <AttachingType>.on<SignalName>
例如,ListView类型有一个附加属性ListView.isCurrentItem,该属性在ListView中的每个代理对象中都可以使用。这可以由每个单独的代理对象用于确定它是否是视图中的当前选中项
在这种情况下,附加类型的名称是ListView
,相关属性是isCurrentItem
,因此附加属性称为ListView.isCurrentItem
。
附加信号处理器以相同的方式引用。例如,最常见的onCompleted
附加信号处理器用于在组件的创建过程完成后执行一些JavaScript代码。在下面的示例中,一旦ListModel完全创建,其Component.onCompleted
信号处理器将自动调用以填充模型
由于附加类型的名称是Component
,并且该类型具有completed
信号,所以附加信号处理器称为Component.onCompleted
。
关于访问附加属性和信号处理器的说明#
一个常见的错误是假设附加属性和信号处理器可以直接从附加了这些属性的对象的子对象中访问。这并不正确。附加类型的实例只附加到特定的对象,而不是对象及其所有子对象。
例如,以下是先前示例的修改版,其中代理是Item,着色的矩形是该项的子对象
这不起作用,因为ListView.isCurrentItem
仅在根代理对象上附加,而不是其子对象。由于矩形是代理的子对象,而不是代理本身,它不能像ListView.isCurrentItem
一样访问附加属性isCurrentItem
。因此,矩形应该通过根代理来访问isCurrentItem
现在delegateItem.ListView.isCurrentItem
正确地引用了代理的附加属性isCurrentItem
。
枚举属性#
枚举提供了一组固定的命名选项。可以使用enum
关键字在QML中声明枚举
如上所示,枚举类型(例如,TextType
)和值(例如,Normal
)必须以大写字母开头。
通过<Type>.<EnumerationType>.<Value>
或<Type>.<Value>
引用值。
有关QML中枚举的更多信息,请参阅QML值类型 枚举文档。
在 Qt 5.10 中引入了在 QML 中声明枚举的功能。