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-qml-type-compiler.qdoc
Go to the documentation of this file.
1
// Copyright (C) 2022 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
3
4
/*!
5
\page qtqml-qml-type-compiler.html
6
\title QML type compiler
7
\brief A tool to compile QML types to C++ ahead of time.
8
\keyword qmltc
9
\ingroup qtqml-tooling
10
\ingroup qtqml-tooling-internal
11
12
The QML type compiler, \c qmltc, is a tool shipped with Qt to translate QML
13
types into C++ types that are \e{ahead-of-time} compiled as part of the user
14
code. Using qmltc can lead to better run-time performance due to more
15
optimization opportunities available to the compiler compared to a
16
QQmlComponent-based object creation. The qmltc is part of the \l{Qt Quick Compiler}
17
toolchain.
18
19
By design, qmltc outputs user-facing code. That code is supposed to be utilized
20
by the C++ application directly, otherwise you won't see any benefit. This
21
generated code essentially replaces QQmlComponent and its APIs to create objects
22
from QML documents. You can find more information under \l{Using qmltc in a QML
23
application} and \l{Generated Output Basics}.
24
25
In order to enable qmltc:
26
27
\list
28
\li Create a \l{qt_add_qml_module}{proper QML module} for your application.
29
30
\li Invoke qmltc, for example, through the \l{qmltc-cmake}{CMake API}.
31
32
\li \c{#include} the generated header file(s) in the application source
33
code.
34
35
\li Instantiate an object of the generated type.
36
\endlist
37
38
In this workflow qmltc usually runs during the build process. Thus, when qmltc
39
rejects a QML document (whether due to errors or warnings, or because of
40
constructs qmltc doesn't yet support), the build process will fail. This is
41
similar to how you receive qmllint errors when you enable the automatic
42
generation of linting targets during \l{qt_add_qml_module}{QML module creation}
43
and then attempt to "build" them to run the qmllint.
44
45
\warning qmltc is currently in a Tech Preview stage and might not compile an
46
arbitrary QML program (see \l{Known Limitations} for more details). When qmltc
47
fails, nothing is generated as your application cannot sensibly use the qmltc
48
output. If your program contains errors (or unsolvable warnings), they should be
49
fixed to enable the compilation. The general rule is to adhere to the best
50
practices and follow \l{qmllint} advice.
51
52
\note \c qmltc does not guarantee that the generated C++ stays API-, source- or
53
binary-compatible between past or future versions, even patch versions.
54
Furthermore, qmltc-compiled apps using Qt's QML modules will require linking
55
against private Qt API. This is because Qt's QML modules do not usually provide
56
a public C++ API since their primary usage is through QML.
57
58
59
\section2 Using qmltc in a QML application
60
61
From the build system perspective, adding qmltc compilation is not much
62
different from adding qml cache generation. Naively, the build process could be
63
described as:
64
65
\image qmltc-compilation-scheme.png {The flow chart shows how to compile qml
66
and C++ files with qml type compiler}
67
68
While the real compilation process is much trickier, this diagram captures the
69
core components that qmltc uses: the QML files themselves and qmldir with
70
qmltypes information. Simpler applications typically have rather primitive
71
qmldir yet, in general, qmldir could be complex, providing essential, nicely
72
packed type information that qmltc relies on to perform correct QML-to-C++
73
translation.
74
75
Nevertheless, adding an extra build step is not enough in qmltc case. The
76
application code must also be modified to use qmltc-generated classes instead of
77
QQmlComponent or its higher-level alternatives.
78
79
\section3 Compiling QML code with qmltc
80
81
Qt, starting from Qt 6, uses CMake to build its various components. User
82
projects can - and are encouraged to - also use CMake to build their components
83
using Qt. Adding out-of-the-box qmltc compilation support to your project would
84
require a CMake-driven build flow as well since this flow is centered around
85
proper QML modules and their infrastructure.
86
87
The easy way to add qmltc compilation is by using the dedicated
88
\l{qmltc-cmake}{CMake API} as part of a QML module creation for the application.
89
Consider a simple application directory structure:
90
91
\badcode
92
.
93
├── CMakeLists.txt
94
├── myspecialtype.h // C++ type exposed to QML
95
├── myspecialtype.cpp
96
├── myApp.qml // main QML page
97
├── MyButton.qml // custom UI button
98
├── MySlider.qml // custom UI slider
99
└── main.cpp // main C++ application file
100
\endcode
101
102
Then the CMake code would usually look similar to the following:
103
104
\snippet qmltc/CMakeLists.txt qmltc-app-name
105
\codeline
106
\snippet qmltc/CMakeLists.txt qmltc-qml-files
107
\codeline
108
\snippet qmltc/CMakeLists.txt qmltc-add-qml-module
109
\codeline
110
\snippet qmltc/CMakeLists.txt qmltc-compile-to-cpp
111
112
\section3 Using the Generated C++
113
114
Unlike in the case of QQmlComponent instantiation, the output of qmltc, being
115
C++ code, is used directly by the application. Generally, constructing a new
116
object in C++ is equivalent to creating a new object through
117
QQmlComponent::create(). Once created, the object could be manipulated from C++
118
or, for example, combined with QQuickWindow to be drawn on screen.
119
120
If a compiled type exposes some required properties, `qmltc` will
121
require an initial value for those properties in the constructor for
122
the generated object.
123
124
Additionally, the constructor for a qmltc object can be provided with
125
with a callback to set up initial values for the component's
126
properties.
127
128
Given a \c{myApp.qml} file, the application code (in both cases) would
129
typically look like this:
130
131
\if defined(onlinedocs)
132
\tab {generated-c++}{tab-qqmlcomponent}{Using QQmlComponent}{checked}
133
\tab {generated-c++}{tab-qmltc}{Using qmltc-generated class}{}
134
\tabcontent {tab-qqmlcomponent}
135
\else
136
\section4 Using QQmlComponent
137
\endif
138
\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-include
139
\codeline
140
\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-app-code-0
141
\codeline
142
\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-app-code-1
143
\codeline
144
\snippet qmltc/tst_qmltc_examples.cpp qqmlcomponent-app-code-2
145
\codeline
146
\snippet qmltc/tst_qmltc_examples.cpp qmltc-app-exec
147
\if defined(onlinedocs)
148
\endtabcontent
149
\tabcontent {tab-qmltc}
150
\else
151
\section4 Using qmltc-generated class
152
\endif
153
\snippet qmltc/tst_qmltc_examples.cpp qmltc-include
154
\codeline
155
\snippet qmltc/tst_qmltc_examples.cpp qmltc-app-code
156
\codeline
157
\snippet qmltc/tst_qmltc_examples.cpp qmltc-app-exec
158
\if defined(onlinedocs)
159
\endtabcontent
160
\endif
161
162
\section2 QML engine
163
164
The generated code uses QQmlEngine to interact with dynamic parts of a QML
165
document - mainly the JavaScript code. For this to work, no special arrangements
166
are needed. Any QQmlEngine instance passed to the constructor of a
167
qmltc-generated class object should work correctly as does
168
\c{QQmlComponent(engine)}. This also means that you can use
169
\l{QQmlEngine}{QQmlEngine methods} that affect QML behavior. However, there are
170
caveats. Unlike QQmlComponent-based object creation, qmltc itself \e{does not}
171
rely on QQmlEngine when compiling the code to C++. For instance,
172
\c{QQmlEngine::addImportPath("/foo/bar/")} - normally resulting in an additional
173
import path to scan for - would be completely ignored by the ahead-of-time qmltc
174
procedure.
175
176
\note To add import paths to the qmltc compilation, consider using a relevant
177
argument of the \l{qmltc-cmake}{CMake command} instead.
178
179
Generally, you can think of it this way: QQmlEngine involves the application
180
process to run, while qmltc does not as it operates \e{before} your application
181
is even compiled. Since qmltc makes no attempt to introspect your application's
182
C++ source code, there is no way for it to know about certain kinds of QML
183
manipulations you, as a user, do. Instead of using QQmlEngine and related
184
run-time routines to expose types to QML, adding import paths, etc. you are,
185
practically, required to create \l{qt_add_qml_module}{well-behaving QML modules}
186
and use \l{Defining QML Types from C++}{declarative QML type registration}.
187
188
\warning Despite qmltc working closely with QQmlEngine and creating C++ code,
189
the generated classes cannot be further exposed to QML and used through
190
QQmlComponent.
191
192
\section2 Generated Output Basics
193
194
\c qmltc aims to be compatible with the existing QML execution model. This
195
implies that the generated code is roughly equivalent to the internal
196
QQmlComponent setup logic and thus you should be able to understand your QML
197
type's behavior, semantics and API the same way you do currently - by visually
198
inspecting the corresponding QML document.
199
200
However, the generated code is still somewhat confusing, especially given that
201
your application should use the qmltc output on the C++ side directly. There are
202
two parts of the generated code: CMake build files structure and the generated
203
C++ format. The former is covered in the \l{qmltc-cmake}{CMake API of qmltc} and
204
the latter is covered here.
205
206
Consider a simple HelloWorld type, that has a \c hello property, a function to
207
print that property, and a signal emitted when the object of that type is
208
created:
209
210
\snippet qmltc/special/HelloWorld.qml qmltc-hello-world-qml
211
212
When providing a C++ alternative of this QML type, the C++ class would need a
213
\l{Overview - QML and C++ Integration}{QML-specific meta-object system macro},
214
Q_PROPERTY decoration for the \c hello property, \c{Q_INVOKABLE} C++ printing
215
function and a regular Qt signal definition. Similarly, qmltc would translate
216
the given HelloWorld type into roughly the following:
217
218
\snippet qmltc/special/HelloWorld.qml.cpp qmltc-hello-world-generated
219
220
Even though specific details of the generated type could differ, the universal
221
aspects remain. For instance:
222
223
\list
224
\li QML types within a document are translated into C++ types, according to
225
the compiler-visible information.
226
\li Properties are translated into C++ properties with Q_PROPERTY
227
declarations.
228
\li JavaScript functions become \c{Q_INVOKABLE} C++ functions.
229
\li QML signals are transformed into C++ Qt signals.
230
\li QML enumerations are converted into C++ enumerations with \c{Q_ENUM}
231
declarations.
232
\endlist
233
234
An additional detail is the way \c qmltc generates class names. A class name for
235
a given QML type is automatically deduced from the QML document defining that
236
type: the QML file name without extensions (up to and excluding the first \c{.},
237
also known as the base name) becomes a class name. The file name case is
238
preserved. Thus, \c{HelloWorld.qml} would result in a \c{class HelloWorld} and
239
\c{helloWoRlD.qml} in a \c{class helloWoRlD}. Following the QML convention, if a
240
QML document file name starts with a lower-case letter, the generated C++ class
241
is assumed to be anonymous and marked with \l{QML_ANONYMOUS}.
242
243
For now, although the generated code is ready to be used from the C++
244
application side, you should generally limit calls to the generated APIs.
245
Instead, prefer implementing the application logic in QML/JavaScript and
246
hand-written C++ types exposed to QML, using the qmltc-created classes for
247
simple object instantiation. While generated C++ gives you direct (and usually
248
faster) access to QML-defined elements of the type, understanding such code
249
could be a challenge.
250
251
\section2 Known Limitations
252
253
Despite covering many common QML features, qmltc is still in the early stage of
254
development with some things yet to be supported.
255
256
Imported QML modules that consist of QML-defined types (such as
257
\c{QtQuick.Controls}) might not get compiled correctly, even if those QML-defined
258
types were compiled by qmltc..
259
At present, you can reliably use \c{QtQml} and \c{QtQuick} modules as well as any
260
other QML module that \b{only} contains C++ classes exposed to QML.
261
262
On top of this, there are some more fundamental peculiarities to consider:
263
264
\list
265
\li Qt's QML modules usually rely on C++ libraries to do the heavy lifting.
266
Often enough, these libraries do not provide public C++ API (since their
267
primary usage is through QML). For the users of qmltc, this means that their
268
apps need to link against private Qt libraries.
269
270
\li Due to the nature of qmltc code generation, QML plugins are unusable for
271
compilation purposes. Instead, QML modules - that use a plugin - have to
272
ensure that the plugin data is accessible at compile time. Such QML modules
273
would then have \e optional plugins. In most cases, the compile-time
274
information can be provided through a header file (with C++ declarations)
275
and linkable library (with C++ definitions). The user code is responsible
276
(usually through CMake) for including a path to the header file and linking
277
against the QML module library.
278
\endlist
279
280
\note
281
Given the tech preview status of the compiler, you might also encounter bugs in
282
qmltc, in the generated code, or some other related part. We encourage you to
283
\l{https://bugreports.qt.io/}{submit a bug report} in this case.
284
285
*/
qtdeclarative
src
qml
doc
src
tools
qtquickcompiler
qtqml-qml-type-compiler.qdoc
Generated on
for Qt by
1.14.0