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