Writing QML Modules | Qt QML 6.4.1

- Qt 6.4
- Qt QML
- Writing QML Modules
- Qt 6.4.1 Reference Documentation

# Writing QML Modules

You can declare a QML module using the CMake QML Module API to:

- Generate qmldir and *.qmltypes files.
- Register C++ types annotated with QML_ELEMENT.
- Invoke qmlcachegen.
- Provide modules both in the physical and in resource file system.
- Use the pre-compiled versions of QML files.
- Bundle the module's files in the resource file system.
- Combine QML files and C++-based types in the same module.
- Create a backing library and an optional plugin. Link the backing library into the application to avoid loading the plugin at run time.

All the above actions can also be configured separately. For more information, see CMake QML Module API.

## Multiple QML Modules in One Binary

You can add multiple QML modules into the same binary. Define a CMake target for each module and then link the targets to the executable. If the extra targets are all static libraries, the result will be one binary, which contains multiple QML modules. In short you can create an application like this:

     myProject
         | - CMakeLists.txt
         | - main.cpp
         | - main.qml
         | - onething.h
         | - onething.cpp
         | - ExtraModule
             | - CMakeLists.txt
             | - Extra.qml
             | - extrathing.h
             | - extrathing.cpp

To begin, let's assume main.qml contains an instantiation of Extra.qml:

     import ExtraModule
     Extra { ... }

The extra module has to be a static library so that you can link it into the main program. Therefore, state as much in ExtraModule/CMakeLists.txt:

     qt_add_library(extra_module STATIC)
     qt_add_qml_module(extra_module
         URI "ExtraModule"
         VERSION 1.0
         QML_FILES
             Extra.qml
         SOURCES
             extrathing.cpp extrathing.h
     )

This generates two targets: `extra_module` for the backing library, and `extra_moduleplugin` for the plugin. Being a static library too, the plugin cannot be loaded at runtime.

In myProject/CMakeLists.txt you need to specify the QML module that main.qml and any types declared in onething.h are part of:

     qt_add_executable(main_program main.cpp)

     qt_add_qml_module(main_program
         VERSION 1.0
         URI myProject
         QML_FILES
             main.qml
         SOURCES
             onething.cpp onething.h

     )

From there, you add the subdirectory for the extra module:

     add_subdirectory(ExtraModule)

To ensure that linking the extra module works correctly, you need to:

- Define a symbol in the extra module.
- Create a reference to the symbol from the main program.

QML plugins contain a symbol you can use for this purpose. You can use the Q_IMPORT_QML_PLUGIN macro to create a reference to this symbol. Add the following code to the main.cpp:

     #include <QtQml/qqmlextensionplugin.h>
     Q_IMPORT_QML_PLUGIN(ExtraModulePlugin)

`ExtraModulePlugin` is the name of the generated plugin class. It's composed of the module URI with `Plugin` appended to it. Then, in the main program's CMakeLists.txt, link the plugin, not the backing library, into the main program:

     target_link_libraries(main_program PRIVATE extra_moduleplugin)

## Exporting Multiple Major Versions from The Same Module

qt_add_qml_module by default considers the major version given in its URI argument, even if the individual types declare other versions in their added specific version via QT_QML_SOURCE_VERSIONS or Q_REVISION. If a module is available under more than one version, you also need to decide what versions the individual QML files are available under. To declare further major versions, you can use the `PAST_MAJOR_VERSIONS` option to `qt_add_qml_module` as well as the `QT_QML_SOURCE_VERSIONS` property on individual QML files.

     set_source_files_properties(Thing.qml
         PROPERTIES
             QT_QML_SOURCE_VERSIONS "1.4;2.0;3.0"
     )

     set_source_files_properties(OtherThing.qml
         PROPERTIES
             QT_QML_SOURCE_VERSIONS "2.2;3.0"
     )

     qt_add_qml_module(my_module
         URI MyModule
         VERSION 3.2
         PAST_MAJOR_VERSIONS
             1 2
         QML_FILES
             Thing.qml
             OtherThing.qml
             OneMoreThing.qml
         SOURCES
             everything.cpp everything.h
     )

`MyModule` is available in major versions 1, 2, and 3. The maximum version available is 3.2. You can import any version 1.x or 2.x with a positive x. For Thing.qml and OtherThing.qml we have added explicit version information. Thing.qml is available from version 1.4, and OtherThing.qml is available from version 2.2. You have to specify the later versions, too, in each `set_source_files_properties()` because you may remove QML files from a module when bumping the major version. There is no explicit version information for OneMoreThing.qml. This means that OneMoreThing.qml is available in all major versions, from minor version 0.

With this setup, the generated registration code will register the module `versionsqmlRegisterModule()` for each of the major versions. This way, all versions can be imported.

## Custom Directory Layouts

The easiest way to structure QML modules is to keep them in directories named by their URIs. For example, a module My.Extra.Module would live in a directory My/Extra/Module relative to the application that uses it. This way, they can easily be found at run time and by any tools.

In more complex projects, this convention can be too limiting. You might for instance want to group all QML modules in one place to avoid polluting the project's root directory. Or you want to reuse a single module in multiple applications. For those cases, `QT_QML_OUTPUT_DIRECTORY` in combination with `RESOURCE_PREFIX` and IMPORT_PATH can be used.

To collect QML modules into a specific output directory, for example a subdirectory "qml" in the build directory QT_QML_OUTPUT_DIRECTORY, set the following in the top-level CMakeLists.txt:

     set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml)

The output directories of QML modules move to the new location. Likewise, the `qmllint` and `qmlcachegen` invocations are automatically adapted to use the new output directory as an import path. Because the new output directory is not part of the default QML import path, you have to add it explicitly at run time, so that the QML modules can be found.

Now that the physical file system is taken care of, you may still want to move the QML modules into a different place in the resource file system. This is what the RESOURCE_PREFIX option is for. You have to specify it separately in each qt_add_qml_module. The QML module will then be placed under the specified prefix, with a target path generated from the URI appended. For example, consider the following module:

     qt_add_qml_module(
         URI My.Great.Module
         VERSION 1.0
         RESOURCE_PREFIX /example.com/qml
         QML_FILES
             A.qml
             B.qml
     )

This will add a directory `example.com/qml/My/Great/Module` to the resource file system and place the QML module defined above in it. You don't strictly need to add the resource prefix to the QML import path as the module can still be found in the physical file system. However, it generally is a good idea to add the resource prefix to the QML import path because loading from the resource file system is faster than loading from the physical file system for most modules.

If the QML modules are meant to be used in a larger project with multiple import paths, you'll have to do an additional step: Even if you add the import paths at run time, tooling like `qmllint` does not have access to it, and might fail to find the correct dependencies. Use `IMPORT_PATH` to tell tooling about the additional paths it has to consider. For example:

     qt_add_qml_module(
         URI My.Dependent.Module
         VERSION 1.0
         QML_FILES
             C.qml
         IMPORT_PATH "/some/where/else"
     )

## Eliminating Run Time File System Access

If all QML modules are always loaded from the resource file system, you can deploy the application as a single binary. Let's first consider the simple case:

     QQmlEngine qmlEngine;
     qmlEngine.addImportPath(QStringLiteral(":/"));
     // Use qmlEngine to load the main.qml file.

Note: ":/" is used for simplicity here. See Custom Directory Layouts for more complex cases.

If all the modules are linked into the application and if you're following the default resource directory structure, do not add any further import paths as those might override the one you added.

If you have specified a custom `RESOURCE_PREFIX`, you have to add the custom resource prefix to the import path instead. You can also add multiple resource prefixes.

The path `:/qt-project.org/imports/` is part of the default QML import path. If you use it, you don't have to specially add it. Qt's own QML modules are placed there, though. You have to be careful not to overwrite them. For modules that are heavily re-used across different projects `:/qt-project.org/imports/` is acceptable. By using it you can avoid forcing all the users to add custom import paths.

## Integrating custom QML plugins

If you bundle an image provider in the QML module, you need to implement the QQmlEngineExtensionPlugin::initializeEngine() method. This, in turn, makes it necessary to write the own plugin. To support this use case, NO_GENERATE_PLUGIN_SOURCE can be used.

Let's consider a module that provides its own plugin source:

     qt_add_qml_module(imageproviderplugin
         VERSION 1.0
         URI "ImageProvider"
         PLUGIN_TARGET imageproviderplugin
         NO_PLUGIN_OPTIONAL
         NO_GENERATE_PLUGIN_SOURCE
         CLASS_NAME ImageProviderExtensionPlugin
         QML_FILES
             AAA.qml
             BBB.qml
         SOURCES
             moretypes.cpp moretypes.h
             myimageprovider.cpp myimageprovider.h
             plugin.cpp
     )

You may declare an image provider in myimageprovider.h, like this:

     class MyImageProvider : public QQuickImageProvider
     {
         [...]
     };

In plugin.cpp you can then define the QQmlEngineExtensionPlugin:

     #include <myimageprovider.h>
     #include <QtQml/qqmlextensionplugin.h>

     class ImageProviderExtensionPlugin : public QQmlEngineExtensionPlugin
     {
         Q_OBJECT
         Q_PLUGIN_METADATA(IID QQmlEngineExtensionInterface_iid)
     public:
         void initializeEngine(QQmlEngine *engine, const char *uri) final
         {
             Q_UNUSED(uri);
             engine->addImageProvider("myimg", new MyImageProvider);
         }
     };

This will make the image provider available. The plugin and the backing library both are in the same CMake target imageproviderplugin. This is done so that the linker does not drop parts of the module in various scenarios.

As the plugin creates an image provider, it no longer has a trivial `initializeEngine` function. Therefore, the plugin is no longer optional.