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