QtQuick基础教程(五)---QML语法

在QtQuick基础教程(二)—QML基本语法, 我简单介绍了QML语法。本篇再具体说下,因为QML语言中对象都继承于Item类型,所以说清楚Item,QML语法也就基本清楚了。
每个QML的Item对象(即C++的QQuickItem)都有一系列属性。包含以下几类:

  • id
  • property
  • signal
  • signal 处理器
  • method (方法)
  • 附加的 property 和 signal 处理器

id属性

每个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 属性

property属性是最常用的属性。它可被直接赋值,也可动态赋值。这种属性一般是可读写的,但也可设为只读。

定义 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())
}

property 属性可用数据类型

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属性赋值

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 } }

注意

  • alias中是为已定义内容创建一个别名,两个property引用的同一个值。也就是说,改变上面代码中buttonText或者 textItem.txt任何一个,另一个都会跟着变。
  • 使用alias可创建与已有属性同名的属性。所以最好不要这么写

default关键字

这个default关键字着实让我理解了一会。
default关键字有以下特性:

  • 每个对象中只能有一个
  • 使用default可在不写这个property名的情况下引用到这个值
    举例,先定义一个QML模块:
// 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可以有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 属性

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处理器就会被调用(参数类型需一致)。

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处理器

附加属性和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

你可能感兴趣的:(语法,property,Signal,qml,QtQuick)