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
。例如
pragma ListPropertyAssignBehavior: ReplaceIfNotDefault
注意:相同的声明也可以为C++定义的类型提供,通过在类声明中添加QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_APPEND、 QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE 和 QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE_IF_NOT_DEFAULT 宏来实现。
ComponentBehavior
您可以在同一个QML文件中定义多个组件。QML文件的根范围是一个组件,您还可以具有类型QQmlComponent的元素,这些元素可以是显式或隐式创建的属性,或者内联组件。这些组件是嵌套的。每个内部组件都位于一个特定的外部组件中。大多数情况下,外部组件中定义的ID对所有其嵌套内部组件都是可访问的。但是,您也可以从不同的组件中创建一个元素,该元素具有不同的ID可用。这样做将破坏外部ID可用的假设。因此,引擎和QML工具无法事先知道这些ID在运行时会解析为什么类型。
使用ComponentBehaviorpragma可限制文件中定义的内部组件只能在其原始上下文中创建对象。如果一个组件与其上下文绑定,则在同一文件中的该组件内可以安全地使用外部组件的ID。QML工具将假定外部ID及其特定类型可用。
为了将组件绑定到其上下文,请指定Bound
参数
pragma ComponentBehavior: Bound
这意味着,如果在绑定组件的情况下发生名称冲突,则在组件外部定义的ID将覆盖由组件创建的对象的本地属性。如果不这样做,那么使用ID实际上并不安全,因为模块的后续版本可能会向组件添加更多属性。如果组件未绑定,本地属性将覆盖组件外部定义的ID,但不能覆盖组件内部定义的ID。
下面示例中打印的是具有id color的Listview对象的r属性,而不是矩形颜色的r属性。
pragma ComponentBehavior: Bound import QtQuick ListView { id: color property int r: 12 model: 1 delegate: Rectangle { Component.onCompleted: console.log(color.r) } }
ComponentBehavior
的默认值是Unbound
。您还可以明确指定它。在Qt的 future版本中,默认值将更改为Bound
。
绑定到其上下文的委托组件在实例化时不会接收自己的私有上下文。这意味着在这种情况下,模型数据只能通过required properties
传递。通过上下文属性传递模型数据将不起作用。这涉及到例如Instantiator
、Repeater
、ListView
、TableView
、GridView
、TreeView
等内部使用DelegateModel
的任何东西。
例如,以下将不会工作
pragma ComponentBehavior: Bound import QtQuick ListView { delegate: Rectangle { color: model.myColor } }
ListView的delegate
属性是一个组件。因此,在这里,一个Component
隐式地围在Rectangle
周围。该组件绑定到其上下文。它不接收ListView提供的上下文属性model
。要使其工作,您必须以这种方式编写它
pragma ComponentBehavior: Bound import QtQuick ListView { delegate: Rectangle { required property color myColor color: myColor } }
您可以在QML文件中嵌套组件。这个pragma适用于文件中的所有组件,不管嵌套有多深。
FunctionSignatureBehavior
使用此pragma,您可以更改处理函数类型注解的方式。从Qt 6.7开始,在调用函数时强制执行类型注解。在此之前,仅QML脚本编译器强制执行类型注解。解释器和JIT编译器忽略了它们。始终强制执行类型注解是与较早版本相比的行为更改,因为您可以在之前使用不匹配的参数调用函数。
将Ignored
指定为值会使QML引擎和QML脚本编译器忽略任何类型注解,从而恢复6.7之前的解释器和JIT的行为。结果,事前编译到C++的代码更少,而需要解释或JIT编译的代码更多。
将Enforced
指定为值明确声明默认值:类型注解始终强制执行。
NativeMethodBehavior
由于历史原因,用与获取时不同的 this
对象来调用 C++ 方法是错误的。原始对象用作 this
对象。您可以通过设置 pragma NativeMethodBehavior: AcceptThisObject
允许给定的 this
对象使用。指定 RejectThisObject
保持历史行为。
有关此示例,请参阅 C++ 方法与 'this' 对象。
ValueTypeBehavior
使用此说明符,您可以更改处理值类型和序列的方式。
通常,小写名称不能在 JavaScript 代码中用作类型名称。这是一个问题,因为值类型的名称是小写的。您可以将 Addressable
指定为此说明符的值来更改此行为。如果指定 Addressable
,则 JavaScript 值可以显式强制转换为特定、命名、的值类型。这使用 as
运算符完成,就像您使用对象类型一样。此外,您还可以使用 instanceof
运算符检查值类型。
pragma ValueTypeBehavior: Addressable import QtQml QtObject { property var a property real b: (a as rect).x property bool c: a instanceof rect property var rect // inaccessible. "rect" is a type name. }
如果不匹配类型,类型转换将返回 undefined
。 instanceof
仅检查继承,而不是检查所有可能的类型转换。因此,例如,由于 rect
在 C++ 中是 QRectF
,所以 QRect
不是一个 rect
值类型,并且由于继承关系的存在而与之一致。使用 as
您可以强制转换为任何通过强制转换兼容的类型。
由于在上面的示例中 rect
现在是一个类型名称,它将隐藏任何名为 rect
的属性。
显式地将数据类型转换为所需类型可以帮助工具。它可以使 Qt Quick Compiler 生成效率高的代码,否则是无法实现的。您可以使用 qmllint 来查找此类实例。
还有一个 Inaddressable
值,您可以使用该值显式指定默认行为。
值类型和序列通常被视为引用。这意味着,如果您从属性中检索值类型实例到局部值,然后更改局部值,原始属性也将更改。此外,如果您显式写入原始属性,则局部值也会更新。在许多地方,这种行为都相当不直观,您不应依赖它。《ValueTypeBehavior》说明符的 Copy
和 Reference
值是实验性选项,用于更改此行为。您不应使用它们。指定 Copy
将使所有值类型都作为实际副本处理。指定 Reference
显式声明默认行为。
与使用 Copy
相反,您应显式重新加载受影响值类型和序列的引用,每次它们可能受到副作用的影响。副作用可能在您调用函数或命令式设置属性时发生。 qmllint 提供了这方面的指导。例如,在下面的代码中,变量 f
在写入 width
后受到副作用的影响。这是因为当 width
改变时,可能有从派生类型或 Binding
元素中将 font
更新的绑定。
import QtQuick Text { function a() : real { var f = font; width = f.pixelSize; return f.pointSize; } }
为了解决这个问题,您可以在 width
的写入操作上避免保留 f
。
import QtQuick Text { function a() : real { var f = font; width = f.pixelSize; f = font; return f.pointSize; } }
这可以简化为
import QtQuick Text { function a() : real { width = font.pixelSize; return font.pointSize; } }
您可能会认为重新检索font
属性的成本很高,但实际上,每当您从它们读取时,QML引擎都会自动刷新值类型引用。因此,这并不比第一个版本更昂贵,而是一种更清晰地表达相同操作的方法。
翻译者
使用此伪指令可以为文件中的翻译设置上下文。
pragma Translator: myTranslationContext
pragma Translator: "myTranslationContext"
有关使用QML进行国际化的更多信息,请参阅使用qsTr。
导入
一个文档必须导入必要的模块或类型命名空间,才能启用引擎加载文档中引用的QML对象类型。默认情况下,一个文档可以访问在同一目录中通过.qml
文件定义的任何QML对象类型;如果文档需要引用任何其他对象类型,则必须导入包含那些类型的类型命名空间。
与C或C++不同,QML没有修改在呈现给QML引擎之前文档的预处理程序。与C或C++不同,import
语句不会复制并在文档开头添加代码,而是指示QML引擎如何解决文档中发现的类型引用。QML文档中的任何类型引用(如Rectangle
和ListView
),包括在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对象类型定义的文档中进一步讨论。
© 2024 Qt公司有限公司。本文档的相关贡献是相应所有者的版权。所提供的文档是根据自由软件基金会发布的GNU自由文档许可证版本1.3的条款许可的。Qt及其相关标志是芬兰和/或世界各地的Qt公司有限公司的商标。所有其他商标均为其各自所有者的财产。