1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
5\page qtqml-writing-a-module.html
6\title Writing QML Modules
7\brief How to write a custom QML module.
9You should declare a QML module using the \l{qt_add_qml_module}
10{CMake QML Module API} to:
13\li Generate \l {Module Definition qmldir Files}{qmldir} and
14 \l {Type Description Files}{*.qmltypes files}.
15\li Register C++ types annotated with \l QML_ELEMENT.
16\li Combine QML files and C++-based types in the same module.
17\li Invoke \l {Ahead-of-Time-Compilation}{qmlcachegen} on all
19\li Use the pre-compiled versions of QML files inside the module.
20\li Provide the module both in the physical and in the
21 \l{The Qt Resource System}{resource file system}.
22\li Create a backing library and an optional plugin. Link the backing library
23 into the application to avoid loading the plugin at run time.
26All the above actions can also be configured separately.
27For more information, see \l {qt_add_qml_module}{CMake QML Module API}.
29\section1 Multiple QML Modules in One Binary
31You can add multiple QML modules into the same binary. Define a CMake target for
32each module and then link the targets to the executable.
33If the extra targets are all static libraries, the result will be one binary,
34which contains multiple QML modules. In short you can create an application
51To begin, let's assume main.qml contains an instantiation of Extra.qml:
58The extra module has to be a static library so that you can link it
59into the main program. Therefore, state as much in ExtraModule/CMakeLists.txt:
61\quotefromfile qml/CMakeLists.txt
62\printuntil extrathing.h
65This generates two targets: \c extra_module for the backing library, and
66\c extra_moduleplugin for the plugin. Being a static library too, the plugin cannot
69In myProject/CMakeLists.txt you need to specify the QML module that main.qml
70and any types declared in onething.h are part of:
72\quotefromfile qml/myProject-CMakeLists.txt
77From there, you add the subdirectory for the extra module:
79\quotefromfile qml/CMakeLists.txt
80\skipto add_subdirectory
83To ensure that linking the extra module works correctly, you need to:
86\li Define a symbol in the extra module.
87\li Create a reference to the symbol from the main program.
90QML plugins contain a symbol you can use for this purpose.
91You can use the \l Q_IMPORT_QML_PLUGIN macro to create a reference to this symbol.
92Add the following code to the main.cpp:
95#include <QtQml/QQmlExtensionPlugin>
96Q_IMPORT_QML_PLUGIN(ExtraModulePlugin)
99\c ExtraModulePlugin is the name of the generated plugin class. It's composed
100of the module URI with \c Plugin appended to it. Then, in the main program's
101CMakeLists.txt, link the plugin, not the backing library, into the main program:
103\quotefromfile qml/myProject-CMakeLists.txt
104\skipto target_link_libraries
109QML has a complex system to assign versions to components and modules. In most
110cases you should ignore all of it by:
113 \li Never adding a version to your import statements
114 \li Never specifying any versions in \l{qt_add_qml_module}
115 \li Never using \l{QML_ADDED_IN_VERSION} or \l{QT_QML_SOURCE_VERSIONS}
116 \li Never using \l{Q_REVISION} or the \c{REVISION()} attribute in \l{Q_PROPERTY}
117 \li Avoiding unqualified access
118 \li Generously using import namespaces
121Versioning is ideally handled outside the language itself. You may, for example,
122keep separate \l{QML Import Path}{import paths} for different sets of QML modules.
123Or you may use a versioning mechanism provided by your operating system to install
124or uninstall packages with QML modules.
126In some cases, Qt's own QML modules may show different behavior, depending on what
127version is imported. In particular, if a property is added to a QML component, and
128your code contains unqualified access to another property of the same name, your
129code will break. In the following example, the code will behave differently
130depending on the version of Qt, because the \l [QML] {Rectangle::}{topLeftRadius}
131property was added in Qt 6.7:
137 // property you want to use
138 property real topLeftRadius: 24
142 // correct for Qt version < 6.7 but uses Rectangle's topLeftRadius in 6.7
143 objectName: "top left radius:" + topLeftRadius
148The solution here is to avoid the unqualified access. \l{qmllint Reference}{qmllint} can be used to
149find such problems. The following example accesses the property you actually mean
150in a safe, qualified way:
158 // property you want to use
159 property real topLeftRadius: 24
163 // never mixes up topLeftRadius with unrelated Rectangle's topLeftRadius
164 objectName: "top left radius:" + root.topLeftRadius
169You can also avoid the incompatibility by importing a specific version of QtQuick:
172// make sure Rectangle has no topLeftRadius property
176 property real topLeftRadius: 24
178 objectName: "top left radius:" + topLeftRadius
183Another problem solved by versioning is the fact that QML components imported by
184different modules may shadow each other. In the following example, if \c{MyModule} were
185to introduce a component named \c{Rectangle} in a newer version, the \c{Rectangle}
186created by this document would not be a \c {QQuickRectangle} anymore, but rather the
187new \c{Rectangle} introduced by \c{MyModule}.
194 // MyModule's Rectangle, not QtQuick's
198A good way to avoid the shadowing would be to import \c{QtQuick} and/or \c{MyModule}
199into type namespaces as follows:
206 // QtQuick's Rectangle
210Alternatively, if you import \c{MyModule} with a fixed version, and the new component
211receives a correct version tag via \l{QML_ADDED_IN_VERSION} or \l{QT_QML_SOURCE_VERSIONS},
212the shadowing is also avoided:
217// Types introduced after 1.0 are not available, like Rectangle for example
221 // QtQuick's Rectangle
225For this to work, you need to use versions in \c{MyModule}. There are a few things
228\section2 If you add versions, add them everywhere
230You need to add a \c{VERSION} attribute to \l{qt_add_qml_module}. The version should
231be the most recent version provided by your module. Older minor versions of the same
232major version will automatically be registered. For older major versions, see
233\l{#Exporting Multiple Major Versions from The Same Module}{below}.
235You should add \l{QML_ADDED_IN_VERSION} or \l{QT_QML_SOURCE_VERSIONS} to every type
236that was \e not introduced in version \c{x.0} of your module, where \c{x} is the current
239If you forget to add a version tag, the component will be available in all versions,
240making the versioning ineffective.
242However, there is no way to add versions to properties, methods, and signals defined
243in QML. The only way to version QML documents is to add a new document with separate
244\l{QT_QML_SOURCE_VERSIONS} for each change.
246\section2 Versions are not transitive
248If a component from your module \c{A} imports another module \c{B} and instantiates a type
249from that module as the root element, then the import version of \c{B} is relevant for the
250properties available from the resulting component, no matter what version of \c{A} is
253Consider a file \c{TypeFromA.qml} with version \c{2.6} in module \c{A}:
258// Exposes TypeFromB 2.7, no matter what version of A is imported
262Now consider a user of \c{TypeFromA}:
267// This is TypeFromB 2.7.
271The user hopes to see version \c{2.6} but actually gets version
272\c{2.7} of the base class \c{TypeFromB}.
274Therefore, in order to be safe, you not only have to duplicate your QML files and
275give them new versions when you add properties yourself, but also when you bump
276versions of modules you import.
278\section2 Qualified access does not honor versioning
280Versioning only affects unqualified access to members of a type or the type itself.
281In the example with \c{topLeftRadius}, if you write \c{this.topLeftRadius}, the
282property will be resolved if you're using Qt 6.7, even if you \c{import QtQuick 6.6}.
284\section2 Versions and revisions
286With \l{QML_ADDED_IN_VERSION}, and the two-argument variants of \l{Q_REVISION} and
287\l{Q_PROPERTY}'s \c{REVISION()}, you can only declare versions that are tightly coupled
288to the \l{QMetaObject}{metaobject's} revision as exposed in \l{QMetaMethod::revision}
289and \l{QMetaProperty::revision}. This means all the types in your type hierarchy have
290to follow the same versioning scheme. This includes any types provided by Qt itself
291that you inherit from.
293With \l{QQmlEngine::}{qmlRegisterType} and related functions you can register any mapping
294between metaobject revisions and type versions. You then need to use the one-argument forms
295of \l{Q_REVISION} and the \c{REVISION} attribute of \l{Q_PROPERTY}. However, this
296can become rather complex and confusing and is not recommended.
298\section2 Exporting multiple major versions from the same module
300\l qt_add_qml_module by default considers the major version given in its
301VERSION argument, even if the individual types declare other versions in their
302added specific version via \l QT_QML_SOURCE_VERSIONS or \l Q_REVISION.
303If a module is available under more than one version, you also need to decide
304what versions the individual QML files are available under. To declare further
305major versions, you can use the \c PAST_MAJOR_VERSIONS option to
306\c qt_add_qml_module as well as the \c {QT_QML_SOURCE_VERSIONS} property on
309\quotefile qml/MajorProject-CMakeLists.txt
311\c MyModule is available in major versions 1, 2, and 3. The maximum version
312available is 3.2. You can import any version 1.x or 2.x with a positive x. For
313Thing.qml and OtherThing.qml we have added explicit version information.
314Thing.qml is available from version 1.4, and OtherThing.qml is available from
315version 2.2. You have to specify the later versions, too, in each
316\c set_source_files_properties() because you may remove QML files
317from a module when bumping the major version. There is no explicit version
318information for OneMoreThing.qml. This means that OneMoreThing.qml is available
319in all major versions, from minor version 0.
321With this setup, the generated registration code will register the module
322\c versions using \l qmlRegisterModule() for each of the major versions. This
323way, all versions can be imported.
326\section1 Custom Directory Layouts
328The easiest way to structure QML modules is to keep them in directories named by
329their URIs. For example, a module My.Extra.Module would live in a directory
330My/Extra/Module relative to the application that uses it. This way, they can
331easily be found at run time and by any tools.
333In more complex projects, this convention can be too limiting. You might for
334instance want to group all QML modules in one place to avoid polluting the
335project's root directory. Or you want to reuse a single module in multiple
336applications. For those cases, \c QT_QML_OUTPUT_DIRECTORY in combination with
337\c RESOURCE_PREFIX and \l IMPORT_PATH can be used.
339To collect QML modules into a specific output directory, for example a
340subdirectory "qml" in the build directory \l QT_QML_OUTPUT_DIRECTORY, set the
341following in the top-level CMakeLists.txt:
344set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml)
347The output directories of QML modules move to the new location.
348Likewise, the \c qmllint and \c qmlcachegen invocations are automatically
349adapted to use the new output directory as an \l[QtQml]{QML Import Path}{import path}.
350Because the new output directory is not part of the default QML import path,
351you have to add it explicitly at run time, so that the QML modules can be found.
354Now that the physical file system is taken care of, you may still want to move
355the QML modules into a different place in the resource file system. This is what
356the RESOURCE_PREFIX option is for. You have to specify it separately in
357each \l qt_add_qml_module. The QML module will then be placed under the specified
358prefix, with a target path generated from the URI appended. For example,
359consider the following module:
365 RESOURCE_PREFIX /example.com/qml
372This will add a directory \c example.com/qml/My/Great/Module to the resource file
373system and place the QML module defined above in it. You don't strictly need to
374add the resource prefix to the QML import path as the module can still be found
375in the physical file system. However, it generally is a good idea to add the
376resource prefix to the QML import path because loading from the resource file
377system is faster than loading from the physical file system for most modules.
379If the QML modules are meant to be used in a larger project with multiple import
380paths, you'll have to do an additional step: Even if you add the import paths at
381run time, tooling like \c qmllint does not have access to it, and might fail to
382find the correct dependencies. Use \c IMPORT_PATH to tell tooling about the
383additional paths it has to consider. For example:
387 URI My.Dependent.Module
391 IMPORT_PATH "/some/where/else"
395\section1 Eliminating Run Time File System Access
397If all QML modules are always loaded from the resource
398file system, you can deploy the application as a single binary.
400If \l{QTP0001} policy is set to \c NEW, the \c RESOURCE_PREFIX argument
401for \c{qt_add_qml_module()} defaults to \c{/qt/qml/}, therefore your
402modules are placed in \c{:/qt/qml/} in the resource file system.
403This is part of the default \l{QML Import Path}, but not used by Qt
404itself. For modules to be used within your application, this is the right place.
406If you have instead specified a custom \c RESOURCE_PREFIX, you have to add the
407custom resource prefix to the \l{QML Import Path}. You can also add multiple
412qmlEngine.addImportPath(QStringLiteral(":/my/resource/prefix"));
413qmlEngine.addImportPath(QStringLiteral(":/other/resource/prefix"));
414// Use qmlEngine to load the main.qml file.
417This might be necessary when using third party libraries to avoid module name
418conflicts. Using a custom resource prefix is discouraged in all other cases.
420The path \c :/qt-project.org/imports/ is also part of the default \l{QML Import
421Path}. For modules that are heavily re-used across different projects or Qt
422versions, \c :/qt-project.org/imports/ is acceptable as resource prefix. Qt's
423own QML modules are placed there, though. You have to be careful not to
426Do not add any unnecessary import paths. The QML engine might find your modules
427in the wrong place then. This can trigger problems which can only be reproduced
428in specific environments.
430\section1 Integrating custom QML plugins
432If you bundle an \l {QQuickImageProvider}{image provider} in the QML module, you
433need to implement the \l {QQmlEngineExtensionPlugin::initializeEngine()}
434method. This, in turn, makes it necessary to write your own plugin. To support
435this use case, \l NO_GENERATE_PLUGIN_SOURCE can be used.
437Let's consider a module that provides its own plugin source:
439\quotefile qml/myimageprovider.txt
441You may declare an image provider in myimageprovider.h, like this:
444class MyImageProvider : public QQuickImageProvider
450In plugin.cpp you can then define the QQmlEngineExtensionPlugin:
452\quotefile qml/plugin.cpp.txt
454This will make the image provider available. The plugin and the backing library
455both are in the same CMake target imageproviderplugin. This is done so that the
456linker does not drop parts of the module in various scenarios.
458As the plugin creates an image provider, it no longer has a trivial
459\c initializeEngine function. Therefore, the plugin is no longer optional.