QML 对象类型属性

QML 对象类型属性

QML 的对象类型 (object type) 有各种 attributes,本文将一一介绍

官方文章: http://doc.qt.io/qt-5/qtqml-syntax-objectattributes.html

note: attributes 和 property 两个单词都有属性的意思。在 QML 中,我们将属性理解为 attribute, 而 property 则直接用英文单词表示

QML 对象类型包含的属性 (attributes) 如下:

  • id 属性
  • property 属性
  • signal 信号属性
  • signal handler 槽属性
  • method 方法属性
  • attached properties and attached signal handler 属性
  • enumeration 枚举属性

本文和另外一篇博客互为姊妹篇,阅读时请对照着看。

1. id 属性

每一个 QML 对象类型有且仅有一个 id 属性,用于在 component scope 中找到 id 所表示的对象

格式: 小写字母或者下划线开头,值只能是字母、数字或者下划线

Note:

  • 有在 对象声明的 component scope 内,对象才可以通过它的 id 属性被引用
  • 一旦对象实例创建,id 属性的值不能改变,

2. Property 属性

property 是一种对象属性,既可以赋静态值(static value),可以绑定动态表达式(bound to dynamic expression) 。 除非显式禁止,通常其他对象也可以改变自己的 property 的值。

2.1 定义 Property 属性

在 C++ 中 通过注册 Q_PROPERTY ,可以实现类的自定义属性。

顺便说一句,我之前学习 Qt 的时候,基本没用过 Q_PROPERTY ,看论坛,有个人是这么说的

如果你不用 QML, 不用 QtScript, 不创建 Desginer 插件,不用 QItemDelegate, 不用 ActiveQt, 那么它确实没什么用.

而在 QML 中,定义 property 属性的格式如下:

[default] property <propertyType> <propertyName>

和前面的 id 属性相同, property 的 名字只能小写字母开头,只能包含字母、数字或者下划线。 JavaScript 保留字段 都不能作为 property 属性的名字。 default 关键字可选,它会改变声明 property 的语义。后面会详细说明。

声明一个自定义的 property 会隐式地为这个 property 创建一个 value-change signal 和 一个相关 signal handler ,名字为 on<PropertyName>Changed,这里 <PropertyName> 是 property 的名字,同时首字母大写。 比如下面这个例子:

除了 枚举(enumeration) 类型,所有 QML 基本类型都可以作为 自定义 property 的类型,比如 string, url, int 等。

enumeration 类型可以用 int 或者 var 代替,如下所示

note: var 基本类型是一个 通用 占位符(placeholder) 类型,它可以包含任何类型的值,包括 list 和 对象类型。也是说,什么时候用 var 都是没有错的,但这样做最大的弊端就是编译时的类型安全就没有保障了,可能会运行时报错。

当然 对象类型也都能被用做 property 类型,包括自定义的对象类型,比如:

2.2 Property 属性赋值

实例化对象的 property 的值有两种赋值方式:

  • 初始化赋值 (initialization)
  • 命令式赋值 (imperative value assignment)

初始化赋值格式如下: <propertyName> : <value> (冒号隔开)

初始化赋值和 property 声明可以结合在一起,格式如下:

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

命令式赋值 就是指通过 JavaScript 赋值运算符 (=) 实现赋值,格式如下:

[<objectId>.]<propertyName> = value

静态值绑定表达式 (binding expression)

类型 Semantics
Static Value 静态值,也就是说,赋值操作的右值是固定不变的,比如上面例子中的 "red"
Binding Expression JavaScript 表达式,用于描述 property 和其他 property 的关系。表达式中使用的变量就是这个 property 的依赖(dependencies)。当任何依赖变化时,QML 引擎自动计算绑定表达式,设置 property 新的值。

初始化赋值 中,如果使用了其他变量,一定是绑定表达式,QML 引擎会自动帮我们更新所有依赖被改变变量的值,是不是很贴心? 这一点和 Reactjs 真的好相似啊

但一定要当心,如果使用命令式赋值,即 Javascript 赋值表达式赋值某个属性,则默认是静态赋值,尽管表达式中使用了其他对象的属性变量 !!!!

这个例子中,当我们按下 空格键后,height property 和 width property 的依赖关系就解除了,height 的值将不会随着 width 的值的改变而改变,它会固定不动。

问: 如何实现命令式绑定赋值呢 ?

答: 使用 Qt.binding() ,将赋值表达式放置在 Qt.binding(function {...})

比如上面的例子修改如下:

height = Qt.binding(function() { return width * 3 })

Qt.binding 中还可以使用 this 关键词,但这里就不推荐了

2.3 类型安全

Property 属性是类型安全的(type safe)。 property 只能被赋予和 property 类型匹配的值。

一些 property 类型没有 natural value representation,对于这些 property 类型, QML 引擎自动执行 string-to-typed-value conversion。比如,尽管 color 类型存储颜色,而不是字符串,我们可将字符串 “red” 赋给一个 color property 并且没有错误报告

list 类型的 property 非常常见,但它有一个很大的坑:

A list type property can be assigned a list of QML object-type values. But it cannot contain any basic type values. (To store basic types within a list, use the var type instead.)

也是说, list 类型只能存储 QML 对象类型,不能存储 基础类型

list 类型的 property 通过以下格式声明:

[default] property list<<objectType>> propertyName

这种方式中,list 不能存储任何类型了,只能存储指定的 objectType 类型

2.4 Grouped Properties

我们一直强调,QML 语法只有两种类型: 基础类型 和 对象类型。

property 属性既可以是基础类型,也可以是对象类型。对象类型的 property 很 easy,但基础类型的 property 会有一个很头疼的地方: 如何处理本身自带 properties 属性的基础类型 的 property, font 基础类型就自带一系列的属性,比如 pixelSize, bold

Qt 官方是这么解释 grouped property 的

The grouped property is read-only, and is initialized to a valid value by the parent object at construction. The grouped property’s sub-properties may be modified from QML but the grouped object itself will never change, whereas an object-type property may be assigned a new object value from QML at any time. Thus, the lifetime of a grouped property is controlled strictly by the C++ parent implementation, whereas an object-type property can be freely created and destroyed through QML code.

grouped property 的生命周期和它的 parent 完全绑定在一起,它是在 parent 的构造阶段随着 parent 一起创建的。grouped property 的子属性可以改变,但它自己不能改变,就是说不能在 QML 中赋值给 grouped property 一个新的值,但可以对它的子属性分别赋值。而 object-type property 则可以通过 QML 代码实现动态创建和销毁

对于这类自带 property 的基础类型的 property (QML 中也称为 grouped property),QML 有两种赋值方式:

  • dot notation (点赋值)
  • group notation (组赋值)

比如下面代码中,第一个 Text 对象初始化它的 font grouped property 使用 dot notation,而第二个使用 group notation:

关于 grouped property,它的 sub property 不会有对应的属性改变信号,比如 font 就不会有 onPixelSizeChanged 信号。但 font property 本身是存在这个信号的,即 onFontChanged 。它的每个 sub property 的改变,都会触发这个信号,具体参考 Property Change Behavior for Basic Types

2.5 Property Aliases

Qt 定义 alias property 如下:

Property aliases are properties which hold a reference to another property. Unlike an ordinary property definition, which allocates a new, unique storage space for the property, a property alias connects the newly declared property (called the aliasing property) as a direct reference to an existing property (the aliased property)

格式如下: (添加了 alias 关键词)

[default] property alias <name>: <alias reference>

note: alias property 有下面的限制:

  1. It can only refer to an object, or the property of an object, that is within the scope of the type within which the alias is declared.
  2. It cannot contain arbitrary JavaScript expressions
  3. The alias reference must be provided when the alias is first declared.
  4. It cannot refer to grouped properties

比如,下面的 Button 类型有一个 buttonText 别名 property,连接到 Text 对象:

下面的代码会创建一个 Button 并且子Text对象的文本定义好了:

Button {buttonText: "Click Me"}

alias property 的好处有两点:

  • 双向修改。上面例子如果采用绑定表达式的方式,即 property buttonText: textItem.text ,那么 buttonText 的改变不会 textItem.text
  • 暴露子组件需要修改的 property。因为 QML 语法规定只有 QML Component 中只有 顶层对象 (root object) 的某些属性,包括该顶层对象所有的 properties, signals, methods。我们有了 alias property,就可以将子组件的 property 暴露出来了

2.6 Default Properties

每个对象类型最多只能有一个 default property。

A default property is the property to which a value is assigned if an object is declared within another object’s definition without declaring it as a value for a particular property.

在声明 property 的时候,加入 default 关键词将它标记为 default property。看下面的例子, MyLabel.qml 文件有 default property someText:

在 MyLabel 对象定义的时候, someText 可以默认赋值,像这样:

上面的代码和下面的效果相同:

所有基于 Item 可视化对象类型的 QML 组件,它的 默认 property 是 data property,同时 QML 引擎还会把 任何添加到该 property 的 Item 对象自动添加到 children property。参考: http://doc.qt.io/qt-5/qml-qtquick-item.html#data-prop

实际等价于:

2.7 只读 Properties

使用 read-only 关键词定义 read-only property,格式如下:

readonly property <propertyType> <propertyName> : <initialValue>

read-only 属性必须初始化赋值。初始化之后,命令赋值或其他方式赋值都是不允许的。

Note: read-only property 不能是 default property

2.8 Property Modifier Objects

参考: property modifier types

声明一个特定 property 的 property modifier type 的格式如下:

某些 property modifier type 只能用于特定的 property 类型。

比如, NumberAnimation 类型只支持 numeric-type (比如 int 或者 real) property。 尝试给 NumberAnimation 赋 non-numeric property 不会报错,但也不会有动画效果。

3. Signal & Signal Handler Attributes

signal 就是 传统 Qt 编程中的 信号 (signal),本质是一个函数

和 Qt 的 C++ 编程类似,

  • signal 可以主动发送。发送信号,就和调用方法一样的。
  • 每个 signal 都实现了一个内建的槽函数: on<Signal> , 其中 <Signal>是 sigal 的名字,首字母大写。当某个地方发送 信号后,Qt 会自动执行 on<Signal> 槽函数
  • 我们也可以主动将 signal 和其他函数连接 (connect),这样就可以实现不同 QML 对象之间的信号传递

比如 MouseArea 对象类型有一个 clicked 信号,这个信号有一个内建的槽函数 onClicked

3.1 定义 Signal Attributes

Qt 文档: Signal and Handler Event System

在 C++,定义 signal 是用 注册 Q_SIGNAL 实现的,在 QML 里面,自定义 signal 定义格式如下:

signal <signalName> [([<type> <parameter name>[, ...]])]

请不要让 两个 signal 或者 method 重名,否则旧的信号会被隐藏,并且无法获得。

如果 signal 没有参数,“()” 是可选的。如果使用了参数,参数类型必须声明。

在 signal 发送之后,任何相关的 signal handlers 都会被调用,并且 handler 使用已定义的 signal 参数名来获得相应的参数。

Property Change Signal

前面说过 QML 提供内建的 property change signal,当 property 的值改变时,该信号会被发送。

3.2 定义 Signal handler Attributes

signal handler 就是上面说到的 QML 内建的 signal 的槽函数。当 signal 被触发时,QML 引擎会自动调用该实现的方法。我们添加一个新的信号会自动添加一个相联系的 signal handler,但函数体是空的。

比如下面的 SquareButton.qml 文件,有信号activateddeactivated:

在任何 SquareButton objects 可以实现 signal handlers

Property Change Signal Handlers

每个 property 都有一个内建的 property change signal ,而每个 signal 都有一个内建的 signal handler,于是每个 property 都有一个内建的 property changer signal handler, 函数名为 on<Property>Changed, <Property> 是 property 名称

比如下面这个例子,尽管 TextInput 没有 textChanged 信号,但 TextInput 有一个 text property,因此当这个属性改变的时候,onTextChanged signal handler 会被调用

4. Method Attributes

  • 在 C++ 中,method 属性就是类中定义的函数,函数可以通过 Q_INVOKABLE 或者 Q_SLOT 注册到 QML type system

  • 在 QML 文件中,method 属性就是 JavaScript 语言定义的函数
    function <functionName> ([<parameterName>[, ...]]) { <body> }
    和 signal 属性不同的是, method 属性的参数都不需要指定类型,都是 var 类型。这是因为 JavaScript 中 function 的参数都是没有类型的

5. Attached Properties & Attached Signal Handlers

Attached properties and attached signal handlers are mechanisms that enable objects to be annotated with extra properties or signal handlers that are otherwise unavailable to the object. In particular, they allow objects to access properties or signals that are specifically relevant to the individual object.

这部分是我觉得最难懂的了,大致意思是 我们本来不能接触其他对象的 property 或者 signal handler,通过这种方法就可以接触到了。

举个例子,在 MVC 框架中,每个 Item 或者 model 怎么才能知道它在整个 View 中的状态呢? 它需要有权限接触 View 的一些 property, signal handler

使用格式如下:

<AttachingType>.<propertyName>
<AttachingType>.on<SignalName>

下面的例子中, ListView 有一个 attached property ListView.isCurrentItem,这样 ListViewdelegate property 的每个 object 都可以接触到 ListView.isCurrentItem

还有我们最熟悉的 Component.onCompleted signal handler, 下面的例子中,包裹model 的组件 (component) 完全初始化后,会发送 completed 信号,model 可以接触 Component 的 signal handler – onCompleted

A common error is to assume that attached properties and signal handlers are directly accessible from the children of the object to which these attributes have been attached. This is not the case. The instance of the attaching type is only attached to specific objects, not to the object and all of its children.

上面的代码没有效果,因为 ListView.isCurrentItem 只是 attached 到 root delegate object,而不是它的 children。 因此 Rectangle 不能接触 isCurrentItem。

这里 delegateItem.ListView.isCurrentItem 可以正确指代 delegate 的 attached property – isCurrentItem

具体如何实现一个 attached property 可以参考另外一篇博客

6 Enumeration Attributes

As shown above, enumeration types (e.g. TextType) and values (e.g. Normal) must begin with an uppercase letter.

Values are referred to via <Type>.<EnumerationType>.<Value> or <Type>.<Value>.

发表评论

电子邮件地址不会被公开。 必填项已用*标注

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d 博主赞过: