QML 文档的结构
QML 文档是一个自包含的 QML 源代码片段,由三个部分组成:
- 一组可选的预处理指令
- 其导入语句
- 一个根对象声明
按惯例,导入语句与对象层次结构定义之间用一个空行分隔。
QML 文档始终采用 UTF-8 格式编码。
指令
Pragma 是针对 QML 引擎本身的指令,可用于指定当前文件中对象的某些特性,或修改引擎对代码的解释方式。以下将详细说明这些 Pragma。
| pragma | 值 | 默认值 | 自 |
|---|---|---|---|
| 单例 | 5.2 | ||
| ListPropertyAssignBehavior | 追加 | X | 6.3 |
| 替换 | 6.3 | ||
| 若非默认值则替换 | 6.3 | ||
| 组件行为 | 绑定 | 6.4 | |
| 未绑定 | X | 6.4 | |
| 函数签名行为 | 被忽略 | 6.5 | |
| 强制执行 | X | 6.5 | |
| 本机方法行为 | 接受此对象 | 6.5 | |
| RejectThisObject | X | 6.5 | |
| 值类型行为 | 引用 | X | 6.5 |
| 复制 | 6.5 | ||
| 可寻址 | 6.6 | ||
| 不可寻址 | X | 6.6 | |
| 可断言的 | 6.8 | ||
| 翻译器 | <翻译上下文> | <文件名> | 6.7 |
单例
pragma Singleton 将定义在 QML 文档根节点的组件声明为单例。更多信息请参阅QML 中的单例。
ListPropertyAssignBehavior
通过此 pragma,您可以定义在 QML 文档中定义的组件中,如何处理对列表属性的赋值操作。默认情况下,对列表属性的赋值会将值追加到列表末尾。 您可以使用值 `Append` 显式请求此行为。此外,您还可以使用 `Replace` 请求始终替换列表属性的内容,或者使用 `ReplaceIfNotDefault` 请求在该属性不是默认属性时进行替换。
以文档 Base.qml 中的一个基类为例:
pragma ListPropertyAssignBehavior: ReplaceIfNotDefault
import QtQuick
Item {
objectName: "outer"
default property list<Item> d: [
Item { objectName: "inner" }
]
property list<Item> notDefault: [
Item { objectName: "one" }
]
}此时,若从 Base 派生并修改其列表属性,ListPropertyAssignBehavior 便会生效。在此情况下:
Base {
// The new item is appended to the list even though you're assigning.
// The (default) property "d" now contains "inner" and "inner2".
d: [
Item { objectName: "inner2" }
]
// The list is replaced by the list given here.
// The (non-default) property "notDefault" now contains only "two".
notDefault: [
Item { objectName: "two" }
]
}如果未指定ListPropertyAssignBehavior ,或者指定了Append ,则“two”对象将被追加到notDefault 属性中,从而生成一个同时包含“one”和“two”的列表。
如果指定了Replace ,默认属性“d”的内容也将被替换,最终生成的列表中仅包含“inner2”。
注意: 对于 C++ 定义的类型,也可以通过在类声明中添加QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_APPEND 、QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE 和QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE_IF_NOT_DEFAULT 宏来实现相同的声明 。例如:
class MyType : public QObject
{
Q_OBJECT
QML_ELEMENT
QML_LIST_PROPERTY_ASSIGN_BEHAVIOR_REPLACE
Q_PROPERTY(QQmlListProperty<QObject> a READ a)
[...]
};ComponentBehavior
您可以在同一个 QML 文件中定义多个组件。QML 文件的根作用域是一个组件,此外您还可以拥有类型为QQmlComponent 的元素,这些元素可以是显式或隐式创建的属性,也可以是内联组件。这些组件是嵌套的。每个内部组件都位于一个特定的外部组件之中。 大多数情况下,在外层组件中定义的 ID 可在其所有嵌套的内部组件中访问。但是,您也可以在任何其他上下文中从某个组件创建元素,此时可用的 ID 可能不同。这样做会打破“外层 ID 可用”这一假设。 因此,引擎和 QML 工具通常无法预先知道此类 ID 在运行时会解析为何种类型(如果有的话)。
通过 ComponentBehavior 指令,您可以限制文件中定义的所有内部组件,使其仅在原始上下文中创建对象。如果组件与其上下文绑定,您就可以在该组件内部安全地使用同一文件中外部组件的 ID。此时,QML 工具将假设这些外部 ID 及其特定类型是可用的。
要将组件绑定到其上下文,请指定Bound 参数:
pragma ComponentBehavior: Bound这意味着,在名称冲突的情况下,绑定组件外部定义的 ID 将覆盖由该组件创建的对象的本地属性。 否则,使用这些 ID 实际上并不安全,因为模块的后续版本可能会向该组件添加更多属性。如果组件未被绑定,则局部属性会覆盖组件外部定义的 ID,但不会覆盖组件内部定义的 ID。
下面的示例会打印ListView 对象中ID为color的 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的未来版本中,默认值将更改为Bound 。
绑定到其上下文的委托组件在实例化时不会获得自己的私有上下文。这意味着在此情况下,模型数据只能通过必填属性传递。通过上下文属性传递模型数据将无法正常工作。这涉及委托给例如Instantiator 、Repeater 、ListView 、TableView 、GridView 、TreeView 的组件,以及一般而言任何在内部使用DelegateModel 的组件。
例如,以下代码将无法正常工作:
pragma ComponentBehavior: Bound
import QtQuick
ListView {
delegate: Rectangle {
color: model.myColor
}
}ListView 的delegate 属性是一个组件。因此,此处会在Rectangle 周围隐式创建一个Component 。该组件与其上下文绑定,无法接收由ListView 提供的model 上下文属性。要使其正常工作,必须按以下方式编写:
pragma ComponentBehavior: Bound
import QtQuick
ListView {
delegate: Rectangle {
required property color myColor
color: myColor
}
}您可以在 QML 文件中嵌套组件。该 pragma 指令对文件中的所有组件均有效,无论嵌套深度如何。
FunctionSignatureBehavior
通过此 pragma,您可以更改函数类型注释的处理方式。自 Qt 6.7 起,在调用函数时会强制执行类型注释。此前,仅Qt Qml Compiler会强制执行类型注释,而解释器和 JIT 编译器则会忽略它们。 始终强制执行类型注解是相对于早期版本的行为变更,因为在此之前,你可以调用参数不匹配的函数。
将值设为 `Ignored ` 会使 QML 引擎和QML 脚本编译器忽略所有类型注解,从而恢复解释器和 JIT 编译器在 6.7 版本之前的行为。结果是预先编译为 C++ 的代码减少,需要通过解释或 JIT 编译处理的代码增加。
将Enforced 作为值指定,即明确声明默认行为:始终强制执行类型注释。
NativeMethodBehavior
由于历史原因,使用与获取来源不同的this 对象调用C++方法会导致功能失效。系统会使用原始对象作为this 对象。您可以通过设置pragma NativeMethodBehavior: AcceptThisObject 来允许使用给定的this 对象。指定RejectThisObject 将保留历史行为。
相关示例可参见C++ 方法和“this”对象部分。
ValueTypeBehavior
通过此pragma,您可以更改值类型和序列的处理方式。
通常,小写名称不能作为 JavaScript 代码中的类型名称。这会造成问题,因为值类型的名称都是小写的。 您可以将Addressable 指定为此 pragma 的值来更改此行为。如果指定了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.
}由于上例中的rect 现在是一个类型名,它将遮蔽任何名为rect 的属性。
显式转换为目标类型有助于工具的运行。这可以使 Qt Quick Compiler 生成原本无法生成的高效代码。您可以使用qmllint来查找此类情况。
此外,您还可以使用Inaddressable 值来显式指定默认行为。
ValueTypeBehavior 指令的另一个属性是Assertable ,该属性在 Qt 6.8 中引入。由于 Qt 6.6 和 6.7 中的一个错误,上文中的a as rect 不仅会检查a 是否为rect ,还会在a 属于兼容类型时构建一个rect 。这显然不是类型断言应有的行为。 指定Assertable 可防止此行为,并将值类型的类型断言限制为仅检查类型。如果您打算将值类型与as 一起使用,应始终指定该选项。无论如何,如果值类型的类型断言失败,结果将是undefined 。
instanceof 不存在此问题,因为它仅检查继承关系,而不检查所有可能的类型转换。
注意: 不建议将 as 与int 和double 类型配合使用, 因为根据JavaScript规则,任何计算的结果都是浮点数,即使它恰好与相应的整数值相同。反之,根据QML的类型映射规则,你在JavaScript中声明的任何整数常量都不是double类型。此外,int 和double 是保留字。 您只能通过类型命名空间来访问这些类型。
值类型和序列通常被视为引用。这意味着,如果你将属性中的值类型实例获取到局部变量中,然后修改该局部变量,原始属性也会随之改变。此外,如果你显式地写入原始属性,局部变量也会被更新。 这种行为在许多情况下都相当反直觉,您不应依赖它。ValueTypeBehavior 指令中的Copy 和Reference 选项是用于更改此行为的实验性选项。您不应使用它们。指定Copy 会导致所有值类型都被视为实际副本。指定Reference 则明确指定默认行为。
与其使用 `Copy `,您应在价值类型和序列可能受到副作用影响时,明确重新加载对其的引用。 每当你调用函数或以命令式方式设置属性时,都可能发生副作用。qmllint提供了相关指导。例如,在以下代码中,写入width 之后,变量f 会受到副作用的影响。这是因为在派生类型或Binding 元素中可能存在一个绑定,当width 发生变化时,该绑定会更新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,你可以设置文件中翻译的上下文。
pragma Translator: myTranslationContextpragma Translator: "myTranslationContext"有关 QML 国际化的更多信息,请参阅《在 QML 中编写待翻译源代码》。
导入
文档必须导入必要的模块或类型命名空间,以便引擎能够加载文档中引用的 QML 对象类型。 默认情况下,文档可以访问通过同一目录中的.qml 文件定义的任何 QML 对象类型;如果文档需要引用任何其他对象类型,则必须导入已注册这些类型的类型命名空间。
与 C 或 C++ 不同,QML没有在将文档提交给QML engine 之前对其进行修改的预处理器。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 对象类型定义的文档”中进一步讨论。
另请参阅 “类型注释与断言”。
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.