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
extending-tutorial.qdoc
Go to the documentation of this file.
1
// Copyright (C) 2021 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
3
4
/*!
5
\page qtqml-tutorials-extending-qml-example.html
6
\title Writing QML Extensions with C++
7
\brief Tutorial about extending QML with Qt C++.
8
9
The \l {Qt Qml} module provides a set of APIs for extending QML through
10
C++ extensions. You can write extensions to add your own QML types, extend existing
11
Qt types, or call C/C++ functions that are not accessible from ordinary QML code.
12
13
This tutorial shows how to write a QML extension using C++ that includes
14
core QML features, including properties, signals and bindings. It also shows how
15
extensions can be deployed through plugins.
16
17
Many of the topics covered in this tutorial are documented in further detail in
18
\l{Overview - QML and C++ Integration} and its documentation sub-topics. In
19
particular, you may be interested in the sub-topics
20
\l{qtqml-cppintegration-exposecppattributes.html}{Exposing Attributes of C++ Classes to QML}
21
and \l {qtqml-cppintegration-definetypes.html}{Defining QML Types from C++}.
22
23
\section1 Opening the Tutorial Sources
24
25
The code in this tutorial is available as part of the Qt sources.
26
If you installed Qt with the \QOI, you can
27
find the sources in the Qt installation directory under
28
Examples/Qt-\QtVersion/qml/tutorials/extending-qml/.
29
30
\section1 Creating Project from Scratch
31
32
Alternatively, you can follow the tutorial by creating the sources from scratch:
33
For each chapter, create a new project using the \e {Qt Quick Application} template
34
in \QC, as instructed in \l {\QC: Create Qt Quick Applications}.
35
Then follow along by adapting and extending the generated skeleton code.
36
37
\section1 Chapter 1: Creating a New Type
38
\c extending-qml/chapter1-basics
39
40
A common task when extending QML is to provide a new QML type that supports some
41
custom functionality beyond what is provided by the built-in \l {Qt Quick QML Types}{Qt Quick types}.
42
For example, this could be done to implement particular data models, or provide
43
types with custom painting and drawing capabilities, or access system features
44
like network programming that are not accessible through built-in QML features.
45
46
In this tutorial, we will show how to use the C++ classes in the Qt Quick
47
module to extend QML. The end result will be a simple Pie Chart display implemented by
48
several custom QML types connected together through QML features like bindings and
49
signals, and made available to the QML runtime through a plugin.
50
51
To begin with, let's create a new QML type called "PieChart" that has two properties: a name
52
and a color. We will make it available in an importable type namespace called "Charts", with
53
a version of 1.0.
54
55
We want this \c PieChart type to be usable from QML like this:
56
57
\qml
58
import Charts
59
60
PieChart {
61
width: 100; height: 100
62
name: "A simple pie chart"
63
color: "red"
64
}
65
\endqml
66
\note You might need to \l {\QC: Configure \QMLLS}
67
{enable the QML Language Server and its semantic highlighting} to make Qt Creator highlight
68
\c{PieChart} correctly.
69
70
71
To do this, we need a C++ class that encapsulates this \c PieChart type and its
72
properties. Since QML makes extensive use of Qt's \l{Meta-Object System}{meta object system},
73
this new class must:
74
75
\list
76
\li Inherit from QObject
77
\li Declare its properties using the Q_PROPERTY macro
78
\endlist
79
80
\section2 Class Declaration
81
82
Here is our \c PieChart class, defined in \c piechart.h:
83
84
\snippet tutorials/extending-qml/chapter1-basics/piechart.h 0
85
86
The class inherits from QQuickPaintedItem because we want to override
87
QQuickPaintedItem::paint() to perform drawing operations with the QPainter API.
88
If the class just represented some data type and was not an item that actually needed
89
to be displayed, it could simply inherit from QObject. Or, if we want to extend the
90
functionality of an existing QObject-based class, it could inherit from that class instead.
91
Alternatively, if we want to create a visual item that doesn't need to perform drawing
92
operations with the QPainter API, we can just subclass QQuickItem.
93
94
The \c PieChart class defines the two properties, \c name and \c color, with the
95
Q_PROPERTY macro, and overrides QQuickPaintedItem::paint(). The \c PieChart
96
class is registered using the QML_ELEMENT macro, to allow it to be used from
97
QML. If you don't register the class, \c App.qml won't be able to create a
98
\c PieChart.
99
100
\section2 qmake Setup
101
102
For the registration to take effect, the \c qmltypes option is added to
103
\c CONFIG in the project file and a \c QML_IMPORT_NAME and
104
\c QML_IMPORT_MAJOR_VERSION are given:
105
106
\snippet tutorials/extending-qml/chapter1-basics/chapter1-basics.pro 0
107
108
Additionally, a \l{Module Definition qmldir Files}{qmldir} file needs to be added manually to
109
create a \l{Writing QML Modules}{QML module}.
110
111
\quotefile tutorials/extending-qml/chapter1-basics/qmldir
112
113
\section2 CMake Setup
114
115
For the registration to take effect when using CMake, use the
116
\l{qt6_add_qml_module} {qt_add_qml_module} command:
117
118
\snippet tutorials/extending-qml/chapter1-basics/CMakeLists.txt 0
119
120
The \l{qt6_add_qml_module}{qt_add_qml_module} API automatically generates a
121
\c qmldir file for the QML module.
122
123
\section2 Class Implementation
124
125
The class implementation in \c piechart.cpp simply sets and returns the
126
\c m_name and \c m_color values as appropriate, and implements \c paint() to
127
draw a simple pie chart:
128
129
\snippet tutorials/extending-qml/chapter1-basics/piechart.cpp 0
130
\dots 0
131
\snippet tutorials/extending-qml/chapter1-basics/piechart.cpp 1
132
133
\section2 QML Usage
134
135
Now that we have defined the \c PieChart type, we will use it from QML. The \c
136
App.qml file creates a \c PieChart item and displays the pie chart's details
137
using a standard QML \l Text item:
138
139
\snippet tutorials/extending-qml/chapter1-basics/App.qml 0
140
141
Notice that although the color is specified as a string in QML, it is automatically
142
converted to a QColor object for the PieChart \c color property. Automatic conversions are
143
provided for various other \l {QML Value Types}{value types}. For example, a string
144
like "640x480" can be automatically converted to a QSize value.
145
146
We'll also create a C++ application that uses a QQuickView to run and
147
display \c App.qml.
148
149
Here is the application \c main.cpp:
150
151
\snippet tutorials/extending-qml/chapter1-basics/main.cpp 0
152
153
\section2 Project Build
154
155
To build the project we include the files, link against the libraries, and
156
define a type namespace called "Charts" with version 1.0 for any types exposed
157
to QML.
158
159
Using qmake:
160
161
\quotefile tutorials/extending-qml/chapter1-basics/chapter1-basics.pro
162
163
Using CMake:
164
165
\quotefile tutorials/extending-qml/chapter1-basics/CMakeLists.txt
166
167
Now we can build and run the application:
168
169
\image extending-tutorial-chapter1.png {Application creates the pie chart with
170
properties defined by the pie chart type}
171
172
\note You may see a warning \e {Expression ... depends on non-bindable properties:
173
PieChart::name}. This happens because we add a binding to the writable \c name
174
property, but haven't yet defined a notify signal for it. The QML engine therefore
175
cannot update the binding if the \c name value changes. This is addressed in
176
the following chapters.
177
178
\section1 Chapter 2: Connecting to C++ Methods and Signals
179
\c extending-qml/chapter2-methods
180
181
Suppose we want \c PieChart to have a "clearChart()" method that erases the
182
chart and then emits a "chartCleared" signal. Our \c App.qml would be able
183
to call \c clearChart() and receive \c chartCleared() signals like this:
184
185
\snippet tutorials/extending-qml/chapter2-methods/App.qml 0
186
187
\image extending-tutorial-chapter2.png {User can click anywhere on the
188
application window to clear the chart. This invokes the Clear Chart
189
method}
190
191
To do this, we add a \c clearChart() method and a \c chartCleared() signal
192
to our C++ class:
193
194
\snippet tutorials/extending-qml/chapter2-methods/piechart.h 0
195
\dots
196
\snippet tutorials/extending-qml/chapter2-methods/piechart.h 1
197
\dots
198
\snippet tutorials/extending-qml/chapter2-methods/piechart.h 2
199
\dots
200
\snippet tutorials/extending-qml/chapter2-methods/piechart.h 3
201
202
The use of Q_INVOKABLE makes the \c clearChart() method available to the
203
Qt Meta-Object system, and in turn, to QML.
204
205
\note You can also declare the method as a Qt slot instead of using Q_INVOKABLE,
206
because public and protected slots are also callable from QML (you cannot call
207
private slots).
208
209
The \c clearChart() method changes the color to Qt::transparent,
210
repaints the chart, then emits the \c chartCleared() signal:
211
212
\snippet tutorials/extending-qml/chapter2-methods/piechart.cpp 0
213
214
Now when we run the application and click the window, the pie chart
215
disappears, and the application outputs:
216
217
\badcode
218
qml: The chart has been cleared
219
\endcode
220
221
222
223
\section1 Chapter 3: Adding Property Bindings
224
\c extending-qml/chapter3-bindings
225
226
Property binding is a powerful feature of QML that allows values of different
227
types to be synchronized automatically. It uses signals to notify and update
228
other types' values when property values are changed.
229
230
Let's enable property bindings for the \c color property. That means
231
if we have code like this:
232
233
\snippet tutorials/extending-qml/chapter3-bindings/App.qml 0
234
235
\image extending-tutorial-chapter3.png {Pie chart B's color property is bound
236
to pie chart A's color property.}
237
238
The "color: chartA.color" statement binds the \c color value of
239
\c chartB to the \c color of \c chartA.
240
Whenever \c chartA's \c color value changes, \c chartB's \c color value
241
updates to the same value. When the window is clicked, the \c onClicked
242
handler in the MouseArea changes the color of \c chartA, thereby changing
243
both charts to the color blue.
244
245
It's easy to enable property binding for the \c color property.
246
We add a \l{Qt's Property System}{NOTIFY} feature to its Q_PROPERTY() declaration to indicate that a "colorChanged" signal
247
is emitted whenever the value changes.
248
249
\snippet tutorials/extending-qml/chapter3-bindings/piechart.h 0
250
\dots
251
\snippet tutorials/extending-qml/chapter3-bindings/piechart.h 1
252
\dots
253
\snippet tutorials/extending-qml/chapter3-bindings/piechart.h 2
254
\dots
255
\snippet tutorials/extending-qml/chapter3-bindings/piechart.h 3
256
257
Then, we emit this signal in \c setColor():
258
259
\snippet tutorials/extending-qml/chapter3-bindings/piechart.cpp 0
260
261
It's important for \c setColor() to check that the color value has actually changed
262
before emitting \c colorChanged(). This ensures the signal is not emitted unnecessarily and
263
also prevents loops when other types respond to the value change.
264
265
The use of bindings is essential to QML. You should always add NOTIFY
266
signals for properties if they are able to be implemented, so that your
267
properties can be used in bindings. Properties that cannot be bound cannot be
268
automatically updated and cannot be used as flexibly in QML. Also, since
269
bindings are invoked so often and relied upon in QML usage, users of your
270
custom QML types may see unexpected behavior if bindings are not implemented.
271
272
273
274
\section1 Chapter 4: Using Custom Property Types
275
276
\c extending-qml/chapter4-customPropertyTypes
277
278
The \c PieChart type currently has a string-type property and a color-type property.
279
It could have many other types of properties. For example, it could have an
280
int-type property to store an identifier for each chart:
281
282
\code
283
// C++
284
class PieChart : public QQuickPaintedItem
285
{
286
Q_PROPERTY(int chartId READ chartId WRITE setChartId NOTIFY chartIdChanged)
287
...
288
289
public:
290
void setChartId(int chartId);
291
int chartId() const;
292
...
293
294
signals:
295
void chartIdChanged();
296
};
297
298
// QML
299
PieChart {
300
...
301
chartId: 100
302
}
303
\endcode
304
305
Aside from \c int, we could use various other property types. Many of the Qt
306
data types such as QColor, QSize and QRect are automatically supported from QML.
307
(See \l {Data Type Conversion Between QML and C++} documentation for a full list.)
308
309
If we want to create a property whose type is not supported by QML by default,
310
we need to register the type with the QML engine.
311
312
For example, let's replace the use of the \c property with a type called
313
"PieSlice" that has a \c color property. Instead of assigning a color,
314
we assign an \c PieSlice value which itself contains a \c color:
315
316
\snippet tutorials/extending-qml/chapter4-customPropertyTypes/App.qml 0
317
318
Like \c PieChart, this new \c PieSlice type inherits from QQuickPaintedItem and declares
319
its properties with Q_PROPERTY():
320
321
\snippet tutorials/extending-qml/chapter4-customPropertyTypes/pieslice.h 0
322
323
To use it in \c PieChart, we modify the \c color property declaration
324
and associated method signatures:
325
326
\snippet tutorials/extending-qml/chapter4-customPropertyTypes/piechart.h 0
327
\dots
328
\snippet tutorials/extending-qml/chapter4-customPropertyTypes/piechart.h 1
329
\dots
330
\snippet tutorials/extending-qml/chapter4-customPropertyTypes/piechart.h 2
331
\dots
332
\snippet tutorials/extending-qml/chapter4-customPropertyTypes/piechart.h 3
333
334
There is one thing to be aware of when implementing \c setPieSlice(). The \c PieSlice
335
is a visual item, so it must be set as a child of the \c PieChart using
336
QQuickItem::setParentItem() so that the \c PieChart knows to paint this child
337
item when its contents are drawn:
338
339
\snippet tutorials/extending-qml/chapter4-customPropertyTypes/piechart.cpp 0
340
341
Like the \c PieChart type, the \c PieSlice type has to be exposted to QML
342
using QML_ELEMENT.
343
344
\snippet tutorials/extending-qml/chapter4-customPropertyTypes/pieslice.h 0
345
\dots
346
347
As with \c PieChart, we add the "Charts" type namespace, version 1.0, to our
348
build file:
349
350
Using qmake:
351
352
\quotefile tutorials/extending-qml/chapter4-customPropertyTypes/chapter4-customPropertyTypes.pro
353
354
Using CMake:
355
356
\dots
357
\snippet tutorials/extending-qml/chapter4-customPropertyTypes/CMakeLists.txt 0
358
\snippet tutorials/extending-qml/chapter4-customPropertyTypes/CMakeLists.txt 1
359
\dots
360
361
362
363
\section1 Chapter 5: Using List Property Types
364
\c extending-qml/chapter5-listproperties
365
366
Right now, a \c PieChart can only have one \c PieSlice. Ideally a chart would
367
have multiple slices, with different colors and sizes. To do this, we could
368
have a \c slices property that accepts a list of \c PieSlice items:
369
370
\snippet tutorials/extending-qml/chapter5-listproperties/App.qml 0
371
372
\image extending-tutorial-chapter5.png {The slices property accepts a list of
373
pie slice items. The pieslice item sets the angle and color of each pie slice}
374
375
To do this, we replace the \c pieSlice property in \c PieChart with a \c slices property,
376
declared as a \l QQmlListProperty type. The \l QQmlListProperty class enables the
377
creation of list properties in types exposed to QML. We replace the \c pieSlice()
378
function with a \c slices() function that returns a list of slices. We also use
379
a QList to store the internal list of slices as \c m_slices:
380
381
\snippet tutorials/extending-qml/chapter5-listproperties/piechart.h 0
382
\dots
383
\snippet tutorials/extending-qml/chapter5-listproperties/piechart.h 1
384
\dots
385
\snippet tutorials/extending-qml/chapter5-listproperties/piechart.h 2
386
387
Although the \c slices property does not have an associated \c WRITE function,
388
it is still modifiable because of the way \l QQmlListProperty works.
389
In the \c PieChart implementation, we implement \c PieChart::slices() to
390
return a \l QQmlListProperty value:
391
392
\snippet tutorials/extending-qml/chapter5-listproperties/piechart.cpp 0
393
394
This synthesizes the necessary functions to interact with the list from QML.
395
The resulting \l QQmlListProperty is a \e view into the list. Alternately, you can
396
manually provide the individual access functions for the list. This is necessary
397
if your list is not a \l QList or if you want to restrict or otherwise customize
398
QML access to your list. In most cases, however, the constructor taking a
399
\l QList pointer is the safest and easiest option.
400
401
The \c PieSlice class has also been modified to include \c fromAngle and \c angleSpan
402
properties and to draw the slice according to these values. This is a straightforward
403
modification if you have read the previous pages in this tutorial, so the code is not shown here.
404
405
406
407
\section1 Chapter 6: Writing an Extension Plugin
408
409
\c extending-qml/chapter6-plugins
410
411
Currently the \c PieChart and \c PieSlice types are used by \c App.qml,
412
which is displayed using a QQuickView in a C++ application. An alternative
413
way to use our QML extension is to create a plugin library to make it available
414
to the QML engine as a new QML import module. This allows the \c PieChart and
415
\c PieSlice types to be registered into a type namespace which can be imported
416
by any QML application, instead of restricting these types to be only used by
417
the one application.
418
419
The steps for creating a plugin are described in \l {Creating C++ Plugins for QML}.
420
To start with, we create a plugin class named \c ChartsPlugin. It subclasses
421
QQmlEngineExtensionPlugin and uses the Q_PLUGIN_METADATA() macro to register the
422
plugin with the Qt meta object system.
423
424
Here is the \c ChartsPlugin definition in \c chartsplugin.h:
425
426
\snippet tutorials/extending-qml/chapter6-plugins/Charts/chartsplugin.h 0
427
428
Then, we configure the build file to define the project as a plugin library.
429
430
Using qmake:
431
432
\quotefile tutorials/extending-qml/chapter6-plugins/Charts/Charts.pro
433
434
Using CMake:
435
436
\quotefile tutorials/extending-qml/chapter6-plugins/Charts/CMakeLists.txt
437
438
When building this example on Windows or Linux, the \c Charts directory will be
439
located at the same level as the application that uses our new import module.
440
This way, the QML engine will find our module as the default search path for QML
441
imports includes the directory of the application executable. On \macos, the
442
plugin binary is copied to \c Contents/PlugIns in the application bundle.
443
With qmake, this path is set in \c {chapter6-plugins/app.pro}:
444
445
\quotefromfile tutorials/extending-qml/chapter6-plugins/app.pro
446
\skipto macos
447
\printuntil }
448
449
To account for this, we also need to add this location as a
450
\l {QML Import Path}{QML import path} in \c main.cpp:
451
452
\snippet tutorials/extending-qml/chapter6-plugins/main.cpp 0
453
\dots
454
455
Defining custom import paths is useful also when there are multiple
456
applications using the same QML imports.
457
458
The \c .pro file also contains additional magic to ensure that the
459
\l {Module Definition qmldir Files}{module definition qmldir file} is always copied
460
to the same location as the plugin binary.
461
462
The \c qmldir file declares the module name and the plugin that is made available
463
by the module:
464
465
\quotefile tutorials/extending-qml/chapter6-plugins/Charts/qmldir
466
467
Now we have a QML module that can be imported to any application, provided that the
468
QML engine knows where to find it. The example contains an executable that loads
469
\c App.qml, which uses the \c {import Charts 1.0} statement. Alternatively, you can
470
load the QML file using the \l {Prototyping with the QML Runtime Tool}{qml tool},
471
setting the import path to the current directory so that it finds the \c qmldir file:
472
473
\code
474
qml -I . App.qml
475
\endcode
476
477
The module "Charts" will be loaded by the QML engine, and the types provided by that
478
module will be available for use in any QML document which imports it.
479
480
481
482
\section1 Chapter 7: Summary
483
484
In this tutorial, we've shown the basic steps for creating a QML extension:
485
486
\list
487
\li Define new QML types by subclassing QObject and registering them with
488
QML_ELEMENT or QML_NAMED_ELEMENT()
489
\li Add callable methods using \l Q_INVOKABLE or Qt slots, and connect to Qt signals
490
with an \c onSignal syntax
491
\li Add property bindings by defining \l{Qt's Property System}{NOTIFY} signals
492
\li Define custom property types if the built-in types are not sufficient
493
\li Define list property types using QQmlListProperty
494
\li Create a plugin library by defining a Qt plugin and writing a
495
\l {Module Definition qmldir Files}{qmldir} file
496
\endlist
497
498
The \l{Overview - QML and C++ Integration}{QML and C++ Integration overview}
499
documentation shows other useful features that can be added to QML extensions.
500
For example, we could use
501
\l{QML Object Attributes#Default Properties}{default properties} to allow
502
slices to be added without using the \c slices property:
503
504
\badcode
505
PieChart {
506
PieSlice { ... }
507
PieSlice { ... }
508
PieSlice { ... }
509
}
510
\endcode
511
512
Or randomly add and remove slices from time to time using \l{Property Value Sources}{property value sources}:
513
514
\badcode
515
PieChart {
516
PieSliceRandomizer on slices {}
517
}
518
\endcode
519
520
\note To continue learning about QML extensions and features follow the
521
\l {Writing advanced QML Extensions with C++} tutorial.
522
*/
qtdeclarative
src
qml
doc
src
cppintegration
extending-tutorial.qdoc
Generated on
for Qt by
1.16.1