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
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}
34template:
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
41Then 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
46A 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}.
48For example, this could be done to implement particular data models, or provide
49types with custom painting and drawing capabilities, or access system features
50like network programming that are not accessible through built-in QML features.
51
52In this tutorial, we will show how to use the C++ classes in the Qt Quick
53module to extend QML. The end result will be a simple Pie Chart display implemented by
54several custom QML types connected together through QML features like bindings and
55signals, and made available to the QML runtime through a plugin.
56
57To begin with, let's create a new QML type called "PieChart" that has two properties: a name
58and a color. We will make it available in an importable type namespace called "Charts", with
59a version of 1.0.
60
61We 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
73highlighting (\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
78To do this, we need a C++ class that encapsulates this \c PieChart type and its
79properties. Since QML makes extensive use of Qt's \l{Meta-Object System}{meta object system},
80this 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
89Here is our \c PieChart class, defined in \c piechart.h:
90
91\snippet tutorials/extending-qml/chapter1-basics/piechart.h 0
92
93The class inherits from QQuickPaintedItem because we want to override
94QQuickPaintedItem::paint() to perform drawing operations with the QPainter API.
95If the class just represented some data type and was not an item that actually needed
96to be displayed, it could simply inherit from QObject. Or, if we want to extend the
97functionality of an existing QObject-based class, it could inherit from that class instead.
98Alternatively, if we want to create a visual item that doesn't need to perform drawing
99operations with the QPainter API, we can just subclass QQuickItem.
100
101The \c PieChart class defines the two properties, \c name and \c color, with the
102Q_PROPERTY macro, and overrides QQuickPaintedItem::paint(). The \c PieChart
103class is registered using the QML_ELEMENT macro, to allow it to be used from
104QML. 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
109For 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
115Additionally, a \l{Module Definition qmldir Files}{qmldir} file needs to be added manually to
116create a \l{Writing QML Modules}{QML module}.
117
118\quotefile tutorials/extending-qml/chapter1-basics/qmldir
119
120\section2 CMake Setup
121
122For 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
127The \l qt_add_qml_module() API automatically generates a
128\c qmldir file for the QML module.
129
130\section2 Class Implementation
131
132The 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
134draw 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
142Now that we have defined the \c PieChart type, we will use it from QML. The \c
143App.qml file creates a \c PieChart item and displays the pie chart's details
144using a standard QML \l Text item:
145
146\snippet tutorials/extending-qml/chapter1-basics/App.qml 0
147
148Notice that although the color is specified as a string in QML, it is automatically
149converted to a QColor object for the PieChart \c color property. Automatic conversions are
150provided for various other \l {QML Value Types}{value types}. For example, a string
151like "640x480" can be automatically converted to a QSize value.
152
153We'll also create a C++ application that uses a QQuickView to run and
154display \c App.qml.
155
156Here is the application \c main.cpp:
157
158\snippet tutorials/extending-qml/chapter1-basics/main.cpp 0
159
160\section2 Project Build
161
162To build the project we include the files, link against the libraries, and
163define a type namespace called "Charts" with version 1.0 for any types exposed
164to QML.
165
166Using qmake:
167
168\quotefile tutorials/extending-qml/chapter1-basics/chapter1-basics.pro
169
170Using CMake:
171
172\quotefile tutorials/extending-qml/chapter1-basics/CMakeLists.txt
173
174Now 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
188Suppose we want \c PieChart to have a "clearChart()" method that erases the
189chart and then emits a "chartCleared" signal. Our \c App.qml would be able
190to 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
198To do this, we add a \c clearChart() method and a \c chartCleared() signal
199to 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
209The use of Q_INVOKABLE makes the \c clearChart() method available to the
210Qt 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,
213because public and protected slots are also callable from QML (you cannot call
214private slots).
215
216The \c clearChart() method changes the color to Qt::transparent,
217repaints the chart, then emits the \c chartCleared() signal:
218
219\snippet tutorials/extending-qml/chapter2-methods/piechart.cpp 0
220
221Now when we run the application and click the window, the pie chart
222disappears, 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
233Property binding is a powerful feature of QML that allows values of different
234types to be synchronized automatically. It uses signals to notify and update
235other types' values when property values are changed.
236
237Let's enable property bindings for the \c color property. That means
238if 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
245The "color: chartA.color" statement binds the \c color value of
246\c chartB to the \c color of \c chartA.
247Whenever \c chartA's \c color value changes, \c chartB's \c color value
248updates to the same value. When the window is clicked, the \c onClicked
249handler in the MouseArea changes the color of \c chartA, thereby changing
250both charts to the color blue.
251
252It's easy to enable property binding for the \c color property.
253We add a \l{Qt's Property System}{NOTIFY} feature to its Q_PROPERTY() declaration to indicate that a "colorChanged" signal
254is 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
264Then, we emit this signal in \c setColor():
265
266\snippet tutorials/extending-qml/chapter3-bindings/piechart.cpp 0
267
268It's important for \c setColor() to check that the color value has actually changed
269before emitting \c colorChanged(). This ensures the signal is not emitted unnecessarily and
270also prevents loops when other types respond to the value change.
271
272The use of bindings is essential to QML. You should always add NOTIFY
273signals for properties if they are able to be implemented, so that your
274properties can be used in bindings. Properties that cannot be bound cannot be
275automatically updated and cannot be used as flexibly in QML. Also, since
276bindings are invoked so often and relied upon in QML usage, users of your
277custom 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
285The \c PieChart type currently has a string-type property and a color-type property.
286It could have many other types of properties. For example, it could have an
287int-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
312Aside from \c int, we could use various other property types. Many of the Qt
313data 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
316If we want to create a property whose type is not supported by QML by default,
317we need to register the type with the QML engine.
318
319For 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,
321we assign an \c PieSlice value which itself contains a \c color:
322
323\snippet tutorials/extending-qml/chapter4-customPropertyTypes/App.qml 0
324
325Like \c PieChart, this new \c PieSlice type inherits from QQuickPaintedItem and declares
326its properties with Q_PROPERTY():
327
328\snippet tutorials/extending-qml/chapter4-customPropertyTypes/pieslice.h 0
329
330To use it in \c PieChart, we modify the \c color property declaration
331and 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
341There is one thing to be aware of when implementing \c setPieSlice(). The \c PieSlice
342is a visual item, so it must be set as a child of the \c PieChart using
343QQuickItem::setParentItem() so that the \c PieChart knows to paint this child
344item when its contents are drawn:
345
346\snippet tutorials/extending-qml/chapter4-customPropertyTypes/piechart.cpp 0
347
348Like the \c PieChart type, the \c PieSlice type has to be exposted to QML
349using QML_ELEMENT.
350
351\snippet tutorials/extending-qml/chapter4-customPropertyTypes/pieslice.h 0
352\dots
353
354As with \c PieChart, we add the "Charts" type namespace, version 1.0, to our
355build file:
356
357Using qmake:
358
359\quotefile tutorials/extending-qml/chapter4-customPropertyTypes/chapter4-customPropertyTypes.pro
360
361Using 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
373Right now, a \c PieChart can only have one \c PieSlice. Ideally a chart would
374have multiple slices, with different colors and sizes. To do this, we could
375have 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
382To do this, we replace the \c pieSlice property in \c PieChart with a \c slices property,
383declared as a \l QQmlListProperty type. The \l QQmlListProperty class enables the
384creation of list properties in types exposed to QML. We replace the \c pieSlice()
385function with a \c slices() function that returns a list of slices. We also use
386a 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
394Although the \c slices property does not have an associated \c WRITE function,
395it is still modifiable because of the way \l QQmlListProperty works.
396In the \c PieChart implementation, we implement \c PieChart::slices() to
397return a \l QQmlListProperty value:
398
399\snippet tutorials/extending-qml/chapter5-listproperties/piechart.cpp 0
400
401This synthesizes the necessary functions to interact with the list from QML.
402The resulting \l QQmlListProperty is a \e view into the list. Alternately, you can
403manually provide the individual access functions for the list. This is necessary
404if your list is not a \l QList or if you want to restrict or otherwise customize
405QML access to your list. In most cases, however, the constructor taking a
406\l QList pointer is the safest and easiest option.
407
408The \c PieSlice class has also been modified to include \c fromAngle and \c angleSpan
409properties and to draw the slice according to these values. This is a straightforward
410modification 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
418Currently the \c PieChart and \c PieSlice types are used by \c App.qml,
419which is displayed using a QQuickView in a C++ application. An alternative
420way to use our QML extension is to create a plugin library to make it available
421to 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
423by any QML application, instead of restricting these types to be only used by
424the one application.
425
426The steps for creating a plugin are described in \l {Creating C++ Plugins for QML}.
427To start with, we create a plugin class named \c ChartsPlugin. It subclasses
428QQmlEngineExtensionPlugin and uses the Q_PLUGIN_METADATA() macro to register the
429plugin with the Qt meta object system.
430
431Here is the \c ChartsPlugin definition in \c chartsplugin.h:
432
433\snippet tutorials/extending-qml/chapter6-plugins/Charts/chartsplugin.h 0
434
435Then, we configure the build file to define the project as a plugin library.
436
437Using qmake:
438
439\quotefile tutorials/extending-qml/chapter6-plugins/Charts/Charts.pro
440
441Using CMake:
442
443\quotefile tutorials/extending-qml/chapter6-plugins/Charts/CMakeLists.txt
444
445When building this example on Windows or Linux, the \c Charts directory will be
446located at the same level as the application that uses our new import module.
447This way, the QML engine will find our module as the default search path for QML
448imports includes the directory of the application executable. On \macos, the
449plugin binary is copied to \c Contents/PlugIns in the application bundle.
450With 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
456To 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
462Defining custom import paths is useful also when there are multiple
463applications using the same QML imports.
464
465The \c .pro file also contains additional magic to ensure that the
466\l {Module Definition qmldir Files}{module definition qmldir file} is always copied
467to the same location as the plugin binary.
468
469The \c qmldir file declares the module name and the plugin that is made available
470by the module:
471
472\quotefile tutorials/extending-qml/chapter6-plugins/Charts/qmldir
473
474Now we have a QML module that can be imported to any application, provided that the
475QML 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
477load the QML file using the \l {Prototyping with the QML Runtime Tool}{qml tool},
478setting 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
484The module "Charts" will be loaded by the QML engine, and the types provided by that
485module will be available for use in any QML document which imports it.
486
487
488
489\section1 Chapter 7: Summary
490
491In 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
505The \l{Overview - QML and C++ Integration}{QML and C++ Integration overview}
506documentation shows other useful features that can be added to QML extensions.
507For example, we could use
508\l{QML Object Attributes#Default Properties}{default properties} to allow
509slices to be added without using the \c slices property:
510
511\badcode
512 PieChart {
513 PieSlice { ... }
514 PieSlice { ... }
515 PieSlice { ... }
516 }
517\endcode
518
519Or 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*/