Back to Blog home

Qt 5.15中新的QML语言特性

Published on 星期二 六月 02, 2020 by Richard Lin | Comments

本文翻译自:New QML language features in Qt 5.15

原文作者:Fabian Kosmale

校审:Kenny Zhang

随着Qt 6.0即将带来的重大变化,QML已在5.15中加入了一些新的语言特性。继续阅读以了解required properties(必备属性), inline components(内联组件) 和nullish coalescing(空值合并)。

Required Properties(必备属性)

有时,您的组件需要设置一些属性,但没有合适的默认值。例如,您可能关心按钮的易访问性(Accessibility),因此当您创建了一个AccessibleButton控件,它至少应该有一个description属性。

// AccessibleButton.qml
Button {
property string description
Accessible.description: description
}

但是,按钮具有description属性这一事实并不意味着任何人都会设置它。所以您或您的同事可能会在某个时候用以下代码实例化组件:

AccessibleButton {
onClicked: (mouse) => { /* fancy business logic here */ }
}

关于易访问性就讲到这里:现在description属性只是一个空字符串!当然,您可以为属性设置一个默认值,但是用哪个呢?“Button”基本没用。"您不应该听到这个"?好吧,至少QA现在可能会针对它。但是,如果QML引擎知道需要设置此属性,不是更有用吗?

不幸的是,在Qt 5.15之前没有办法强制设置description属性。但从Qt 5.15开始,这就成为可能:

Button {
required property string description
Accessible.description: description
}

现在,如果创建一个AccessibleButton,但没有设置description属性,那么整个应用程序将无法启动。但如果该组件是动态创建的(例如通过Loader加载),则无法做到这一点。这种情况下,将仅出现运行时警告。

我们还计划为qmllint和QtCreator添加更多的工具支持,以便在未设置Required Properties时显示警告。

Required Properties和Delegates

此外,Required Properties在Delegates中扮演着特殊的角色。正如您可能知道的,Delegates可以通过名称以及其他属性,如model和index,直接访问所提供的模型角色。

ListView {
model: root.myModel
delegate: Text {
id: delegate
color: index % 2 ? "gray" : "black"
text: description
}
}

如果您的Delegates不包含Required Properties,则此处不会发生任何更改。但是,如果它们包含至少一个Required Properties,那么这些名称就不能再访问了。相反,您必须将它们显式的指定为Required Properties。

ListView {
model: root.myModel
delegate: Text {
id: delegate
required property int index
required property string description
color: index % 2 ? "gray" : "black"
text: description
}
}

然后QML引擎将相应设置Required Properties。请注意,如您的model是可编辑的,新方法和旧方法之间有一个重要的区别:使用旧方法,您可以这样写代码:

Text {
id: delegate
Component.onCompleted: description = "My fancy new text"
}

model也会相应更新。但如果你这样写代码

Text {
id: delegate
required property string description
Component.onCompleted: delegate.description = "My fancy new text"
}

然后,description的绑定将被破坏(QML引擎将会打印警告),model将不会被更新。我们决定采用这种行为,以确保无论在delegates中或在delegates之外使用,components的行为不会有太大的差异。此外,我们也不鼓励任何人对属性执行命令式赋值(因为这通常会破坏绑定)。

如果您确实想要更新model的值,当然还有一种办法可以实现:将model设置为Required Properties并用以下代码


Component.onCompleted: model.description= "My fancy new text"

我们建议您始终在Delegates中使用Required Properties。这避免了非限定查找,后者对工具来说是个问题,并往往会降低处理速度。

Inline Components(内联组件)

Qt 5.15中的另一个新特性是内联组件。顾名思义,它们允许您在文件中定义一个新组件。基本语法是


component <component name> : BaseType {
// declare properties and bindings here
}

在文件内部,您可以通过名称引用新组件,就像在其自己的文件中定义的一样。让我们以LabeledImage组件为例来说明其工作原理:

// Images.qml
import QtQuick 2.15

Item {
component LabeledImage: Column {
property alias source: image.source
property alias caption: text.text

Image {
id: image
width: 50
height: 50
}
Text {
id: text
font.bold: true
}
}

Row {
LabeledImage {
id: before
source: "before.png"
caption: "Before"
}
LabeledImage {
id: after
source: "after.png"
caption: "After"
}
}
property LabeledImage selectedImage: before
}

您也可以在其它文件中引用该组件。在这种情况下,您需要在其名字之前加上包含其组件的名称:

// LabeledImageBox.qml
import QtQuick 2.15

Rectangle {
property alias caption: image.caption
property alias source: image.source
border.width: 2
border.color: "black"
Images.LabeledImage {
id: image
}
}

您可能会想,既然QML已经有了组件类型,为什么还要使用内联组件呢?查看前面的示例,我们可以看到,内联组件使您可以执行组件无法执行的以下操作:

  • 您可以在没有Loader开销的情况下创建组件实例。
  • 您可以在属性声明中使用组件类型。
  • 您可以在定义组件的文件之外的其他文件中引用该组件。

希望您能和我们一样方便地找到内联组件!

Nullish Coalescing(空值合并)

最后一个新语言特性是由我们的实习生Maximilian Goldstein实现的。虽然QML通常只支持EcmaScript 6,但是Maximilian为一个即将提出的语言特性增加了支持,该特性正在被添加到最新的EcmaScript标准中:空值合并。引用MDN:

空值合并操作符(??)是一个逻辑操作符,当其左侧操作数为空或未定义时,返回其右侧操作数,否则返回其左侧操作数。

有关详细信息,请参阅MDN页面。下面是一个示例,演示如何在QML中使用它来设置JSON中的属性,并在未提供属性的情况下提供合理的默认值。

Item {
property var settings
property int brightness: settings.brightness ?? 100
property color color: settings.color ?? "blue"
Component.onCompleted: settings = JSON.parse(settingsString)
}

注意我们在设置brightness时不能用“||”代替“??”。因为settings.brightness可能已经是0,在这种情况下,我们将获取默认值。

展望

随着Qt 6的到来,QML将迎来更多改变。除了改进QML引擎的内部机制之外,我们还希望利用静态类型来生成更快的代码(包括编译成C++),并改进我们的工具链。此外,虽然我们在最初的6.0版本中侧重那些大主题,但我们也会在那些小改进上花时间:optional chaining或添加对fetch API的支持就是两个来自Qt社区的特性需求示例,我们会在较高的6.x版本基线中考虑 (不是最初的6.0版本)。您希望在QML中看到什么其它的特性吗?请在bug跟踪系统中创建一个提议。未来由Qt写就!

Subscribe to Our Blog

Stay up to date with the latest marketing, sales and service tips and news.