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