在QtQuick基础教程(二)—QML基本语法, 我简单介绍了QML语法。本篇再具体说下,因为QML语言中对象都继承于Item类型,所以说清楚Item,QML语法也就基本清楚了。
每个QML的Item对象(即C++的QQuickItem)都有一系列属性。包含以下几类:
每个QML对象都有唯一一个id属性,设定(缺省时系统设定)后不能被修改或者覆盖。
id属性可用于识别对象,和被其他对象引用。id的命名必须以小写字母或者下划线开头,名称中只能有字母、数学和下划线。
举个例子,TextInput对象的id被Text对象引用,且通过引用myTextInput.text
,两个对象显示的内容一致。
注意:id只在作用域内可被访问。
注意:id属性不能被显示访问,比如TextInput.id
是错误语句。
import QtQuick 2.0
Column { width: 200; height: 200 TextInput { id: myTextInput; text: "Hello World" }
Text { text: myTextInput.text }
}
property属性是最常用的属性。它可被直接赋值,也可动态赋值。这种属性一般是可读写的,但也可设为只读。
QML定义 property 的语法如下:
[default] property <propertyType> <propertyName>
C++使用宏 Q_PROPERTY
定义QML中访问的 property。
property的命名规则和id属性一致,但不能使用Javascript保留字。
default
可选,后文有讲解。
property属性的一大优点是在定义property属性的同时,也定义了这个property的signal,这个signal通过on<property-name>Changed
来访问,此时的property-name 首字母需大写。如下代码所示:
Rectangle {
property color nextColor
onNextColorChanged: console.log("The next color will be: " + nextColor.toString())
}
QML中非枚举类型外的所有已定义类型都可做为property类型。举例如下:
Item {
property int someNumber
property string someString
property url someUrl
property color myColor
property Rectangle rect
}
此外,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" }
property属性赋值有两种方式:
初始化赋值的方式很常见,上段代码便是。其语法如下:
<propertyName> : <value>
当然也可以在定义对象的property时设定默认初值。语法如下:
[default] property <propertyType> <propertyName> : <value>
例子:
import QtQuick 2.0
Rectangle {
color: "red"
property color nextColor: "blue" // combined property declaration and initialization
}
这个在调用其他对象时使用。语法如下:
[<objectId>.]<propertyName> = value
示例为:
import QtQuick 2.0
Rectangle { id: rect Component.onCompleted: { rect.color = "red" }
}
直接上例子:
import QtQuick 2.0
Rectangle { width: 400 ; height: 200 // 静态值 Rectangle { width: parent.width / 2 ; height: parent.height // 绑定表达式 }
}
注意: 绑定值不是双向的。以上段代码为例,父Rectangle的宽高改变会影响子Rectangle,但反向不会。
QML会做值类型检查,因而赋值一定是安全的。如property int volume: "four"
是一个语法错误。
如果在运行时,QML会提供一个错误信息,赋值会失败。
但为了方便,QML会提供一些类型转换,比如property color = 'red'
。
以list为例,list的定义语法为[ <item 1>, <item 2>, ... ]
。list类型property赋值语法为:
[default] property list<<objectType>> propertyName: <value>
示例为:
import QtQuick 2.0
Rectangle {
// 不初始化
property list<Rectangle> siblingRects
// 初始化
property list<Rectangle> childRects: [
Rectangle { color: "red" },
Rectangle { color: "blue"}
]
}
如果list只有一个成员,那么可省略方括号。
import QtQuick 2.0
Rectangle {
property list<Rectangle> childRects: Rectangle { color: "red" }
}
在QML代码中,可用点赋值,或者按组赋值。不多说,上代码:
Text {
//点赋值
font.pixelSize: 12
font.b: true
}
Text {
//按组赋值
font { pixelSize: 12; b: true }
}
属性别名用于为已作用域定义的属性创建另一个引用,但不能传递Javascript表达式。语法如下:
[default] property alias <name>: <alias reference>
示例如下:
// Button.qml
import QtQuick 2.0
Rectangle {
property alias buttonText: textItem.text width: 100; height: 30; color: "yellow" Text { id: textItem } }
注意:
buttonText
或者 textItem.txt
任何一个,另一个都会跟着变。这个default关键字着实让我理解了一会。
default关键字有以下特性:
// MyLabel.qml
import QtQuick 2.0
Text {
default property var someText
text: "Hello, " + someText.text
}
调用它时,余下两者等价:
MyLabel { Text { text: "world!" } }
MyLabel { someText: Text { text: "world!" } }
因为someText是default的。
其实我们一直在用default功能,写QML代码的时候不是不写property名,直接写Item一类的定义了?这就是因为Item的default属性是它的children,只要写到Item类型的内容直接就添加到当前Item的children中了。
只读属性必须在声明时赋初值,且不能同时是default属性。其语法如下:
readonly property <propertyType> <propertyName> : <initialValue>
示例如下:
Item {
readonly property int someNumber: 10
Component.onCompleted: someNumber = 20 // 错误!!!给只读属性赋值
}
property可以有property修改对象(Modifier Object),其语法如下:
<PropertyModifierTypeName> on <propertyName> {
// attributes of the object instance
}
注意:这条语句建立了一个对象。
示例如下:
import QtQuick 2.0
Rectangle { width: 100; height: 100 color: "red" NumberAnimation on x { to: 50; duration: 1000 }
}
其作用是让Rectangle的x在1秒内移动到50(初始x值默认为0)。
signal是一个状态信息改变(比如property更改、文件下载完成、用户输入等)时发出的通知消息。具体来讲,鼠标被点击时MouseArea对象会获得click信号。
signal需要被signal处理器处理。signal处理器需要用Javascript语言定义在对象内。
例子如下:
import QtQuick 2.0
Item { width: 100; height: 100 MouseArea { anchors.fill: parent onClicked: { console.log("Click!")}
}
}
QML定义信号方式如下:
signal <signalName>[([<type> <parameter name>[, ...]])]
C++使用宏Q_SIGNAL
来定义信号,并可在QML中使用。
同一个对象内的信号和方法不能重名。使用一个类型时可定义同名信号(这个类型之前的signal被隐藏),但不推荐。示例代码:
import QtQuick 2.0
Item {
signal clicked
signal hovered()
signal actionPerformed(string action, var actionResult)
}
当没有参数时,signal名后的”()”可省略。但只要有参数,这些参数必须给出参数类型。
想发射一个信号,只需把这个signal当一个函数调用就可以。与它相联的signal处理器就会被调用(参数类型需一致)。
每当property被改变时,就会发出相应的signal,然后就会有相应的处理器来处理(如果某个signal没有关联处理器,会执行空操作)。示例如下:
// SquareButton.qml
Rectangle {
id: root
signal activated(real xPosition, real yPosition)
signal deactivated
width: 100; height: 100
MouseArea {
anchors.fill: parent
onPressed: root.activated(mouse.x, mouse.y)
onRelased: root.deactivated()
}
}
这段代码中Rectangle被点击后,就会同时发射activated和deactivated两个signal。
这些信号可以被同个目录下的其他的QML模块来接收。例如:
// myapplication.qml
SquareButton {
onActivated: console.log("Activated at " + xPosition + "," + yPosition)
onDeactivated: console.log("Deactivated!")
}
除此之外,QML还支持类似于C++的connect方式
Text { text: applicationData.getCurrentDateTime() Connections { target: applicationData onDataChanged: console.log("The application data changed!") }
}
方法可用来处理操作,也可用来处理信号。
QML的方法定义语法如下:
function <functionName>([<parameterName>[, ...]]) { <body> }
C++的定义的函数用宏Q_INVOKABLE
或者Q_SLOT
声明后,即可在QML中被调用。
方法的内容需用Javascript语言来写。方法可以在类内外调用。
方法命名与signal相似,且也是不能重名,但可覆盖同名方法。示例如下:
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: label.moveTo(mouse.x, mouse.y)
}
Text {
id: label
function moveTo(newX, newY) {
label.x = newX;
label.y = newY;
}
text: "Move me!"
}
}
附加属性和signal处理器是非常有用的一类功能。它们不能被自己访问,但可以被与自己想关的特殊对象访问。语法如下:
<AttachingType>.<propertyName>
<AttachingType>.on<SignalName>
先上附加属性代码:
import QtQuick 2.0
ListView { width: 240; height: 320 model: 3 delegate: Rectangle { width: 100; height: 30 color: ListView.isCurrentItem ? "red" : "yellow" }
}
再上附加signal处理器代码
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 }
}
这段代码中,Item的附加类型是Component,它的signal是completed,所以使用Component.onCompleted。
但附加属性和signal处理器只对特殊对象可用。例如:
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" // 错误!!!代码不工作 }
}
}
这段代码会出错,因为Rectangle不是ListView的delegate。改成以下代码就可以了。
ListView {
//....
delegate: Item { id: delegateItem width: 100; height: 30 Rectangle { width: 100; height: 30 color: delegateItem.ListView.isCurrentItem ? "red" : "yellow" // 正确 } } }
参考:QML Object Attributes