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