QML文档结构#

描述QML文档的结构

QML文档是一个自我包含的QML源代码片段,由三部分组成

  • 可选的预处理器指令列表

  • 它的导入语句

  • 一个单个的根对象声明

按照惯例,一个单个的空白行将导入语句与对象层次定义分开。

QML文档始终以UTF-8格式编码。

预处理器指令#

预处理器指令是给QML引擎本身的指令,可以用来指定当前文件中对象的特定特性,或者修改引擎解释代码的方式。以下预处理器指令将在以下部分进行详细解释。

  • Singleton

  • ListPropertyAssignBehavior

  • ComponentBehavior

  • FunctionSignatureBehavior

  • NativeMethodBehavior

  • ValueTypeBehavior

  • Translator

Singleton#

pragma Singleton 声明在QML文档中定义的组件为单例。单例在QML引擎中只创建一次。为了使用在QML中声明的单例,您还必须将其与其模块注册。有关如何使用CMake进行此操作的信息,请参阅 qt_target_qml_sources

ListPropertyAssignBehavior#

使用这个预处理指令,您可以定义如何在QML文档中定义的组件中对列表属性进行赋值处理。默认情况下,向列表属性赋值会将元素追加到列表中。您可以使用值 Append 显式请求此行为。或者,您可以请求使用 Replace 替换列表属性的内容,或当属性不是默认属性时使用 ReplaceIfNotDefault 进行替换。例如

注释

相同的声明也可以对C++定义的类型使用,通过在类声明中添加宏 QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_APPENDQML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE ,以及 QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE_IF_NOT_DEFAULT 到类声明中。

ComponentBehavior#

在同一个QML文件中,您可以定义多个组件。QML文件的根作用域是一个组件,此外,您还可以有类型为QQmlComponent的元素,它们可以显式或隐式地作为属性或内联组件创建。这些组件是嵌套的。每个内部组件都在一个特定的外部组件内。通常,外部组件中定义的ID可以在所有嵌套内部组件中访问。但是,您也可以在任意一个不同的上下文中从组件中创建元素,使用不同的ID。这样做会打破外部ID可以访问的假设。因此,引擎和QML工具无法提前知道这些ID在运行时将解析为哪种类型(如果有的话)。

使用ComponentBehavior代码段,您可以限制文件中定义的所有内部组件仅在它们的原始上下文中创建对象。如果一个组件与其上下文绑定,则可以在组件内安全地使用同一文件中外部组件的ID。然后QML工具将假设外部ID及其特定类型是可用的。

为了将组件绑定到它们的上下文,请指定Bound参数

这意味着,如果发生名称冲突,绑定组件外定义的ID将覆盖从组件创建的本地对象的本地属性。否则,由于模块的后续版本可能会向组件添加更多属性,因此实际上不安全使用ID。如果组件未绑定,则本地属性覆盖组件外定义的ID,但不会覆盖组件内定义的ID。

以下示例将打印ListView对象ID为colorr属性,而不是矩形的颜色属性。

ComponentBehavior的默认值是Unbound。您也可以显式指定它。在Qt的下一个版本中,默认值将更改为Bound

绑定到其上下文的委托组件在实例化时不接收它们自己的私有上下文。这意味着在这种情况下,模型数据只能通过required properties传递。通过上下文属性传递模型数据将不起作用。这对于使用 DelegateModel 的 Delegate(例如Instantiator、Repeater、ListView、TableView、GridView、TreeView)等组件至关重要。

例如,以下将无法工作

ListView的delegate属性是一个组件。因此,在 Rectangle 这里隐式创建了一个 Component。这个组件与其上下文绑定。它不接收ListView提供的上下文属性model。要想使其工作,您必须这样写

您可以在一个QML文件中嵌套组件。此代码段适用于文件中的所有组件,无论嵌套有多深。

FunctionSignatureBehavior#

使用此代码段,您可以更改函数类型注解的处理方式。从Qt 6.7起,调用函数时将强制执行类型注解。在之前,只有QML脚本编译器强制执行类型注解。解释器和JIT编译器忽略了它们。与以前版本相比,始终强制执行类型注解是一个行为变化,因为在以前,您可以调用带有不匹配参数的函数。

Ignored指定为值让QML引擎和QML脚本编译器忽略任何类型注解,从而恢复解释器和JIT在6.7之前的behaviour。因此,需要编译到C++的代码较少,而需要解释或JIT编译的代码则更多。

Enforced指定为值会显式地设置默认选项:始终强制类型注解。

NativeMethodBehavior#

由于历史原因,使用与获取对象不同的this对象调用C++方法已被破坏。原始对象被用作this对象。您可以设置pragma NativeMethodBehavior: AcceptThisObject来允许给定的this对象被使用。指定RejectThisObject将保持历史行为。

这一点的例子可以在< Span class = "xref std std-ref" > C ++方法和“this”对象 < / Span >下找到。

ValueTypeBehavior#

使用这个说明符,您可以改变处理值类型和序列的方式。

在JavaScript代码中,通常小写名称不能是类型名称。这是一个问题,因为值类型名称是小写的。您可以指定该说明符的值为Addressable以更改这一点。如果指定了Addressable,则可以通过as运算符显式地将JavaScript值强制转换为特定的、命名过的值类型。这就像处理对象类型时一样。此外,您还可以使用instanceof运算符检查值类型

如果类型不匹配,强制转换返回undefinedinstanceof只检查继承,而不是所有可能类型强制。因此,例如,QRect不是代码中的rect值类型,因为C ++中的rect是QRectF,因此不通过继承相关。您可以通过as将其转换为通过强制支持的任何类型。

由于上面的例子中rect now是一个类型名称,它将隐藏列出的任何名称为rect的属性。

显式地强制转换为所需的类型有助于工具。它允许Qt Quick编译器生成更有效的代码,否则它无法做到这一点。您可以使用qmllint查找此类发生。

还有另一个Inaddressable值,您可以用它来显式指定默认行为。

值类型和序列通常被视为引用。这意味着,如果您从一个属性中将值类型实例检索到局部值中,然后更改局部值,原始属性也会被更改。此外,如果您明确写出原始属性,局部值也会更新。这种行为在很多地方都不直观,您不应该依赖它。对于 ValueTypeBehavior 约束的 CopyReference 值是实验性的选项,用于更改此行为。您不应该使用它们。指定 Copy 将导致所有值类型被视为实际的副本。明确指定 Reference 表示默认行为。

而不是使用 Copy,您应该在任何可能受到副作用影响的情况下显式重新加载值类型和序列的引用。副作用可能发生在您调用函数或命令式设置属性时。qmllint 提供了有关此方面的指导。例如,在以下代码中,变量 f 在写入 width 后受到影响。这是因为当更改 width 时,可能在派生类型或 Binding 元素中存在绑定来更新 font

为了解决这个问题,您可以在对 width 进行写操作时避免持有 f

这反过来可以简化为

您可能认为重新检索 font 属性是有代价的,但实际上,当您从它们中读取时,QML 引擎会自动刷新值类型引用。因此,这并不会比第一个版本更昂贵,但是一种更清晰的方式来表达相同操作。

翻译者#

使用此约束,您可以为文件中的翻译设置上下文。

有关使用 QML 进行国际化的更多信息,请参阅使用 qsTr。

导入#

文档必须导入必要的模块或类型命名空间,以启用引擎加载文档中引用的 QML 对象类型。默认情况下,文档可以访问通过同一目录中的 .qml 文件定义的所有 QML 对象类型;如果文档需要引用其他任何对象类型,它必须导入那些类型已注册的类型命名空间。

QML没有修改在展示给QML engine之前的预处理器,与C或C++不同。import语句不会复制并添加到文档中的代码,而是向QML引擎指示如何解决在文档中找到的类型引用。QML文档中存在的任何类型引用,例如RectangleListView,包括在JavaScript块属性绑定中创建的类型引用,都是基于进口声明解决的。至少必须存在一个import语句,如import QtQuick 2.0

有关QML进口的详细信息,请参阅QML语法 - 导入语句文档。

根对象声明#

一个QML文档描述了可以实例化的对象层次结构。每个对象定义都有一个特定的结构;它有一个类型,它可以有一个id和对象名称,它可以有属性,它可以有方法,它可以有信号,并且它可以有信号处理器。

一个QML文件必须只包含单个根对象定义。以下是不合法的,将生成错误

// MyQmlFile.qml
import QtQuick 2.0

Rectangle { width: 200; height: 200; color: "red" }
Rectangle { width: 200; height: 200; color: "blue" }    // invalid!

这是因为.qml文件会自动定义一个QML类型,它封装一个单一 QML对象定义。这将在作为QML对象类型定义的文档中进一步讨论。