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