Property别名
属性别名是保存对另一个属性的引用的属性。与为属性分配新的唯一存储空间的普通属性定义不同,属性别名将新声明的属性(称为别名属性)连接为对现有属性(别名属性)的直接引用。
属性别名声明看起来像普通的属性定义,只是它需要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别名的注意事项
仅在组件完全初始化后才能激活别名。当引用未初始化的别名时,将生成错误。同样,对别名属性进行别名也将导致错误。
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.qmlMyItem { 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 { someText: Text { text: "world!" }}
但是,由于该someText属性已被标记为默认属性,因此无需将Text对象显式分配给该属性。
您会注意到,可以将子对象添加到任何基于Item的类型,而无需将其显式添加到children属性。这是因为item的默认属性是其data属性,并添加到此列表中的任何item会自动添加到其列表中的子对象。
默认属性对于重新分配item的子项很有用。请参见TabWidget示例,该示例使用默认属性自动将TabWidget的子级重新分配为内部ListView的子级。
只读属性
对象声明可以使用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)}
如果信号没有参数,则"()"括号是可选的。如果使用了参数,则必须声明参数类型,如上述信号actionPerformed的string和var参数。允许的参数类型与此页面上的" 定义property属性"下列出的参数类型相同。
要发出信号,请将其作为方法调用。发出信号时,将调用任何相关的信号处理程序,并且处理程序可以使用定义的信号参数名称来访问相应的参数。
Property变更信号
QML类型还提供了内置的属性更改信号,每当属性值更改时都会发出该信号,如先前在property属性部分中所述。有关这些信号为何有用以及如何使用它们的更多信息,请参见即将到来的关于属性更改信号处理程序的部分。
信号处理程序属性
信号处理程序是一种特殊的方法属性,只要发出关联的信号,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委托的附加属性。
枚举属性
枚举提供了一组固定的命名选项。可以使用enum关键字在QML中声明它们:
// MyText.qmlText { enum TextType { Normal, Heading }}
如上所示,枚举类型(例如TextType)和值(例如Normal)必须以大写字母开头。
通过<Type>.<EnumerationType>.<Value>或引用值<Type>.<Value>。
// MyText.qmlText { 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中声明枚举的功能。