Qt 6.x
The Qt SDK
Loading...
Searching...
No Matches
qtqml-writing-a-module.qdoc
Go to the documentation of this file.
1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
3
4/*!
5\page qtqml-writing-a-module.html
6\title Writing QML Modules
7\brief How to write a custom QML module.
8
9You should declare a QML module using the \l{qt_add_qml_module}
10{CMake QML Module API} to:
11
12\list
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
18 QML files.
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.
24\endlist
25
26All the above actions can also be configured separately.
27For more information, see \l {qt_add_qml_module}{CMake QML Module API}.
28
29\section1 Multiple QML Modules in One Binary
30
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
35like this:
36
37\badcode
38myProject
39 | - CMakeLists.txt
40 | - main.cpp
41 | - main.qml
42 | - onething.h
43 | - onething.cpp
44 | - ExtraModule
45 | - CMakeLists.txt
46 | - Extra.qml
47 | - extrathing.h
48 | - extrathing.cpp
49\endcode
50
51To begin, let's assume main.qml contains an instantiation of Extra.qml:
52
53 \badcode
54 import ExtraModule
55 Extra { ... }
56 \endcode
57
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:
60
61\quotefromfile qml/CMakeLists.txt
62\printuntil extrathing.h
63\printuntil )
64
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
67be loaded at runtime.
68
69In myProject/CMakeLists.txt you need to specify the QML module that main.qml
70and any types declared in onething.h are part of:
71
72\quotefromfile qml/myProject-CMakeLists.txt
73\printuntil onething.h
74\printuntil )
75
76
77From there, you add the subdirectory for the extra module:
78
79\quotefromfile qml/CMakeLists.txt
80\skipto add_subdirectory
81\printuntil )
82
83To ensure that linking the extra module works correctly, you need to:
84
85\list
86\li Define a symbol in the extra module.
87\li Create a reference to the symbol from the main program.
88\endlist
89
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:
93
94\badcode
95#include <QtQml/QQmlExtensionPlugin>
96Q_IMPORT_QML_PLUGIN(ExtraModulePlugin)
97\endcode
98
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:
102
103\quotefromfile qml/myProject-CMakeLists.txt
104\skipto target_link_libraries
105\printuntil )
106
107\section1 Exporting Multiple Major Versions from The Same Module
108
109\l qt_add_qml_module by default considers the major version given in its
110URI argument, even if the individual types declare other versions in their
111added specific version via \l QT_QML_SOURCE_VERSIONS or \l Q_REVISION.
112If a module is available under more than one version, you also need to decide
113what versions the individual QML files are available under. To declare further
114major versions, you can use the \c PAST_MAJOR_VERSIONS option to
115\c qt_add_qml_module as well as the \c {QT_QML_SOURCE_VERSIONS} property on
116individual QML files.
117
118\quotefile qml/MajorProject-CMakeLists.txt
119
120\c MyModule is available in major versions 1, 2, and 3. The maximum version
121available is 3.2. You can import any version 1.x or 2.x with a positive x. For
122Thing.qml and OtherThing.qml we have added explicit version information.
123Thing.qml is available from version 1.4, and OtherThing.qml is available from
124version 2.2. You have to specify the later versions, too, in each
125\c set_source_files_properties() because you may remove QML files
126from a module when bumping the major version. There is no explicit version
127information for OneMoreThing.qml. This means that OneMoreThing.qml is available
128in all major versions, from minor version 0.
129
130With this setup, the generated registration code will register the module
131\c versions using \l qmlRegisterModule() for each of the major versions. This
132way, all versions can be imported.
133
134
135\section1 Custom Directory Layouts
136
137The easiest way to structure QML modules is to keep them in directories named by
138their URIs. For example, a module My.Extra.Module would live in a directory
139My/Extra/Module relative to the application that uses it. This way, they can
140easily be found at run time and by any tools.
141
142In more complex projects, this convention can be too limiting. You might for
143instance want to group all QML modules in one place to avoid polluting the
144project's root directory. Or you want to reuse a single module in multiple
145applications. For those cases, \c QT_QML_OUTPUT_DIRECTORY in combination with
146\c RESOURCE_PREFIX and \l IMPORT_PATH can be used.
147
148To collect QML modules into a specific output directory, for example a
149subdirectory "qml" in the build directory \l QT_QML_OUTPUT_DIRECTORY, set the
150following in the top-level CMakeLists.txt:
151
152\badcode
153set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml)
154\endcode
155
156The output directories of QML modules move to the new location.
157Likewise, the \c qmllint and \c qmlcachegen invocations are automatically
158adapted to use the new output directory as an \l[QtQml]{QML Import Path}{import path}.
159Because the new output directory is not part of the default QML import path,
160you have to add it explicitly at run time, so that the QML modules can be found.
161
162
163Now that the physical file system is taken care of, you may still want to move
164the QML modules into a different place in the resource file system. This is what
165the RESOURCE_PREFIX option is for. You have to specify it separately in
166each \l qt_add_qml_module. The QML module will then be placed under the specified
167prefix, with a target path generated from the URI appended. For example,
168consider the following module:
169
170\code
171qt_add_qml_module(
172 URI My.Great.Module
173 VERSION 1.0
174 RESOURCE_PREFIX /example.com/qml
175 QML_FILES
176 A.qml
177 B.qml
178)
179\endcode
180
181This will add a directory \c example.com/qml/My/Great/Module to the resource file
182system and place the QML module defined above in it. You don't strictly need to
183add the resource prefix to the QML import path as the module can still be found
184in the physical file system. However, it generally is a good idea to add the
185resource prefix to the QML import path because loading from the resource file
186system is faster than loading from the physical file system for most modules.
187
188If the QML modules are meant to be used in a larger project with multiple import
189paths, you'll have to do an additional step: Even if you add the import paths at
190run time, tooling like \c qmllint does not have access to it, and might fail to
191find the correct dependencies. Use \c IMPORT_PATH to tell tooling about the
192additional paths it has to consider. For example:
193
194\badcode
195qt_add_qml_module(
196 URI My.Dependent.Module
197 VERSION 1.0
198 QML_FILES
199 C.qml
200 IMPORT_PATH "/some/where/else"
201)
202\endcode
203
204\section1 Eliminating Run Time File System Access
205
206If all QML modules are always loaded from the resource
207file system, you can deploy the application as a single binary.
208
209If \l{QTP0001} policy is set to \c NEW, the \c RESOURCE_PREFIX argument
210for \c{qt_add_qml_module()} defaults to \c{/qt/qml/}, therefore your
211modules are placed in \c{:/qt/qml/} in the resource file system.
212This is part of the default \l{QML Import Path}, but not used by Qt
213itself. For modules to be used within your application, this is the right place.
214
215If you have instead specified a custom \c RESOURCE_PREFIX, you have to add the
216custom resource prefix to the \l{QML Import Path}. You can also add multiple
217resource prefixes:
218
219\badcode
220QQmlEngine qmlEngine;
221qmlEngine.addImportPath(QStringLiteral(":/my/resource/prefix"));
222qmlEngine.addImportPath(QStringLiteral(":/other/resource/prefix"));
223// Use qmlEngine to load the main.qml file.
224\endcode
225
226This might be necessary when using third party libraries to avoid module name
227conflicts. Using a custom resource prefix is discouraged in all other cases.
228
229The path \c :/qt-project.org/imports/ is also part of the default \l{QML Import
230Path}. For modules that are heavily re-used across different projects or Qt
231versions, \c :/qt-project.org/imports/ is acceptable as resource prefix. Qt's
232own QML modules are placed there, though. You have to be careful not to
233overwrite them.
234
235Do not add any unnecessary import paths. The QML engine might find your modules
236in the wrong place then. This can trigger problems which can only be reproduced
237in specific environments.
238
239\section1 Integrating custom QML plugins
240
241If you bundle an \l {QQuickImageProvider}{image provider} in the QML module, you
242need to implement the \l {QQmlEngineExtensionPlugin::initializeEngine()}
243method. This, in turn, makes it necessary to write your own plugin. To support
244this use case, \l NO_GENERATE_PLUGIN_SOURCE can be used.
245
246Let's consider a module that provides its own plugin source:
247
248\quotefile qml/myimageprovider.txt
249
250You may declare an image provider in myimageprovider.h, like this:
251
252\badcode
253class MyImageProvider : public QQuickImageProvider
254{
255 [...]
256};
257\endcode
258
259In plugin.cpp you can then define the QQmlEngineExtensionPlugin:
260
261\quotefile qml/plugin.cpp.txt
262
263This will make the image provider available. The plugin and the backing library
264both are in the same CMake target imageproviderplugin. This is done so that the
265linker does not drop parts of the module in various scenarios.
266
267As the plugin creates an image provider, it no longer has a trivial
268\c initializeEngine function. Therefore, the plugin is no longer optional.
269
270*/