Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
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
9
You 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
26
All the above actions can also be configured separately.
27
For more information, see \l {qt_add_qml_module}{CMake QML Module API}.
28
29
\section1 Multiple QML Modules in One Binary
30
31
You can add multiple QML modules into the same binary. Define a CMake target for
32
each module and then link the targets to the executable.
33
If the extra targets are all static libraries, the result will be one binary,
34
which contains multiple QML modules. In short you can create an application
35
like this:
36
37
\badcode
38
myProject
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
51
To begin, let's assume main.qml contains an instantiation of Extra.qml:
52
53
\badcode
54
import ExtraModule
55
Extra { ... }
56
\endcode
57
58
The extra module has to be a static library so that you can link it
59
into the main program. Therefore, state as much in ExtraModule/CMakeLists.txt:
60
61
\quotefromfile qml/CMakeLists.txt
62
\printuntil extrathing.h
63
\printuntil )
64
65
This 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
67
be loaded at runtime.
68
69
In myProject/CMakeLists.txt you need to specify the QML module that main.qml
70
and any types declared in onething.h are part of:
71
72
\quotefromfile qml/myProject-CMakeLists.txt
73
\printuntil onething.h
74
\printuntil )
75
76
77
From there, you add the subdirectory for the extra module:
78
79
\quotefromfile qml/CMakeLists.txt
80
\skipto add_subdirectory
81
\printuntil )
82
83
To 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
90
QML plugins contain a symbol you can use for this purpose.
91
You can use the \l Q_IMPORT_QML_PLUGIN macro to create a reference to this symbol.
92
Add the following code to the main.cpp:
93
94
\badcode
95
#include <QtQml/QQmlExtensionPlugin>
96
Q_IMPORT_QML_PLUGIN(ExtraModulePlugin)
97
\endcode
98
99
\c ExtraModulePlugin is the name of the generated plugin class. It's composed
100
of the module URI with \c Plugin appended to it. Then, in the main program's
101
CMakeLists.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 Versions
108
109
QML has a complex system to assign versions to components and modules. In most
110
cases you should ignore all of it by:
111
112
\list 1
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
119
\endlist
120
121
Versioning is ideally handled outside the language itself. You may, for example,
122
keep separate \l{QML Import Path}{import paths} for different sets of QML modules.
123
Or you may use a versioning mechanism provided by your operating system to install
124
or uninstall packages with QML modules.
125
126
In some cases, Qt's own QML modules may show different behavior, depending on what
127
version is imported. In particular, if a property is added to a QML component, and
128
your code contains unqualified access to another property of the same name, your
129
code will break. In the following example, the code will behave differently
130
depending on the version of Qt, because the \l [QML] {Rectangle::}{topLeftRadius}
131
property was added in Qt 6.7:
132
133
\qml
134
import QtQuick
135
136
Item {
137
// property you want to use
138
property real topLeftRadius: 24
139
140
Rectangle {
141
142
// correct for Qt version < 6.7 but uses Rectangle's topLeftRadius in 6.7
143
objectName: "top left radius:" + topLeftRadius
144
}
145
}
146
\endqml
147
148
The solution here is to avoid the unqualified access. \l{qmllint} can be used to
149
find such problems. The following example accesses the property you actually mean
150
in a safe, qualified way:
151
152
\qml
153
import QtQuick
154
155
Item {
156
id: root
157
158
// property you want to use
159
property real topLeftRadius: 24
160
161
Rectangle {
162
163
// never mixes up topLeftRadius with unrelated Rectangle's topLeftRadius
164
objectName: "top left radius:" + root.topLeftRadius
165
}
166
}
167
\endqml
168
169
You can also avoid the incompatibility by importing a specific version of QtQuick:
170
171
\qml
172
// make sure Rectangle has no topLeftRadius property
173
import QtQuick 6.6
174
175
Item {
176
property real topLeftRadius: 24
177
Rectangle {
178
objectName: "top left radius:" + topLeftRadius
179
}
180
}
181
\endqml
182
183
Another problem solved by versioning is the fact that QML components imported by
184
different modules may shadow each other. In the following example, if \c{MyModule} were
185
to introduce a component named \c{Rectangle} in a newer version, the \c{Rectangle}
186
created by this document would not be a \c {QQuickRectangle} anymore, but rather the
187
new \c{Rectangle} introduced by \c{MyModule}.
188
189
\qml
190
import QtQuick
191
import MyModule
192
193
Rectangle {
194
// MyModule's Rectangle, not QtQuick's
195
}
196
\endqml
197
198
A good way to avoid the shadowing would be to import \c{QtQuick} and/or \c{MyModule}
199
into type namespaces as follows:
200
201
\qml
202
import QtQuick as QQ
203
import MyModule as MM
204
205
QQ.Rectangle {
206
// QtQuick's Rectangle
207
}
208
\endqml
209
210
Alternatively, if you import \c{MyModule} with a fixed version, and the new component
211
receives a correct version tag via \l{QML_ADDED_IN_VERSION} or \l{QT_QML_SOURCE_VERSIONS},
212
the shadowing is also avoided:
213
214
\qml
215
import QtQuick 6.6
216
217
// Types introduced after 1.0 are not available, like Rectangle for example
218
import MyModule 1.0
219
220
Rectangle {
221
// QtQuick's Rectangle
222
}
223
\endqml
224
225
For this to work, you need to use versions in \c{MyModule}. There are a few things
226
to be aware of.
227
228
\section2 If you add versions, add them everywhere
229
230
You need to add a \c{VERSION} attribute to \l{qt_add_qml_module}. The version should
231
be the most recent version provided by your module. Older minor versions of the same
232
major version will automatically be registered. For older major versions, see
233
\l{#Exporting Multiple Major Versions from The Same Module}{below}.
234
235
You should add \l{QML_ADDED_IN_VERSION} or \l{QT_QML_SOURCE_VERSIONS} to every type
236
that was \e not introduced in version \c{x.0} of your module, where \c{x} is the current
237
major version.
238
239
If you forget to add a version tag, the component will be available in all versions,
240
making the versioning ineffective.
241
242
However, there is no way to add versions to properties, methods, and signals defined
243
in 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.
245
246
\section2 Versions are not transitive
247
248
If a component from your module \c{A} imports another module \c{B} and instantiates a type
249
from that module as the root element, then the import version of \c{B} is relevant for the
250
properties available from the resulting component, no matter what version of \c{A} is
251
imported by a user.
252
253
Consider a file \c{TypeFromA.qml} with version \c{2.6} in module \c{A}:
254
255
\qml
256
import B 2.7
257
258
// Exposes TypeFromB 2.7, no matter what version of A is imported
259
TypeFromB { }
260
\endqml
261
262
Now consider a user of \c{TypeFromA}:
263
264
\qml
265
import A 2.6
266
267
// This is TypeFromB 2.7.
268
TypeFromA { }
269
\endqml
270
271
The user hopes to see version \c{2.6} but actually gets version
272
\c{2.7} of the base class \c{TypeFromB}.
273
274
Therefore, in order to be safe, you not only have to duplicate your QML files and
275
give them new versions when you add properties yourself, but also when you bump
276
versions of modules you import.
277
278
\section2 Qualified access does not honor versioning
279
280
Versioning only affects unqualified access to members of a type or the type itself.
281
In the example with \c{topLeftRadius}, if you write \c{this.topLeftRadius}, the
282
property will be resolved if you're using Qt 6.7, even if you \c{import QtQuick 6.6}.
283
284
\section2 Versions and revisions
285
286
With \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
288
to the \l{QMetaObject}{metaobject's} revision as exposed in \l{QMetaMethod::revision}
289
and \l{QMetaProperty::revision}. This means all the types in your type hierarchy have
290
to follow the same versioning scheme. This includes any types provided by Qt itself
291
that you inherit from.
292
293
With \l qmlRegisterType and related functions you can register any mapping
294
between metaobject revisions and type versions. You then need to use the one-argument forms
295
of \l{Q_REVISION} and the \c{REVISION} attribute of \l{Q_PROPERTY}. However, this
296
can become rather complex and confusing and is not recommended.
297
298
\section2 Exporting multiple major versions from the same module
299
300
\l qt_add_qml_module by default considers the major version given in its
301
VERSION argument, even if the individual types declare other versions in their
302
added specific version via \l QT_QML_SOURCE_VERSIONS or \l Q_REVISION.
303
If a module is available under more than one version, you also need to decide
304
what versions the individual QML files are available under. To declare further
305
major 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
307
individual QML files.
308
309
\quotefile qml/MajorProject-CMakeLists.txt
310
311
\c MyModule is available in major versions 1, 2, and 3. The maximum version
312
available is 3.2. You can import any version 1.x or 2.x with a positive x. For
313
Thing.qml and OtherThing.qml we have added explicit version information.
314
Thing.qml is available from version 1.4, and OtherThing.qml is available from
315
version 2.2. You have to specify the later versions, too, in each
316
\c set_source_files_properties() because you may remove QML files
317
from a module when bumping the major version. There is no explicit version
318
information for OneMoreThing.qml. This means that OneMoreThing.qml is available
319
in all major versions, from minor version 0.
320
321
With this setup, the generated registration code will register the module
322
\c versions using \l qmlRegisterModule() for each of the major versions. This
323
way, all versions can be imported.
324
325
326
\section1 Custom Directory Layouts
327
328
The easiest way to structure QML modules is to keep them in directories named by
329
their URIs. For example, a module My.Extra.Module would live in a directory
330
My/Extra/Module relative to the application that uses it. This way, they can
331
easily be found at run time and by any tools.
332
333
In more complex projects, this convention can be too limiting. You might for
334
instance want to group all QML modules in one place to avoid polluting the
335
project's root directory. Or you want to reuse a single module in multiple
336
applications. For those cases, \c QT_QML_OUTPUT_DIRECTORY in combination with
337
\c RESOURCE_PREFIX and \l IMPORT_PATH can be used.
338
339
To collect QML modules into a specific output directory, for example a
340
subdirectory "qml" in the build directory \l QT_QML_OUTPUT_DIRECTORY, set the
341
following in the top-level CMakeLists.txt:
342
343
\badcode
344
set(QT_QML_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/qml)
345
\endcode
346
347
The output directories of QML modules move to the new location.
348
Likewise, the \c qmllint and \c qmlcachegen invocations are automatically
349
adapted to use the new output directory as an \l[QtQml]{QML Import Path}{import path}.
350
Because the new output directory is not part of the default QML import path,
351
you have to add it explicitly at run time, so that the QML modules can be found.
352
353
354
Now that the physical file system is taken care of, you may still want to move
355
the QML modules into a different place in the resource file system. This is what
356
the RESOURCE_PREFIX option is for. You have to specify it separately in
357
each \l qt_add_qml_module. The QML module will then be placed under the specified
358
prefix, with a target path generated from the URI appended. For example,
359
consider the following module:
360
361
\code
362
qt_add_qml_module(
363
URI My.Great.Module
364
VERSION 1.0
365
RESOURCE_PREFIX /example.com/qml
366
QML_FILES
367
A.qml
368
B.qml
369
)
370
\endcode
371
372
This will add a directory \c example.com/qml/My/Great/Module to the resource file
373
system and place the QML module defined above in it. You don't strictly need to
374
add the resource prefix to the QML import path as the module can still be found
375
in the physical file system. However, it generally is a good idea to add the
376
resource prefix to the QML import path because loading from the resource file
377
system is faster than loading from the physical file system for most modules.
378
379
If the QML modules are meant to be used in a larger project with multiple import
380
paths, you'll have to do an additional step: Even if you add the import paths at
381
run time, tooling like \c qmllint does not have access to it, and might fail to
382
find the correct dependencies. Use \c IMPORT_PATH to tell tooling about the
383
additional paths it has to consider. For example:
384
385
\badcode
386
qt_add_qml_module(
387
URI My.Dependent.Module
388
VERSION 1.0
389
QML_FILES
390
C.qml
391
IMPORT_PATH "/some/where/else"
392
)
393
\endcode
394
395
\section1 Eliminating Run Time File System Access
396
397
If all QML modules are always loaded from the resource
398
file system, you can deploy the application as a single binary.
399
400
If \l{QTP0001} policy is set to \c NEW, the \c RESOURCE_PREFIX argument
401
for \c{qt_add_qml_module()} defaults to \c{/qt/qml/}, therefore your
402
modules are placed in \c{:/qt/qml/} in the resource file system.
403
This is part of the default \l{QML Import Path}, but not used by Qt
404
itself. For modules to be used within your application, this is the right place.
405
406
If you have instead specified a custom \c RESOURCE_PREFIX, you have to add the
407
custom resource prefix to the \l{QML Import Path}. You can also add multiple
408
resource prefixes:
409
410
\badcode
411
QQmlEngine qmlEngine;
412
qmlEngine.addImportPath(QStringLiteral(":/my/resource/prefix"));
413
qmlEngine.addImportPath(QStringLiteral(":/other/resource/prefix"));
414
// Use qmlEngine to load the main.qml file.
415
\endcode
416
417
This might be necessary when using third party libraries to avoid module name
418
conflicts. Using a custom resource prefix is discouraged in all other cases.
419
420
The path \c :/qt-project.org/imports/ is also part of the default \l{QML Import
421
Path}. For modules that are heavily re-used across different projects or Qt
422
versions, \c :/qt-project.org/imports/ is acceptable as resource prefix. Qt's
423
own QML modules are placed there, though. You have to be careful not to
424
overwrite them.
425
426
Do not add any unnecessary import paths. The QML engine might find your modules
427
in the wrong place then. This can trigger problems which can only be reproduced
428
in specific environments.
429
430
\section1 Integrating custom QML plugins
431
432
If you bundle an \l {QQuickImageProvider}{image provider} in the QML module, you
433
need to implement the \l {QQmlEngineExtensionPlugin::initializeEngine()}
434
method. This, in turn, makes it necessary to write your own plugin. To support
435
this use case, \l NO_GENERATE_PLUGIN_SOURCE can be used.
436
437
Let's consider a module that provides its own plugin source:
438
439
\quotefile qml/myimageprovider.txt
440
441
You may declare an image provider in myimageprovider.h, like this:
442
443
\badcode
444
class MyImageProvider : public QQuickImageProvider
445
{
446
[...]
447
};
448
\endcode
449
450
In plugin.cpp you can then define the QQmlEngineExtensionPlugin:
451
452
\quotefile qml/plugin.cpp.txt
453
454
This will make the image provider available. The plugin and the backing library
455
both are in the same CMake target imageproviderplugin. This is done so that the
456
linker does not drop parts of the module in various scenarios.
457
458
As the plugin creates an image provider, it no longer has a trivial
459
\c initializeEngine function. Therefore, the plugin is no longer optional.
460
461
\sa {Changes to Qt QML}, {Modern QML modules}, {Port QML modules to CMake}
462
463
*/
qtdeclarative
src
qml
doc
src
qtqml-writing-a-module.qdoc
Generated on
for Qt by
1.14.0