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