QML 是一种多范式语言,可以根据对象的属性以及响应其他对象的变化来定义对象。QML 的声明式语法将属性和行为的变化直接集成到对象的定义中,而纯命令式代码是通过一系列逐步处理的语句来表达的。当然,QML 中的属性定义也可以写命令式代码。

QML documents 独立于 QML 源码,引擎通过 document 加载 QML 源码。QML 源码用于定义 QML object types,可以在程序中重复使用。请注意,类型名必须以大写字母开头。

1.1 导入语句

在 QML document 文件顶部可以有多个导入。可以导入:

  • 命名空间。其中包含了已注册的类型。
  • 相对目录。其中的 QML document 包含了定义的类型。
  • 一个 JavaScript 文件。导入时必须进行限定。

示例:

  1. import QtQuick 2.0 //import 命名空间 主版本号.次版本号
  2. import QtQuick.LocalStorage 2.0 as Database //import 命名空间 主版本号.次版本号 as 限定符
  3. import "../privateComponents" //import "相对目录"
  4. import "somefile.js" as Script //import "js文件" as 限定符

1.1.1 导入模块(命名空间)

客户端可以导入 QML modules,这是最常见的导入:

  1. import <ModuleIdentifier> [<Version.Number>] [as <Qualifier>]

<ModuleIdentifier>:是用点分 URI 表示法指定的标识符,它唯一标识模块提供的类型命名空间。 <Version.Number>:是 MajorVersion.MinorVersion 形式的一个版本,它指定各种对象类型和 JavaScript 资源的哪些定义可用。可以省略,默认导入模块的最新版本。也可以只省略次要版本,这时导入给定主版本的最新次版本。 <Qualifier>:是一个可选的本地命名空间标识符,如果给定,模块提供的对象类型和 JavaScript 资源将安装到该标识符中。如果省略,模块提供的对象类型和 JavaScript 资源将被安装到全局命名空间中。

示例:

  1. //示例(简单)。以下代码允许使用 QtQuick 模块提供的所有类型
  2. import QtQuick
  3. Rectangle {
  4. width: 200
  5. height: 100
  6. color: "red"
  7. }
  8. //示例(带版本)
  9. import QtQuick 2.10
  10. //示例(带标识符)。可以解决名称冲突。
  11. import QtQuick as Quick
  12. Quick.Rectangle {
  13. width: 200
  14. height: 100
  15. color: "red"
  16. }

1.1.1.1 导入C++模块

使用 QML_ELEMENTQML_NAMED_ELEMENT() 宏声明 C++ 类型,然后构建系统使用 QML_IMPORT_NAME、QML_IMPORT_MAJOR_VERSION 进行注册。以这种方式给出的导入名称和版本构成了一个可以导入以访问类型的模块。这在使用 C++ 定义自己的 QML 对象类型的客户端应用程序中最为常见。

1.1.1.2 导入到限定的本地命名空间

import 语句可以使用 as 关键字来将类型导入到特定的本地命名空间。命名空间不会成为根对象的属性,可以像使用属性、信号和方法那样从外部引用。例如:

  1. //示例(如何使用 as 以及前缀)
  2. import QtQuick as CoreItems
  3. CoreItems.Rectangle {
  4. width: 100; height: 100
  5. CoreItems.Text { text: "Hello, world!" }
  6. //错误!没有指定前缀,Text 类型无法找到!
  7. Text { text: "Hello, world!" }
  8. }
  9. //示例(可以导入不同命名空间)
  10. import QtQuick as CoreItems
  11. import "../textwidgets" as MyModule
  12. CoreItems.Rectangle {
  13. width: 100; height: 100
  14. MyModule.Text { text: "Hello from my custom text item!" }
  15. CoreItems.Text { text: "Hello from Qt Quick!" }
  16. }
  17. //示例(可以将多个模块导入同一个命名空间中)
  18. import QtQuick as Project
  19. import QtMultimedia as Project
  20. Project.Rectangle {
  21. width: 100; height: 50
  22. Project.Audio {
  23. source: "music.wav"
  24. autoPlay: true
  25. }
  26. }

1.1.2 导入目录

可以导入相对目录:

  1. import "<DirectoryPath>" [as <Qualifier>]

可以是本地的,也可以是远程的。关于URL 解析规则,请参阅 Network Transparency。 如果是远程目录,必须包含一个目录导入列表 qmldir 文件,请参阅 directory imports

1.1.3 导入JavaScript资源

必须有限定符且唯一,这与导入模块的限定符不同:

  1. import "<JavaScriptFile>" as <Identifier>

1.1.3.1 来自模块的JavaScript资源

模块还可以提供 Javascript 文件,方法是在该模块的 qmldir 文件中添加标识符。例如:

  1. //qmldir文件
  2. module projects.MyQMLProject.MyFunctions //模块名:projects.MyQMLProject.MyFunctions
  3. SystemFunctions 1.0 SystemFunctions.js //js文件的标识符:SystemFunctions
  4. UserFunctions 1.0 UserFunctions.js //js文件的标识符:UserFunctions

导入模块后,使用js文件的标识符访问该模块中的 JavaScript 资源:

  1. import projects.MyQMLProject.MyFunctions
  2. Item {
  3. Component.onCompleted: { SystemFunctions.cleanUp(); }
  4. }

如果将模块导入到本地命名空间,必须使用限定符作为前缀:

  1. import projects.MyQMLProject.MyFunctions as MyFuncs
  2. Item {
  3. Component.onCompleted: {
  4. MyFuncs.SystemFunctions.cleanUp();
  5. }
  6. }

1.1.3.2 更多信息

有关 JavaScript 资源的更多信息,请参阅 defining JavaScript resources in QML。有关如何导入和使用 JavaScript 资源,请参阅 importing JavaScript resources in QML

1.2 QML 导入路径

导入 identified module 时,QML 引擎会搜索匹配模块的导入路径,QQmlEngine::importPathList() 返回该路径。默认路径包含:

  • 当前文件所在目录
  • QLibraryInfo::QmlImportsPath 指定的位置
  • 由 QML_IMPORT_PATH 环境变量指定的路径
  • 资源中的 qrc:/qt-project.org/imports 路径。

可以通过 QQmlEngine::addImportPath() 或 QML_IMPORT_PATH 环境变量添加其他导入路径。运行 qmlscene 工具时,还可以使用 -I 选项添加导入路径。

您可以在 QML_IMPORT_PATH 环境变量中指定多个导入路径,方法是使用路径分隔符连接它们。在 Windows 上是一个分号 (;),其他平台是一个冒号 (:)。这意味着您不能在 QML_IMPORT_PATH 中指定资源路径或 URL,因为它们本身包含冒号。但是可以调用 QQmlEngine::addImportPath() 来添加资源路径和 URL。

1.3 调试

QML_IMPORT_TRACE 环境变量可用于调试,请参阅 Debugging module imports

1.4 对象声明

在语法上,QML 代码块定义了一个 QML 对象树。使用 object declarations 来定义对象,该 declarations 描述了对象的类型、属性。可以嵌套子对象。例如:

  1. import QtQuick 2.0
  2. //第一种写法:不在一行
  3. Rectangle {
  4. width: 100
  5. height: 100
  6. color: "red"
  7. }
  8. //第二种写法:在一行
  9. Rectangle { width: 100; height: 100; color: "red" }

Rectangle 是 QtQuick 模块提供的一种类型。如果上述对象是 QML document 的一部分,则引擎可以加载它。也就是说,使用 import 语句导入 QtQuick 模块可以使 Rectangle 类型可用。上述代码将创建一个 Rectangle 对象:
image.png

1.2.1 子对象

嵌套定义子对象时,会隐式地声明了一个包含任意数量的子对象的对象树:

  1. import QtQuick 2.0
  2. Rectangle { //根对象:Rectangle
  3. width: 100
  4. height: 100
  5. gradient: Gradient { //子对象:Gradient
  6. GradientStop { position: 0.0; color: "yellow" } //孙对象:GradientStop
  7. GradientStop { position: 1.0; color: "green" }
  8. }
  9. }

注意,这仅是QML 对象树中的父子关系,而不是视觉场景中的父子关系。视觉场景中父子关系的概念由 QtQuick 模块中的 Item 类型提供,它是大多数 QML 类型的基本类型,因为大多数 QML 对象旨在进行视觉渲染。例如,RectangleText 都是基于 Item 的类型,下面将 Text 对象声明为 Rectangle 对象的可视子对象:

  1. import QtQuick 2.0
  2. Rectangle {
  3. width: 200
  4. height: 200
  5. color: "red"
  6. Text {
  7. anchors.centerIn: parent
  8. text: "Hello, QML!"
  9. }
  10. }

Text 对象在上面的代码中引用它的父值时,它引用的是它的可视化父对象,而不是对象树中的父对象。在这种情况下,它们是一回事:在 QML 对象树的上下文以及视觉场景的上下文中,Rectangle 对象都是 Text 对象的父对象。 可以修改 parent 属性以更改可视化父级,但无法更改对象树父级。

注意,Text 对象已被声明,但并未将其分配给 Rectangle 的属性,这与之前将 Gradient 对象分配给矩形的渐变属性的示例不同。这是因为 Itemchildren 属性已设置为类型的 default property 以启用这种更方便的语法。

更多有关 Item 类型的视觉父母的信息,请参阅 visual parent 文档。

1.3 注释

QML 中注释的语法类似于 JavaScript:

  1. Text {
  2. //第一种
  3. text: "Hello world!" //a basic greeting
  4. //第二种
  5. /*
  6. We want this text to stand out from the rest so
  7. we give it a large size and different font.
  8. */
  9. font.family: "Helvetica"
  10. font.pointSize: 24
  11. }