qt_add_qml_module
定义一个 QML 模块。
该命令于 Qt 6.2 中引入。
该命令定义在Qt6 包的Qml 组件中,可按如下方式加载:
find_package(Qt6 REQUIRED COMPONENTS Qml)语法概述
qt_add_qml_module(
target
URI uri
[VERSION version]
[PAST_MAJOR_VERSIONS ...]
[STATIC | SHARED]
[PLUGIN_TARGET plugin_target]
[OUTPUT_DIRECTORY output_dir]
[RESOURCE_PREFIX resource_prefix]
[CLASS_NAME class_name]
[TYPEINFO typeinfo]
[IMPORTS ...]
[OPTIONAL_IMPORTS ...]
[DEFAULT_IMPORTS ...]
[DEPENDENCIES ...]
[IMPORT_PATH ...]
[SOURCES ...]
[QML_FILES ...]
[RESOURCES ...]
[OUTPUT_TARGETS out_targets_var]
[DESIGNER_SUPPORTED]
[FOLLOW_FOREIGN_VERSIONING]
[NAMESPACE namespace]
[NO_PLUGIN]
[NO_PLUGIN_OPTIONAL]
[NO_CREATE_PLUGIN_TARGET]
[NO_GENERATE_PLUGIN_SOURCE]
[NO_GENERATE_QMLTYPES]
[NO_GENERATE_QMLDIR]
[NO_GENERATE_EXTRA_QMLDIRS]
[NO_GENERATE_QTCONF] # since Qt 6.12
[NO_GENERATE_AOT_VALIDATION] # since Qt 6.12
[NO_LINT]
[NO_CACHEGEN]
[NO_RESOURCE_TARGET_PATH]
[NO_IMPORT_SCAN]
[DISCARD_QML_CONTENTS]
[ENABLE_TYPE_COMPILER]
[TYPE_COMPILER_NAMESPACE namespace]
[QMLTC_EXPORT_DIRECTIVE export_macro]
[QMLTC_EXPORT_FILE_NAME header_defining_export_macro]
)如果禁用了无版本号的命令,请改用qt6_add_qml_module() 。该命令支持与本命令相同的参数集。
有关定义 QML 模块的示例,请参阅《构建 QML 应用程序》和《构建可重用的 QML 模块》。
描述
此命令用于定义一个 QML 模块,该模块可以包含 C++ 源文件、.qml 文件,或两者兼有。它确保提供了模块的基本信息,并保证这些信息的一致性。此外,它还会配置并协调相关事项,例如.qml 源文件的缓存编译、资源嵌入、代码检查以及某些关键模块文件的自动生成。
有关概念概述及常见用法示例,请参阅《使用 CMake 将一切整合在一起》。有关自定义目录布局和版本控制等高级主题,请参阅《编写 QML 模块》。
目标结构
QML 模块可以采用几种不同的结构形式。以下是典型的结构安排:
将后端目标与插件目标分离
这是针对大多数 QML 模块的推荐结构。模块的所有功能都在后端目标中实现,该目标作为第一个命令参数提供。C++ 源文件、.qml 文件和资源都应添加到后端目标中。后端目标是一个库,应安装在项目定义的任何其他库所在的同一位置。
创建后端目标所处的源目录结构应与 QML 模块的目标路径一致(目标路径即模块的 URI,其中点被斜杠替换)。如果源目录结构与目标路径不一致,qt_add_qml_module() 将发出警告。
以下示例展示了 URI 为MyThings.Panels 的 QML 模块的合适源目录结构。对qt_add_qml_module() 的调用将出现在所示的CMakeLists.txt 文件中。
src
+-- MyThings
+-- Panels
+-- CMakeLists.txtQML 模块关联了一个独立的插件目标。当应用程序尚未链接到后端目标时,该插件目标将在运行时用于动态加载该模块。该插件目标也将作为库存在,通常安装在与模块的qmldir文件相同的目录下。
理想情况下,插件目标应仅包含插件类的简单实现。这样便可将该插件在qmldir 文件中标记为可选。其他目标可直接链接到后端目标,运行时便无需加载该插件,从而提升加载性能。 默认情况下,系统会自动生成一个定义了最简插件类的 C++ 源文件,并将其添加到插件目标中。如果 QML 模块需要自定义的插件类实现,则需要使用NO_GENERATE_PLUGIN_SOURCE选项,通常还需配合使用NO_PLUGIN_OPTIONAL选项。
如果未指定 `NO_PLUGIN `,STATIC QML模块也会生成静态QML插件。导入此类STATIC QML模块的目标还需显式链接到相应的QML插件。
注意:使用 静态链接时, 可能需要使用Q_IMPORT_QML_PLUGIN 来确保QML插件被正确链接。
没有后端目标的插件目标
可以定义一个 QML 模块,使其插件目标作为其自身的后端目标。在这种情况下,该模块必须在运行时动态加载,且不能被其他目标直接链接。要实现这种配置,必须使用PLUGIN_TARGET 关键字,并将target 作为插件目标名称重复使用。例如:
qt_add_qml_module(someTarget
PLUGIN_TARGET someTarget
...
)虽然这种安排在部署上似乎略微简单一些,但鉴于单独的后端目标可能具有更好的加载性能,在可能的情况下应优先使用单独的后端目标。
作为 QML 模块运行
可执行目标可作为 QML 模块的后端目标。在此情况下,不会生成插件库,因为 QML 模块将始终作为应用程序的一部分被直接加载。qt_add_qml_module() 命令会检测到可执行文件被用作后端目标的情况,并自动禁用单独插件的生成。 使用此配置时,请勿使用名称中包含“PLUGIN ”的任何选项。
当使用可执行文件作为后端目标时,源目录结构不应与 QML 模块的目标路径匹配。有关编译后资源的目标路径差异,请参阅《缓存编译后的 QML 源文件》。
自动生成qmldir 和typeinfo文件
默认情况下,系统会为正在定义的 QML 模块自动生成一个qmldir文件和一个 typeinfo 文件。这些文件的内容由提供给此命令的各种参数,以及添加到后端目标的源文件和.qml 文件共同决定。OUTPUT_DIRECTORY参数决定qmldir 和 typeinfo 文件的写入位置。如果 QML 模块包含插件,该插件也将创建在与qmldir 文件相同的目录中。
如果将QTP0004策略设置为NEW ,则对于每个包含.qml 文件的子目录,都会生成另一个qmldir 文件。这些额外的qmldir 文件仅通过prefer 指令重定向到模块的基目录。这样设计是为了确保模块中的所有 QML 组件都能相互访问,无论它们存储在哪个目录中。
如果使用静态构建的 Qt,在 CMake 配置运行期间会扫描后端目标的.qml 文件,以确定模块使用的导入内容并设置链接关系(可使用NO_IMPORT_SCAN 关键字禁用此操作)。 当向模块中添加或从模块中移除一个.qml 文件时,由于CMakeLists.txt 文件已被修改,CMake 通常会自动重新运行,并重新扫描相关文件。 在开发过程中,现有的.qml 文件可能会添加或移除某个导入项或类型。仅此操作本身并不会触发 CMake 自动重新运行,因此您应显式地重新运行 CMake,以强制重新生成qmldir 文件并更新所有链接关系。
在构建时,系统会扫描后端目标的 C++ 源文件,以生成一个类型信息文件和一个用于注册相关类型的 C++ 文件。生成的 C++ 文件会自动作为源文件添加到后端目标中。这要求在目标上启用AUTOMOC 。 项目负责确保这一点,通常是在调用qt_add_qml_module() 之前将CMAKE_AUTOMOC 变量设置为TRUE ,或者传入一个已将AUTOMOC 目标属性设置为TRUE 的现有目标。在目标上禁用AUTOMOC 不会导致错误,但项目需自行处理由此产生的后果。 这可能包括必须手动生成 typeinfo 文件(而非允许其自动生成但存在细节缺失的情况),以及添加 C++ 代码来注册这些类型。
项目应尽可能优先使用自动生成的typeinfo和qmldir 文件。这些文件更易于维护,且不像手动编写的文件那样容易出错。不过,如果项目需要自行提供这些文件,则可以禁用自动生成功能。NO_GENERATE_QMLDIR 选项将禁用qmldir 的自动生成,而NO_GENERATE_QMLTYPES 选项将禁用typeinfo和C++类型注册的自动生成。如果自动生成的typeinfo文件可以接受,但项目希望为该文件使用不同的名称,可以使用TYPEINFO 选项覆盖默认名称(但通常不需要这样做)。
缓存已编译的 QML 源文件
所有通过QML_FILES 参数添加到模块中的.qml 、.js 和.mjs 文件都将被编译为字节码,并直接缓存到后端目标中。这提高了模块的加载性能。原始的未编译文件也会存储在后端目标的资源中,因为在某些情况下QML引擎可能仍需要这些文件。
每个文件的资源路径由其相对于当前源目录(CMAKE_CURRENT_SOURCE_DIR )的相对路径决定。该资源路径将附加到由RESOURCE_PREFIX与目标路径拼接而成的前缀之后(但请参阅NO_RESOURCE_TARGET_PATH以了解此规则的例外情况)。
如果QTP0001策略设置为NEW ,则RESOURCE_PREFIX默认为/qt/qml/ ,这是 QML 引擎的默认导入路径。这确保了模块会被放入QML 导入路径中,无需额外配置即可被找到。
通常情况下,项目应将.qml 文件放置在与资源中相同的相对位置。如果.qml 文件所在的相对目录与其目标资源路径不同,则需要显式指定其在资源中的位置。这可通过设置源文件属性QT_RESOURCE_ALIAS 来实现,该属性必须在添加.qml 文件之前设置。例如:
set_source_files_properties(path/to/somewhere/MyFrame.qml PROPERTIES
QT_RESOURCE_ALIAS MyFrame.qml
)
qt_add_qml_module(someTarget
URI MyCo.Frames
RESOURCE_PREFIX /my.company.com/imports
QML_FILES
path/to/somewhere/MyFrame.qml
AnotherFrame.qml
)在上例中,目标路径将为MyCo/Frames 。在考虑了源文件属性后,两个.qml 文件将位于以下资源路径中:
/my.company.com/imports/MyCo/Frames/MyFrame.qml/my.company.com/imports/MyCo/Frames/AnotherFrame.qml
在极少数情况下,如果您希望覆盖 qmlcachegen 程序的自动选择,可以在模块目标上设置QT_QMLCACHEGEN_EXECUTABLE 目标属性。例如:
set_target_properties(someTarget PROPERTIES
QT_QMLCACHEGEN_EXECUTABLE qmlcachegen
)这将明确选择 qmlcachegen 作为要使用的程序,即使存在更好的替代方案。
此外,您还可以通过设置QT_QMLCACHEGEN_ARGUMENTS 选项向qmlcachegen传递额外参数。特别是,--only-bytecode 选项将禁用将QML脚本代码编译为C++的功能。例如:
set_target_properties(someTarget PROPERTIES
QT_QMLCACHEGEN_ARGUMENTS "--only-bytecode"
)另一个重要的参数是--direct-calls 。如果已安装Qt Quick Compiler 扩展,您可以使用该参数启用QML脚本编译器的直接模式。如果未安装这些扩展,则该参数将被忽略。该参数有一个简写形式,即QT_QMLCACHEGEN_DIRECT_CALLS 。
set_target_properties(someTarget PROPERTIES
QT_QMLCACHEGEN_DIRECT_CALLS ON
)最后,--verbose 参数可用于查看 qmlcachegen 的诊断输出:
set_target_properties(someTarget PROPERTIES
QT_QMLCACHEGEN_ARGUMENTS "--verbose"
)设置此标志后,qmlcachegen 会针对无法编译为 C++ 的每个函数输出警告。其中部分警告将指出您 QML 代码中的问题,另一些则会提示 QML 语言的某些特性在 C++ 代码生成器中尚未实现。 无论哪种情况,qmlcachegen 仍会为这些函数生成字节码。如果您只想查看 QML 代码中的问题,则应改用 qmllint 及其生成的目标文件。
QML 源代码的代码检查
如果通过QML_FILES 关键字,或通过后续对qt_target_qml_sources()的调用,向模块中添加了任何.qml 文件,系统将自动创建一个独立的代码检查目标。该代码检查目标的名称为target ,后跟_qmllint 。此外,还提供了一个all_qmllint 目标,它依赖于所有单独的*_qmllint 目标,以方便使用。
系统会自动创建一个名为dump_qml_context_properties 的全局构建目标,当尚未存在 QML 上下文属性转储文件时,该目标会运行qmlcontextpropertydump。qmlcontextpropertydump会生成一个 QML 上下文属性转储文件,qmllint会读取该文件,并针对 QML 中上下文属性的使用情况发出警告。
clean_qml_context_properties 全局构建目标允许删除已存在的 QML 上下文属性转储文件,并可在后续构建中通过dump_qml_context_properties 全局构建目标重新计算该文件。
当QT_QMLLINT_CONTEXT_PROPERTY_DUMP变量启用时,代码检查目标依赖于dump_qml_context_properties 构建目标。
.js 文件的命名约定
intended to be addressed as components 的 JavaScript 文件名应以大写字母开头。
或者,您也可以使用小写文件名,并将源文件属性QT_QML_SOURCE_TYPENAME设置为所需的类型名称。
单例
如果某个 QML 模块包含提供单例类型的.qml 文件,则必须将这些文件的QT_QML_SINGLETON_TYPE 源属性设置为TRUE ,以确保singleton 命令被写入qmldir文件。除了在包含pragma Singleton 语句的QML文件中进行设置外,还必须执行此操作。该源属性必须在创建单例所属的模块之前进行设置。
有关如何设置QT_QML_SINGLETON_TYPE 属性的示例,请参阅qt_target_qml_sources()。
使用 QML 类型编译器将 QML 编译为 C++
注意: QML 类型编译器 qmltc 无法保证生成的 C++ 代码在过去或未来的版本(甚至包括补丁版本)之间保持 API、源代码或二进制兼容性。 此外,使用 Qt Qml 模块且通过 qmltc 编译的应用程序将需要链接 Qt 的私有 API,另请参阅《使用 qmltc 编译 QML 代码》。
如果某个 QML 模块包含.qml 文件,您可以使用qmltc 将其编译为 C++。与字节码编译不同,您必须通过ENABLE_TYPE_COMPILER参数显式启用 qmltc。在此情况下,QML_FILES 下指定的.qml 文件将被编译。 以.js 和.mjs 结尾的文件将被忽略,因为 qmltc 不编译 JavaScript 代码。此外,标记了 QT_QML_SKIP_TYPE_COMPILER 源文件属性的文件也会被跳过。
默认情况下,qmltc 会针对给定的.qml 文件生成小写的.h 和.cpp 文件。例如,Foo.qml 最终会被编译为foo.h 和foo.cpp 。
生成的 C++ 文件会被放置到target 的BINARY_DIR 目录下专用的.qmltc/<target>/ 子目录中。随后,这些文件会自动添加到目标源文件中,并与其他源文件一起作为 Qt C++ 代码进行编译。
在处理 QML_FILES 时,将遵循以下源文件属性:
QT_QMLTC_FILE_BASENAME:使用此源文件属性指定非默认的 .h 和 .cpp 文件名,这可能有助于解决文件名冲突等问题(例如,假设正在编译 main.qml,但 main.h 已经存在,此时 #include "main.h" 可能无法产生预期的效果)。 QT_QMLTC_FILE_BASENAME 预期为文件名(不带扩展名),因此其前面的任何目录都会被忽略。与默认行为不同,QT_QMLTC_FILE_BASENAME 不会被转换为小写。QT_QML_SKIP_TYPE_COMPILER: 使用此源文件属性来指定 qmltc 必须忽略某个 QML 文件。
参数
qt_add_qml_module 的参数分为以下几类:
| 类别 | 参数 |
|---|---|
| 必填参数 | target,URI,STATIC,SHARED |
| 版本 | VERSION,PAST_MAJOR_VERSIONS,FOLLOW_FOREIGN_VERSIONING |
| 来源与资源 | QML_FILES,SOURCES,RESOURCES,RESOURCE_PREFIX,NO_RESOURCE_TARGET_PATH,DISCARD_QML_CONTENTS |
| 模块依赖项 | IMPORTS、OPTIONAL_IMPORTS 、DEFAULT_IMPORTS 、DEPENDENCIES 、IMPORT_PATH |
| 插件配置 | PLUGIN_TARGET、NO_PLUGIN、NO_PLUGIN_OPTIONAL、NO_CREATE_PLUGIN_TARGET、NO_GENERATE_PLUGIN_SOURCE、CLASS_NAME |
| 代码生成与工具 | NO_GENERATE_QMLTYPES,NO_GENERATE_QMLDIR,NO_GENERATE_EXTRA_QMLDIRS,TYPEINFO,NO_CACHEGEN,NO_LINT,NO_IMPORT_SCAN,NO_GENERATE_AOT_VALIDATION |
| 输出与安装 | OUTPUT_DIRECTORY,OUTPUT_TARGETS,NO_GENERATE_QTCONF |
| 其他 | NAMESPACE,DESIGNER_SUPPORTED |
| QML 类型编译器 (qmltc) | ENABLE_TYPE_COMPILER、TYPE_COMPILER_NAMESPACE 、QMLTC_EXPORT_DIRECTIVE 、QMLTC_EXPORT_FILE_NAME |
必填参数
target 指定了 QML 模块的底层目标名称。默认情况下,如果 Qt 是作为共享库构建的,则该模块将作为共享库创建;否则,将作为静态库创建。可以通过STATIC 或SHARED 选项显式覆盖此选择。
每个 QML 模块都必须定义一个 `URI`。它应采用点分隔的 URI 表示法,例如 `QtQuick.Layouts`。每个分段必须是格式正确的 ECMAScript 标识符名称。这意味着,例如,分段不能以数字开头,且不能包含-(减号)字符。 由于URI 将被转换为目录名称,因此应将其限制为拉丁字母表中的字母数字字符、下划线和点。 其他 QML 模块可能会在导入语句中使用此名称来导入该模块。URI 将用于生成的qmldir文件中的module 这一行。URI 也会被用于构建目标路径,方法是将点替换为正斜杠。
有关模块 URI 的进一步深入讨论,请参阅“已标识模块”。
版本
QML 模块还可以定义VERSION ,其形式为Major.Minor ,其中Major 和Minor 必须均为整数。可以附加.Patch 组件,但该组件将被忽略。此外,还可以选择在PAST_MAJOR_VERSIONS 关键字之后列出该模块所支持的早期主版本(详见下文)。 有关版本编号的进一步深入讨论,请参阅“已标识的模块”;有关注册过往主要版本的信息,请参阅“注册过往主要版本”;有关保持模块版本同步的信息,请参阅“保持模块版本同步”。
如果您不需要版本信息,应省略VERSION 参数。其默认值为可能的最高版本。QML模块的内部版本管理存在一些根本缺陷。您应使用外部包管理机制来管理QML模块的不同版本。
向模块添加源文件和资源
注意: QML模块是一个 逻辑上分组、自包含的功能单元。 构成该模块的所有文件应位于定义该模块的 CMakeLists.txt 文件所在的同一目录中,或其子目录之一中。如果多个模块都需要某项特定功能,请考虑将其封装到一个独立的模块中。然后,该模块可以被导入到其他模块中,从而提高代码的重用性和可维护性。
SOURCES 指定要添加到后端目标(backing target)中的一组非 QML 源文件。此功能仅为方便起见,其效果等同于使用内置的 CMake 命令 `target_sources() ` 将源文件添加到后端目标中。
QML_FILES 列出了该模块的.qml 、.js 和.mjs 文件。除非指定了NO_CACHEGEN 选项,否则这些文件将被自动编译为字节码并嵌入到后端目标中。未编译的文件始终存储在后端目标的嵌入资源中,即使指定了NO_CACHEGEN 也是如此。除非指定了NO_LINT 选项,否则未编译的文件还将通过一个单独的自定义构建目标由qmllint 进行处理。默认情况下,这些文件还将用于填充生成的qmldir文件中的类型信息。可以使用NO_GENERATE_QMLDIR 选项来禁用qmldir 文件的自动生成。通常应避免这样做,但在项目需要提供自己的qmldir 文件的情况下,可以使用此选项。 自 Qt 6.8 起,当启用QTP0004时,qt_add_qml_module 会为 QML 模块中的每个子目录创建额外的qmldir 文件,以确保每个 QML 文件都能通过隐式导入(implicit import)导入其所属的模块。可以通过向 QML 模块传递NO_GENERATE_EXTRA_QMLDIRS 标志来关闭此行为。NO_GENERATE_QMLDIR 等同于NO_GENERATE_EXTRA_QMLDIRS 。
注意: 有关在调用qt_add_qml_module() 之后如何添加qml文件的更多详细信息,请参阅 qt_target_qml_sources()。 例如,您可能希望根据 if 语句表达式有条件地添加文件,或者从只有在满足特定条件时才会被添加的子目录中添加文件。此外,通过qt_target_qml_sources()添加的文件还可以指定是否应在代码检查、字节码编译或生成qmldir 文件时被跳过。
RESOURCES 列出了该模块所需的其他文件,例如 QML 代码中引用的图像。这些文件将被作为编译内联资源添加(关于它们所在的基目录,请参阅RESOURCE_PREFIX的说明)。 如有需要,可以通过设置QT_RESOURCE_ALIAS 源属性来控制其相对位置,这与.qml 文件的做法相同(参见“缓存编译后的QML源文件”)。
RESOURCE_PREFIX 旨在封装项目的命名空间,并且通常对于项目定义的所有 QML 模块都保持一致。
不过,最好改用QTP0001CMake 策略。它定义了一个默认资源前缀,可确保您的 QML 模块最终位于 QML 引擎的默认导入路径之一之下。
如果您设置了RESOURCE_PREFIX ,还应将其添加到 QML 引擎的导入路径中,以便 QML 引擎能够找到该 QML 模块。
如果启用了QTP0001(例如通过qt_standard_project_setup(REQUIRES 6.5) ),其默认值为"/qt/qml/" ;否则,默认值为"/" 。
当各种文件被添加到编译后的资源中时,它们会被放置在由RESOURCE_PREFIX 与目标路径拼接而成的路径下。 对于后端目标为可执行文件的特殊情况,可能希望将模块的.qml 文件和其他资源直接放置在RESOURCE_PREFIX 之下。这可以通过指定NO_RESOURCE_TARGET_PATH 选项来实现,该选项仅在后端目标为可执行文件时才可使用。
注意:资源路径 、磁盘上的输出目录以及导入路径彼此相关。更改其中任何一项时,请确保其他部分保持一致,以便 QML 引擎和工具能够找到该模块。完整说明请参阅《编写 QML 模块》中的“自定义目录布局”部分。
注册过往的大版本
PAST_MAJOR_VERSIONS 包含该模块提供的其他主版本列表。对于这些版本中的每一个,以及每个未设置QT_QML_SOURCE_VERSIONS 的QML文件,qmldir文件中都会生成一个额外条目以指定该附加版本。此外,生成的模块注册代码将在C++端使用qmlRegisterModule()来注册这些过往主版本。 除非您指定了NO_GENERATE_QMLTYPES (但强烈建议不要使用此选项),否则系统会自动为您的 QML 模块生成模块注册代码。使用PAST_MAJOR_VERSIONS 会在导入模块时增加一些开销。您应尽可能少地增加模块的主版本号。 一旦您可以确信所有导入该模块的 QML 文件都会在导入语句中省略版本号,即可安全地省略 `PAST_MAJOR_VERSIONS`。届时,所有 QML 文件都将导入您模块的最新版本。如果您必须支持带版本号的导入,请考虑仅支持数量有限的过往主版本。
声明模块依赖关系
IMPORTS 提供了一个该模块所导入的其他 QML 模块列表。此处列出的每个模块都将作为import 条目添加到生成的qmldir文件中。如果某个 QML 文件导入了该模块,它也会导入IMPORTS 下列出的所有模块。 可选地,可以在斜杠后附加版本号,例如QtQuick/2.0 。省略版本号将导致导入可用的最高版本。您也可以只指定主版本,例如QtQuick/2 。在这种情况下,将导入给定主版本下可用的最高次版本。 最后,可将auto 作为版本指定(例如QtQuick/auto )。若指定auto ,则当前模块所使用的版本将传递给待导入的模块。假设模块YourModule 中包含条目QtQuick/auto ,若某个 QML 文件指定import YourModule 3.14 ,则会导入QtQuick 的3.14 版本。对于遵循共同版本方案的相关模块,应使用auto 。
IMPORTS 中的条目还可以通过在目标名称前添加TARGET 关键字来引用 CMake 目标。在这种情况下,QML 模块的 URI 将根据该目标自动确定。这需要启用 CMake 策略QTP0005。
例如,一个 QML 模块可以通过引用提供该模块的 CMake 目标来导入另一个模块:
qt_add_qml_module(my_module
URI MyModule
VERSION 1.0
IMPORTS
TARGET OtherQmlModule
)OPTIONAL_IMPORTS 提供了一组该模块可在运行时导入的其他 QML 模块。这些模块在导入当前模块时不会被 QML 引擎自动导入,而是作为对qmllint 等工具的提示。版本号的指定方式与IMPORTS 相同。此处列出的每个模块都将作为optional import 条目添加到生成的qmldir文件中。
OPTIONAL_IMPORTS 中的条目还可以通过在目标名称前添加TARGET 关键字来引用 CMake 目标。在这种情况下,QML 模块的 URI 将根据目标自动确定。这需要启用 CMake 策略QTP0005。
例如:
qt_add_qml_module(my_module
URI MyModule
VERSION 1.0
OPTIONAL_IMPORTS
TARGET OptionalQmlModule
)DEFAULT_IMPORTS 指定哪些可选导入应作为默认条目由工具加载。模块中的每组OPTIONAL_IMPORTS 都应指定一个条目。由于可选导入仅在运行时解析,因此 qmllint 等工具通常无法确定应解析哪个可选导入。 为了解决这个问题,您可以将其中一个可选导入指定为默认导入;工具随后会选择该导入。如果您有一个可选导入在运行时无需任何额外配置即可被使用,那么它就是默认导入的理想人选。
DEFAULT_IMPORTS 中的条目还可以通过在目标名称前添加TARGET 关键字来引用 CMake 目标。在这种情况下,QML 模块的 URI 将根据该目标自动确定。这需要启用 CMake 策略QTP0005。
例如:
qt_add_qml_module(my_module
URI MyModule
VERSION 1.0
OPTIONAL_IMPORTS
TARGET BackendA
TARGET BackendB
DEFAULT_IMPORTS
TARGET BackendA
)DEPENDENCIES 提供了一个该模块所依赖但未必会导入的其他 QML 模块列表。它通常用于仅存在于 C++ 层级的依赖关系,例如某个模块向 QML 注册了一个类,而该类使用或继承了另一个模块中定义的 C++ 类型。
例如,如果希望像以下这样继承QQuickItem :
class MyItem: public QQuickItem { ... };则必须确保包含 `QQuickItem` 的模块(名为 `QtQuick`)通过 `DEPENDENCIES ` 选项被声明为依赖项:
qt_add_qml_module(myTarget
...
DEPENDENCIES QtQuick
)若未这样做,可能会导致代码检查错误,或者在使用qmltc进行类型编译时出现错误,或者在使用qmlcachegen 将代码绑定并编译为 C++ 函数时出现错误。
另一个示例可能是:
class MyComponent : public QObject {
Q_OBJECT
QML_ELEMENT
// ...
signals:
void sigZoomAtMousePosition(const QPointF& aMousePos, double aZoomScaleFactor);
};其中,DEPENDENCIES QtQml 是QML工具链查找并使用QPointF 所必需的:
qt_add_qml_module(myTarget
...
DEPENDENCIES QtQml
)DEPENDENCIES 中的条目还可以通过在目标名称前添加TARGET 关键字来引用 CMake 目标。在这种情况下,QML 模块的 URI 和导入路径将根据该目标自动确定。这需要启用 CMake 策略QTP0005。
例如,一个 QML 模块可以通过引用提供该模块的 CMake 目标来声明对另一个模块的依赖:
qt_add_qml_module(my_module
URI MyModule
VERSION 1.0
DEPENDENCIES
TARGET OtherQmlModule
)注意: 在DEPENDENCIES 、IMPORTS 、OPTIONAL_IMPORTS 或DEFAULT_IMPORTS 中使用 TARGET <cmake-target> 不会自动链接到该目标。它仅用于推导 QML 元数据、导入路径以及建立构建顺序依赖关系。如果 QML 模块或其生成的插件需要该目标中的符号,则仍必须使用target_link_libraries() 显式链接该目标。
注意: 与TARGET 关键字一起使用的<cmake-target> 必须是您的项目构建的目标 (而非由find_package 提供的导入目标)。
注意: 在DEPENDENCIES 、IMPORTS 、OPTIONAL_IMPORTS 或DEFAULT_IMPORTS中 使用TARGET <cmake-target> 时, 仅会建立对指定目标的直接依赖关系。被引用的目标的传递性 QML 模块依赖关系(即该目标本身所依赖的 QML 模块)不会自动添加。每个 QML 模块依赖关系都必须显式声明。
注意: 如果该模块已通过IMPORTS 选项导入,则无需将其添加 到DEPENDENCIES 中。建议使用更轻量级的DEPENDENCIES ,而非IMPORTS 。
当后端目标为可执行文件且使用了TARGET 依赖项(需要QTP0005)时,qt_add_qml_module() 会自动在构建树中可执行文件旁生成一个qt.conf 文件。该文件配置了QML导入路径,使QML引擎能在运行时定位模块的依赖项,而无需手动添加IMPORT_PATH 条目。NO_GENERATE_QTCONF 选项可抑制此行为,当生成的文件与构建目录中已存在的qt.conf 发生冲突时,可能需要使用此选项。该选项自Qt 6.12起引入。
警告: 使用NO_GENERATE_QTCONF时, 应用程序可能无法在运行时找到其QML模块的依赖项。您必须手动配置QML导入路径——例如,通过向现有的qt.conf 文件中添加相应的导入路径。
必须在指定模块名称的同时,按与IMPORTS 和OPTIONAL_IMPORTS 相同的格式指定依赖项的模块版本。此处列出的每个模块都将作为depends 条目添加到生成的qmldir文件中。
IMPORT_PATH 可用于将其他 QML 模块添加到搜索路径中,以便查找本模块所依赖的模块。其他模块的qmldir 文件必须位于其自身目标路径下,且该路径位于某个搜索路径之下。Qt 假设IMPORT_PATH 下的所有文件均来自可信来源。
如果后端目标是一个静态库,且该静态库将被安装,则应提供 `OUTPUT_TARGETS ` 以指定一个变量,用于存储同样需要安装的附加目标列表。这些附加目标由 `qt_add_qml_module() ` 在内部生成,并作为后端目标链接要求的一部分被引用,以确保资源能够正确设置和加载。
目标与插件目标
以下选项控制插件目标的创建和配置方式。对于大多数模块,默认设置已足够,无需使用这些选项。常见场景:
- 默认— 系统会自动创建一个独立的插件目标,并生成相应的源文件。该插件为可选(当直接链接底层库时不会被加载)。
- 自定义插件— 使用NO_GENERATE_PLUGIN_SOURCE来提供您自己的插件类实现。您还应将CLASS_NAME设置为与您的自定义插件类名称一致。通常还需设置NO_PLUGIN_OPTIONAL,因为此时插件包含非简单的代码。
- 无插件— 当模块总是直接链接且从未动态加载时,请使用NO_PLUGIN。
- 可执行目标— 不会自动生成插件。
PLUGIN_TARGET 指定与 QML 模块关联的插件目标。PLUGIN_TARGET 可以与后端目标target 相同,在这种情况下将不会有单独的后端目标。如果未提供PLUGIN_TARGET ,则默认为target 并附加plugin 。 例如,名为mymodule 的后端目标,其默认插件名称为mymoduleplugin 。插件目标的名称将用于填充生成的qmldir文件中plugin 这一行。因此,切勿通过设置OUTPUT_NAME 或其任何相关属性来尝试更改插件的输出名称。
除非已存在,否则该命令将创建后端target 以及插件目标(如果不同)。项目通常应允许由该命令自动创建,以确保它们以适当的目标类型生成。如果后端target 是静态库,则插件也将作为静态库创建。 如果后端目标target 是共享库,则插件将作为模块库创建。如果传入了一个现有的target 且其为可执行目标,则不会生成插件。如果您打算始终直接链接到后端目标且不需要插件,可以通过添加NO_PLUGIN 选项来禁用插件。同时指定NO_PLUGIN 和PLUGIN_TARGET 属于错误操作。
在某些情况下,项目可能希望延迟创建插件目标,直到调用之后再进行。此时可以提供NO_CREATE_PLUGIN_TARGET 选项。随后,项目应在插件目标创建完成后对其调用qt_add_qml_plugin()。当提供NO_CREATE_PLUGIN_TARGET 时,还必须提供PLUGIN_TARGET 以显式指定插件目标的名称。
默认情况下,qt_add_qml_module() 会自动生成一个.cpp 文件,该文件实现了由CLASS_NAME 参数指定的插件类。生成的.cpp 文件将自动作为待编译的源文件添加到插件目标中。 如果项目希望提供插件类的自定义实现,应指定NO_GENERATE_PLUGIN_SOURCE 选项。若未提供CLASS_NAME ,则默认采用URI (将点替换为下划线),并在后缀处追加Plugin 。除非 QML 模块没有插件,否则类名将作为classname 行记录在生成的qmldir文件中。 您需要将包含自定义插件代码的任何 C++ 文件添加到插件目标中。由于该插件很可能包含超出简单加载底层库范围的功能,您可能还需要添加NO_PLUGIN_OPTIONAL。否则,如果 QML 引擎检测到底层库已被链接,它可能会跳过加载该插件。
如果指定了NO_PLUGIN 关键字,则不会构建任何插件。 因此,该关键字与所有用于自定义插件目标的选项均不兼容,特别是NO_GENERATE_PLUGIN_SOURCE、NO_PLUGIN_OPTIONAL、PLUGIN_TARGET、NO_CREATE_PLUGIN_TARGET 以及CLASS_NAME。 如果您未为模块提供插件,则只有当其底层库已被链接到可执行文件中时,该模块才能被完全使用。通常很难保证链接器会保留其认为未使用的库的链接关系。
如果指定了NO_PLUGIN_OPTIONAL 关键字,则该插件会在生成的qmldir 文件中被记录为非可选。如果某个QML模块的所有功能都在其后端目标中实现,而插件目标是独立的,那么该插件可以是可选的——这也是默认且推荐的配置方式。 自动生成的插件源文件满足此要求。若项目为插件提供了自己的.cpp 实现,通常意味着还需使用NO_PLUGIN_OPTIONAL 关键字,因为该插件几乎肯定包含QML模块所需的功能。
自动类型注册
qt_add_qml_module 会自动生成多个文件。以下选项控制生成内容。在大多数情况下,默认设置是正确的,无需使用这些选项。
| 选项 | 禁用内容 |
|---|---|
NO_GENERATE_QMLTYPES | Typeinfo 文件(.qmltypes )和 C++ 类型注册代码 |
NO_GENERATE_QMLDIR | qmldir 模块定义文件(隐含NO_GENERATE_EXTRA_QMLDIRS ) |
NO_GENERATE_EXTRA_QMLDIRS | 子目录中的其他qmldir 文件(参见QTP0004) |
NO_CACHEGEN | .qml 、.js 和.mjs 文件的字节码编译 |
NO_LINT | *_qmllint 的代码检查目标 |
| NO_IMPORT_SCAN | 自动导入扫描(静态构建:配置时;可执行文件:构建时) |
NO_GENERATE_AOT_VALIDATION | 生成用于验证由 Qt Quick Compiler。该选项在 Qt 6.12 中引入。 |
对于由AUTOMOC 处理的底层目标的C++源文件,将自动执行类型注册。这将在输出目录中生成一个typeinfo文件,文件名为target 名称后缀.qmltypes 。如果需要,可以使用TYPEINFO 选项更改此文件名,但通常没有必要。 该文件名还会作为typeinfo 条目记录在生成的qmldir文件中。可通过NO_GENERATE_QMLTYPES 选项禁用自动类型注册;在此情况下,系统将不会生成typeinfo文件,但仍要求项目生成一个typeinfo文件,并将其放置在与生成的qmldir 文件相同的目录中。
OUTPUT_DIRECTORY 指定插件库、qmldir 和typeinfo文件的生成位置。若未指定此关键字,默认值将为目标路径(由URI 生成)后缀QT_QML_OUTPUT_DIRECTORY变量的值。若该变量未定义,则默认值取决于底层目标的类型。 对于可执行文件,该值将是目标路径后缀为${CMAKE_CURRENT_BINARY_DIR} 的形式;而对于其他目标,则仅为${CMAKE_CURRENT_BINARY_DIR} 。当源代码树的结构与 QML 模块目标路径的结构相匹配时(强烈建议如此),通常无需设置QT_QML_OUTPUT_DIRECTORY。 为了与目标路径的结构保持一致,您必须将目录命名为与模块 URI 各段完全一致的名称。例如,如果您的模块 URI 是MyUpperCaseThing.mylowercasething ,则需要将其放置在名为MyUpperCaseThing/mylowercasething/ 的目录中。
通常很少需要指定OUTPUT_DIRECTORY 关键字,但如果使用了该关键字,调用方很可能还需要向IMPORT_PATH中添加内容,以确保代码检查、QML源文件的缓存编译、静态构建中插件的自动导入,以及非静态构建中导入的QML模块的部署都能正常工作。
Qt Quick 设计器兼容性
DESIGNER_SUPPORTED 如果 QML 模块支持Qt Quick 设计器,则应提供此兼容性。当提供此兼容性时,生成的qmldir 文件将包含一行designersupported 。有关这如何影响Qt Quick 设计器处理该插件的方式,请参阅《模块定义 qmldir 文件》。
保持模块版本同步
FOLLOW_FOREIGN_VERSIONING 关键字与您在不同QML模块中定义的、基于C++的QML类型的基类相关。通常,您的模块的版本方案与提供基类的模块的版本方案并不一致。 因此,默认情况下,在导入您的模块时,基础类型的所有修订版本都会被提供。如果指定了 `FOLLOW_FOREIGN_VERSIONING `,则会遵循附加在基础类型及其属性上的版本信息。 因此,import MyModule 2.8 将仅提供来自MyModule 以外的任何基类中版本号不超过2.8 的属性。这在您希望将模块版本与作为基类的其他模块保持同步时尤为有用。在这种情况下,您可能希望自定义类型不暴露来自版本号高于所导入版本的模块的基类属性。
生成的代码中的 C++ 命名空间
如果使用NAMESPACE 关键字指定了命名空间,则插件和注册代码将生成到此名称的C++命名空间中。
qmlimportscanner 和 NO_IMPORT_SCAN
对于静态 Qt 构建,会在 configure 阶段运行qmlimportscanner ,以扫描 QML 模块的.qml 文件,并识别其使用的 QML 导入项(参见qt_import_qml_plugins())。 对于非静态 Qt 构建,如果目标是可执行文件,则会在构建时执行类似的扫描,以提供部署脚本所需的信息(参见qt_deploy_qml_imports())。通过提供NO_IMPORT_SCAN 选项,可以禁用这两种扫描。 这样做意味着,对于静态构建,项目需自行确保所有必需的插件均已实例化并链接;对于非静态构建,项目必须手动确定并部署可执行目标所使用的所有 QML 模块。
DISCARD_QML_CONTENTS
默认情况下,QML 和 JS 源文件的内容会被包含在目标的资源系统中。使用DISCARD_QML_CONTENTS 可移除这些内容,从而减小二进制文件的大小。
注意:如果 从二进制文件中省略源代码,QML 引擎将不得不依赖由qmlcachegen或qmlsc 生成的编译单元。这些编译单元与构建时所使用的特定 Qt 版本绑定。如果您更改了应用程序所使用的 Qt 版本,这些编译单元将无法再被加载。
qmltc 的参数
ENABLE_TYPE_COMPILER 可用于通过qmltc 将.qml 文件编译为 C++ 源代码。源属性为QT_QML_SKIP_TYPE_COMPILER 的文件不会被编译为 C++。
TYPE_COMPILER_NAMESPACE 该参数允许覆盖qmltc生成代码时使用的命名空间。默认情况下,生成的代码的命名空间遵循 URI 中显示的模块层次结构,例如,对于 URI 为MyModule 的模块,生成的代码命名空间为MyModule ;对于 URI 为com.example.MyModule 的模块,生成的代码命名空间为com::example::Module 。 通过指定TYPE_COMPILER_NAMESPACE 选项,生成的代码可以置于自定义命名空间中,其中不同的子命名空间需用“::”分隔,例如,位于MyNamespace内部的MySubnamespace命名空间表示为“MyNamespace::MySubnamespace”。 除“::”外,还适用 C++ 命名空间的命名规则。
QMLTC_EXPORT_DIRECTIVE 当需要将qmltc生成的类从 QML 库中导出时,应配合使用QMLTC_EXPORT_FILE_NAME 。默认情况下,qmltc 生成的类不会从其库中导出。 定义当前库导出宏的头文件可作为QMLTC_EXPORT_FILE_NAME 的可选参数指定,而导出宏的名称应作为QMLTC_EXPORT_DIRECTIVE 的参数指定。如果不需要或不希望额外包含头文件(例如,当导出宏的头文件已被基类间接包含时),则可以省略QMLTC_EXPORT_FILE_NAME 选项。
© 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.