QML 对象特性

 Qt Quick
时间:

对象特性

每一个QML对象类型都包含一组已定义的特性。每个对象类型的实例在创建时都包含一组特性,这些特性是在该对象类型中定义的,一个QML文档中的对象声明定义了一个新的类型,其中可以包含如下特性:

* id特性
* 属性特性
* 信号特性
* 信号处理器特性
* 方法特性
* 附加属性和附加信号处理器特性
* 枚举特性

可通过Qt帮助文档,QML Object Attributes.

id 特性

每一个对象都可以指定一个唯一的id,这样便可以在其他对象中识别并引用该对象。这个特性是语言本身提供的,不能被QML,对象类型进行重定义或重写。可以在一个对象所在组件中的任何位置使用该对象的id来引用这个对象。因此,id值在一个组件的作用域中必须是唯一的。

import QtQuick 2.9

Row {
    Text {
        id: text1
        text: "Hello World"
    }

    Text { text: text1.text }
}

注意: 一旦对象被创建,它的id值就无法改变。尽管id看上去非常像字符串,但它不是字符串,而是由语言提供的一种数据类型。
注意: id值必须使用小写字母或者下划线开头,并且不能使用字母,数字和下划线以外的字符。

属性特性

属性是对象的一个特性,可以分配一个静态的值,也可以绑定一个动态表达式。属性的值可以被其他对象读取。一般而言,属性的值也可以被其他对象那个修改,除非显示声明不允许这么做(即声明为只读属性).

1 . 定义属性特性

QML文档中使用下面的语法自定义一个属性:

[default] property <propertyType> <propertyName> : <value>

第一个default修饰符是可选的,如果添加了default 修饰符,说明正在定义的是一个默认属性。

import QtQuick 2.9

Rectangle {
    property color previousColor
    property color nextColor
    onNextColorChanged: console.log("The next color will be: " + nextColor.toString())
    nextColor: "red"
    MouseArea {
        anchors.fill: parent
        onClicked: nextColor = "yellow"
    }
}

例子解析: 从Rectangle基类型派生了一个新类型,它包含两个新属性:previousColor和nextColor。我们希望在nextColor属性发生改变时得到通知。所以增加了一个信号处理onNextColorChanged,以此达到这一目的。

除enumeration外,QML的基本类型都可以用作自定义属性的类型。

Item {
    property int someNumber
    property string someString
    property url someUrl
}

var类型,var是一种通用的占位符类型,类似于QVariant,它可以包含任意类型的值,包括列表和对象。

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

2 . 初始化和赋值

QML属性的值可以通过初始化或者赋值操作给出,这两种途径都可以直接给定一个静态数据值或绑定一个表达式。

  • 初始化
<propertyName> : <value>
import QtQuick 2.9

Rectangle {
    color: "yellow

property color nextColor: "blue"
}
  • 代码中赋值
[ <objectId>. ]<propertyName> = value
impoort QtQuick 2.9

Rectangle {
    id: rect
    Component.onCompleted: {
        rect.color = "red"
    }
}
  • 有效的属性值

一个属性可以分配两种类型的值:静态值和绑定表达式的值。

静态值:与属性要求的类型匹配(或者可以转换成属性要求的类型)的值。
绑定表达式:该JavaScript表达式可以运算出结果,并且运算结果的类型与属性要求的类型匹配(或者可以转换成属性要求的类型)。当绑定表达式使用的变量值发生变化时,表达式可以自动更新运算结果。

import QtQuick 2.9

Rectangle {
    // 使用静态初始化
    width: 400
    height: 200
    color: "red"
    Rectangle {
        // 使用绑定表达式初始化
        width: parent.width / 2
        height: parent.height
    }
}
  • 对象列表属性

可以将一个QML对象类型值列表赋值给一个list类型的属性。

[ <item1>, <item2>, ... ]

Item类型有一个states属性,用于保存一个State类型对象的列表。

Item {
    states: [
        State { name: "loading" },
        State { name: "running" },
        State { name: "stopped" }
    ]
}

语法格式:

[ default ] property list << objectType >>  propertyName: <value>
import QtQuick 2.9

Rectangle {
    // 只声明,不初始化
    property list <Rectangle> siblingRects

    // 声明并且初始化
    property list <Rectangle> childRects: [
        Rectangle { color: "red" },
        Rectangle { color: "blue" }
    ]

    MouseArea {
        anchors.fill: parent
        onClicked: {
            // 使用length属性获取列表中对象数量
            for(var i = 0; i < childRects.length; i++)
            {
                console.log("color", i, childRects[i].color)
            }
        }
    }
}

4 . 属性组

QML属性可以按照逻辑关系进行分组。属性可以是一个包含子属性特性的逻辑组,而子属性特性也可以使用点标记或者组标记来赋值。

import QtQuick 2.9

Row {
    Text {
        //点标记
        font.pixeiSize: 12; font.bold: true
        text: "text1"
    }
    Text {
        //组标记
        font { pixelSize: 12; bold: true }
        text: "text2"
    }
}

5 . 属性别名

语法如下:

[default] property alias <name> : <alias reference>
import QtQuick 2.9

Rectangle {
    property alias buttonText: textItem.text
    width: 100; height: 30; color: "yellow"
    Text { id: textItem }
}

使用属性别名时需要注意下面两点:

* 属性别名在整个组件初始化完毕之后才是可用的。
* 属性别名可以与现有的属性同名,但会覆盖现有属性。

6 . 默认属性

对象定义可以有一个默认属性。默认属性至多有一个,当定义对象时,如果其子对象没有明确指定它要分配到的属性名,那么这个子对象就被赋值给默认属性。

// MyLabel.qml
import QtQuick 2.9

Text {
    default property var someText
    text: "Hello, " + someText.text
}
import QtQuick 2.9

Rectangle {
    width: 360; height: 360

    MyLabel {
        anchors.centerIn: parent
        Text { text: "world!" }
    }
}

注意:所有基于Item的类型都有一个默认属性data : list<Object>, 该属性允许将可视化对象和资源自由添加到Item对象中。如果添加的时可视化对象,那么将作为children;如果添加的是其他对象类型,那么将作为resource。

Item {
    Text {}
    Rectangle {}
    Timer {}
}

相当于:

Item {
    children: [
        Text {},
        Rectangle {}
    ]
    resources: [
        Timer {}
    ]
}

7 . 只读属性

通过指定readonly关键字,就可以定义一个只读属性。

readonly property <propertyType> <propertyName> <initialValue>

只读属性必须给出初始值,否则这个属性是没有意义的。一旦只读属性初始化完毕,属性就不允许再更改。

Item {
    readonly property int someNumber: 10
    Component.onCompleted: someNumber = 20 // 错误
}

注意: 只读属性不允许是默认属性,也不允许有别名。

信号和信号处理器特性

1 . 定义信号特性

信号可以在C++中使用Q_SIGNAL宏定义,也可以在QML文档中直接定义。

signal <signalName> [([ <type> <parameterName> [, ...] ])]

同一作用域中不能有两个同名的信号或方法。但是,新的信号可以重用已有信号的名字。这意味着,原来的信号会被新的信号屏蔽,变得不可访问。

import QtQuick 2.9
Item {
    signal clicked
    signal hovered()
    signal actionPerformed(string action, var actionResult
    )
}

如果信号没有参数,小括号()可以省略,就像代码中clicked信号那样。如果有参数,参数类型必须声明,比如上面代码中actionPerformed信号的两个参数分别是string和var类型的。

2 . 信号处理器

信号处理器是一类特殊的方法特性。当对应的信号发射时,信号处理器会被QML引擎自动调用。在QML的对象定义中添加一个信号,则自动在对象定义中添加一个相应的对象处理器,只不过其中没有具体的实现代码。

// SquareButton.qml
import QtQuick 2.9
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)
        onReleased: root.deactivated()
    }
}
// myapplication.qml
import QtQuick 2.9

SquareButton {
    onActivated: console.log("Activated at " + xPosition + "," + yPosition)
    onDeactivated: console.log("Deactivated!")
}
  1. 使用Connections类型

QtQuick模块提供了Connections类型,用于连接外部对象的信号。Connections对象可以接收指定目标(target)的任意信号。

import QtQuick 2.9

Rectangle {
    id: rect; width: 100; height: 100

    MouseArea {
        id: mouseArea
        anchors.fill: parent
    }

    Connections {
        target: mouseArea
        onClicked: rect.color = "red"
    }
}

QML的信号对象提供了connect()函数,支持将信号与一个方法或者另外的信号连接起来,这与Qt/C++是类似的。

impoort QtQuick 2.9

Rectangle {
    id: relay

    signal messageReceived(string person, string notice)

    Component.onCompleted: {
        relay.messageReceived.connect(sendToPost)
        relay.messageReceived.connect(sendToTelegraph)
        relay.messageReceived.connect(sendToEmail)
        relay.messageReceived("Tom", "Happy Birthday")
    }

    function sendToPost(person, notice) {
        console.log("Sending to post: " + person + ", " + notice)
    }

    function sendToTelegraph(person, notice) {
        console.log("Sending to telegraph: " + person + ", " + notice)
    }

    function sendToEmail(person, notice) {
        console.log(" Sending to email: " + person + ", " + notice)
    }
}

如果需要解除连接,则可以调用信号对象的disconnect()函数。不仅如此,使用connect()函数还可以构成一个信号链。

import QtQuick 2.9

Rectangle {
    id: forwarder
    width: 100; height: 100

    signal send()
    onSend: console.log("Send clicked")

    Component.onCompleted: {
        mousearea.clicked.connect(send)
    }
}

方法特性

在C++中,可以使用Q_INVOKABLE宏或者Q_SLOT宏进行注册的方式定义方法;在QML文档的对象声明里使用下面的语法:

function <functionName> ([parameterName[, ...]]) { <body> }

与C++中的函数类似,QML的方法中如果有参数,可以在方法中通过参数名称来方访问这些参数。

import QtQuick 2.9

Item {
    width: 200; height: 200

    MouseArea {
        anchors.fill: parent
        onClicked: label.moveTo(mouse.x, mouse.y)
    }

    Text {
        id: label; text: "Move me!"

        function moveTo(newX, newY) {
            label.x = newX;
            label.y = newY;
        }
    }
}

附加属性和附加信号处理器

附加属性和附加信号处理器是一种允许对象使用额外的属性或信号处理器的机制,如果没有这种机制,这些属性或信号处理器就无法应用于这个对象。特别是这个机制允许对象访问一些与个别对象相关的属性或者信号,这一点在实际编程时非常有用。

<ArrachingType> . <propertyName>
<ArrachingType> .on <SignalName>

1 . 附加属性

import QtQuick 2.9

ListView {
    width: 240; height: 320; model: 3; focus: true

    delegate: Rectangle {
        width: 240; height: 30
        color: ListView.isCurrentItem ? "red" : "yellow"
    }
}

2 . 附加信号处理器

import QtQuick 2.9

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 + " " + Name}
    }
}

3 . 注意事项

使用附加属性或附加信号处理器的常见错误是在附加对象的子对象中使用它们。因此附加类型的实例只是附加到了特定的对象,并不是对象及其所有子对象。

枚举

在QML中通过enum关键字来声明。

// MyText.qml

Text {
    enum TextType {
        Normal,
        Heading
    }
    property int textType: MyText.TextType.Normal

    font.bold: textType == MyText.TextType.Heading
    font.puxelSize: textType == MyText.TextType.Heading ? 24 : 12
}

0 评论