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-advanced.qdoc
Go to the documentation of this file.
1
// Copyright (C) 2023 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-advanced-example.html
6
\meta tags{qml,extensions,advanced}
7
8
\title Writing advanced QML Extensions with C++
9
\brief Tutorial about advanced extensions to QML with Qt C++.
10
11
12
\section1 BirthdayParty Base Project
13
\c extending-qml-advanced/advanced1-Base-project
14
15
This tutorial uses the example of a birthday party to demonstrate some of
16
the features of QML. The code for the various features explained below is
17
based on this birthday party project and relies on some of the material in the
18
first tutorial on \l {Writing QML Extensions with C++}{QML extensions}. This
19
simple example is then expanded upon to illustrate the various QML extensions
20
explained below. The complete code for each new extension to the code can be
21
found in the tutorials at the location specified under each section's title or
22
by following the link to the code at the very end of this page.
23
24
25
The base project defines the \c Person class and the \c BirthdayParty class,
26
which model the attendees and the party itself respectively.
27
\quotefromfile tutorials/extending-qml-advanced/advanced1-Base-project/person.h
28
\skipto class
29
\printuntil QML_ELEMENT
30
\dots
31
\skipuntil private:
32
\printuntil /\};/
33
34
\quotefromfile tutorials/extending-qml-advanced/advanced1-Base-project/birthdayparty.h
35
\skipto class
36
\printuntil QML_ELEMENT
37
\dots
38
\skipto Person *m_host = nullptr;
39
\printuntil /\};/
40
41
All the information about the party can then be stored in the corresponding QML
42
file.
43
\quotefromfile tutorials/extending-qml-advanced/advanced1-Base-project/Main.qml
44
\skipto BirthdayParty
45
\printuntil /^\}/
46
47
The \c main.cpp file creates a simple shell application that displays whose
48
birthday it is and who is invited to their party.
49
\quotefromfile tutorials/extending-qml-advanced/advanced1-Base-project/main.cpp
50
\skipto engine
51
\printuntil }
52
53
The app outputs the following summary of the party.
54
\badcode
55
"Bob Jones" is having a birthday!
56
They are inviting:
57
"Leo Hodges"
58
"Jack Smith"
59
"Anne Brown"
60
\endcode
61
62
The following sections go into how to add support for \c Boy and \c Girl
63
attendees instead of just \c Person by using inheritance and coercion, how to
64
make use of default properties to implicitly assign attendees of the party as
65
guests, how to assign properties as groups instead of one by one, how to use
66
attached objects to keep track of invited guests' reponses, how to use a
67
property value source to display the lyrics of the happy birthday song over
68
time, and how to expose third party objects to QML.
69
70
71
72
\section1 Inheritance and Coercion
73
\c extending-qml-advanced/advanced2-Inheritance-and-coercion
74
75
Right now, each attendant is being modelled as a person. This is a bit too
76
generic and it would be nice to be able to know more about the attendees. By
77
specializing them as boys and girls, we can already get a better idea of who's
78
coming.
79
80
To do this, the \c Boy and \c Girl classes are introduced, both inheriting from
81
\c Person.
82
\quotefromfile tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.h
83
\skipto Boy
84
\printuntil /^\};/
85
86
\quotefromfile tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.h
87
\skipto Girl
88
\printuntil /^\};/
89
90
The \c Person class remains unaltered and the \c Boy and \c Girl C++ classes
91
are trivial extensions of it. The types and their QML name are registered with
92
the QML engine with \l QML_ELEMENT.
93
94
Notice that the \c host and \c guests properties in \c BirthdayParty still take
95
instances of \c Person.
96
\quotefromfile tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/birthdayparty.h
97
\skipto BirthdayParty
98
\printuntil QML_ELEMENT
99
\dots
100
\skipto /^\};/
101
\printuntil /^\};/
102
103
The implementation of the \c Person class itself has not been changed. However,
104
as the \c Person class was repurposed as a common base for \c Boy and \c Girl,
105
\c Person should no longer be instantiable from QML directly. An explicit
106
\c Boy or \c Girl should be instantiated instead.
107
\quotefromfile tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.h
108
\skipto Person
109
\printto Q_OBJECT
110
\dots
111
\skipto QML_ELEMENT
112
\printuntil QML_UNCREATABLE
113
\dots
114
\skipto /^\};/
115
\printuntil /^\};/
116
117
While we want to disallow instantiating \c Person from within QML, it still
118
needs to be registered with the QML engine so that it can be used as a property
119
type and other types can be coerced to it. This is what the QML_UNCREATABLE
120
macro does. As all three types, \c Person, \c Boy and \c Girl, have been
121
registered with the QML system, on assignment, QML automatically (and type-safely)
122
converts the \c Boy and \c Girl objects into a \c Person.
123
124
With these changes in place, we can now specify the birthday party with the
125
extra information about the attendees as follows.
126
\quotefromfile tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/Main.qml
127
\skipto BirthdayParty
128
\printuntil /^\}/
129
130
131
132
\section1 Default Properties
133
\c extending-qml-advanced/advanced3-Default-properties
134
135
Currently, in the QML file, each property is assigned explicitly. For example,
136
the \c host property is assigned a \c Boy and the \c guests property is
137
assigned a list of \c Boy or \c Girl. This is easy but it can be made a bit
138
simpler for this specific use case. Instead of assigning the \c guests property
139
explicitly, we can add \c Boy and \c Girl objects inside the party directly
140
and have them assigned to \c guests implicitly. It makes sense that all the
141
attendees that we specify, and that are not the host, are guests. This change
142
is purely syntactical but it can add a more natural feel in many situations.
143
144
The \c guests property can be designated as the default property of
145
\c BirthdayParty. Meaning that each object created inside of a \c BirthdayParty
146
is implicitly appended to the default property \c guests. The resulting QML
147
looks like this.
148
\quotefromfile tutorials/extending-qml-advanced/advanced3-Default-properties/Main.qml
149
\skipto BirthdayParty
150
\printuntil /^\}/
151
152
The only change required to enable this behavior is to add the \c DefaultProperty
153
class info annotation to \c BirthdayParty to designate \c guests as its default
154
property.
155
\quotefromfile tutorials/extending-qml-advanced/advanced3-Default-properties/birthdayparty.h
156
\skipto class
157
\printuntil QML_ELEMENT
158
\dots
159
\skipto /^\};/
160
\printuntil /^\};/
161
162
You may already be familiar with this mechanism. The default property for all
163
descendants of \c Item in QML is the \c data property. All elements not
164
explicitly added to a property of an \c Item will be added to \c data. This
165
makes the structure clear and reduces unnecessary noise in the code.
166
167
\sa {Specifying Default and Parent Properties for QML Object Types}
168
169
170
171
\section1 Grouped Properties
172
\c extending-qml-advanced/advanced4-Grouped-properties
173
174
More information is needed about the shoes of the guests. Aside from their
175
size, we also want to store the shoes' color, brand, and price. This
176
information is stored in a \c ShoeDescription class.
177
\quotefromfile tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.h
178
\skipto ShoeDescription
179
\printuntil price
180
\dots
181
\skipto /^\};/
182
\printuntil /^\};/
183
184
Each person now has two properties, a \c name and a shoe description \c shoe.
185
\quotefromfile tutorials/extending-qml-advanced/advanced4-Grouped-properties/person.h
186
\skipto Person
187
\printuntil shoe
188
\dots
189
\skipto /^\};/
190
\printuntil /^\};/
191
192
Specifying the values for each element of the shoe description works but is a
193
bit repetitive.
194
\quotefromfile tutorials/extending-qml-advanced/advanced4-Grouped-properties/Main.qml
195
\skipto Girl
196
\printuntil }
197
198
Grouped properties provide a more elegant way of assigning these properties.
199
Instead of assigning the values to each property one-by-one, the individual
200
values can be passed as a group to the \c shoe property making the code more
201
readable. No changes are required to enable this feature as it is available by
202
default for all of QML.
203
\quotefromfile tutorials/extending-qml-advanced/advanced4-Grouped-properties/Main.qml
204
\skipto host
205
\printuntil /^....}/
206
207
\sa {Grouped Properties}
208
209
210
211
\section1 Attached Properties
212
\c extending-qml-advanced/advanced5-Attached-properties
213
214
The time has come for the host to send out invitations. To keep track of which
215
guests have responded to the invitation and when, we need somewhere to store
216
that information. Storing it in the \c BirthdayParty object iself would not
217
really fit. A better way would be to store the responses as attached objects to
218
the party object.
219
220
First, we declare the \c BirthdayPartyAttached class which holds the guest reponses.
221
\quotefromfile tutorials/extending-qml-advanced/advanced5-Attached-properties/birthdayparty.h
222
\skipto BirthdayPartyAttached
223
\printuntil QML_ANONYMOUS
224
\dots
225
\skipto /^\};/
226
\printuntil /^\};/
227
228
And we attach it to the \c BirthdayParty class and define
229
\c qmlAttachedProperties() to return the attached object.
230
\quotefromfile tutorials/extending-qml-advanced/advanced5-Attached-properties/birthdayparty.h
231
\skipto /BirthdayParty : public QObject/
232
\printuntil /^{/
233
\dots
234
\skipto QML_ATTACHED
235
\printuntil QML_ATTACHED
236
\dots
237
\skipto qmlAttachedProperties
238
\printuntil qmlAttachedProperties
239
\skipto /^\};/
240
\printuntil /^\};/
241
242
Now, attached objects can be used in the QML to hold the rsvp information of the invited guests.
243
\quotefromfile tutorials/extending-qml-advanced/advanced5-Attached-properties/Main.qml
244
\skipto BirthdayParty
245
\printuntil /^}/
246
247
Finally, the information can be accessed in the following way.
248
\quotefromfile tutorials/extending-qml-advanced/advanced5-Attached-properties/main.cpp
249
\skipto rsvpDate
250
\printuntil attached->property("rsvp").toDate();
251
252
The program outputs the following summary of the party to come.
253
\badcode
254
"Jack Smith" is having a birthday!
255
He is inviting:
256
"Robert Campbell" RSVP date: "Wed Mar 1 2023"
257
"Leo Hodges" RSVP date: "Mon Mar 6 2023"
258
\endcode
259
260
\sa {Providing Attached Properties}
261
262
263
264
\section1 Property Value Source
265
\c extending-qml-advanced/advanced6-Property-value-source
266
267
During the party the guests have to sing for the host. It would be handy if the
268
program could display the lyrics customized for the occasion to help the
269
guests. To this end, a property value source is used to generate the verses of
270
the song over time.
271
\quotefromfile tutorials/extending-qml-advanced/advanced6-Property-value-source/happybirthdaysong.h
272
\skipto class
273
\printuntil Q_INTERFACES
274
\dots
275
\skipto setTarget
276
\printuntil setTarget
277
\skipto /^\};/
278
\printuntil /^\};/
279
280
The class \c HappyBirthdaySong is added as a value source. It must inherit from
281
\c QQmlPropertyValueSource and implement the QQmlPropertyValueSource interface
282
with the Q_INTERFACES macro. The \c setTarget() function is used to define
283
which property this source acts upon. In this case, the value source writes to
284
the \c announcement property of the \c BirthdayParty to display the lyrics
285
over time. It has an internal timer that causes the \c announcement
286
property of the party to be set to the next line of the lyrics repeatedly.
287
288
In QML, a \c HappyBirthdaySong is instantiated inside the \c BirthdayParty. The
289
\c on keyword in its signature is used to specify the property that the value
290
source targets, in this case \c announcement. The \c name property of the
291
\c HappyBirthdaySong object is also \l {Property Binding}{bound} to the name of
292
the host of the party.
293
\quotefromfile tutorials/extending-qml-advanced/advanced6-Property-value-source/Main.qml
294
\skipto BirthdayParty
295
\printuntil }
296
\dots
297
\skipto /^\}/
298
\printuntil /^\}/
299
300
The program displays the time at which the party started using the
301
\c partyStarted signal and then prints the following happy birthday verses
302
over and over.
303
\badcode
304
Happy birthday to you,
305
Happy birthday to you,
306
Happy birthday dear Bob Jones,
307
Happy birthday to you!
308
\endcode
309
310
\sa {Property Value Sources}
311
312
313
314
\section1 Foreign objects integration
315
\c extending-qml-advanced/advanced7-Foreign-objects-integration
316
317
Instead of just printing the lyrics out to the console, the attendees would
318
like to use a more fancy display with support for colors. They would like to
319
integrate it in the project but currently it is not possible to configure the
320
screen from QML because it comes from a third party library. To solve this, the
321
necessary types need to be exposed to the QML engine so its properties are
322
available for modification in QML directly.
323
324
The display can be controlled by the \c ThirdPartyDisplay class. It has
325
properties to define the content and the foreground and background colors of the text
326
to display.
327
\quotefromfile tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/ThirdPartyDisplay.h
328
\skipto ThirdPartyDisplay
329
\printuntil backgroundColor
330
\dots
331
\skipto };
332
\printuntil };
333
334
To expose this type to QML, we can register it with the engine with
335
QML_ELEMENT. However, since the class isn't accessible for modification,
336
QML_ELEMENT cannot simply be added to it. To register the type with the engine,
337
the type needs to be registered from the outside. This is what QML_FOREIGN is
338
for. When used in a type in conjunction with other QML macros, the other macros
339
apply not to the type they reside in but to the foreign type designated by
340
QML_FOREIGN.
341
\quotefromfile tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreigndisplay.h
342
\skipto ForeignDisplay
343
\printuntil };
344
345
This way, the BirthdayParty now has a new property with the display.
346
\quotefromfile tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/birthdayparty.h
347
\skipuntil BirthdayPartyAttached
348
\skipto BirthdayParty
349
\printto Q_CLASSINFO
350
\dots
351
\skipto };
352
\printuntil };
353
354
And, in QML, the colors of the text on the fancy third display can be set explicitly.
355
\quotefromfile tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/Main.qml
356
\skipto BirthdayParty
357
\printuntil BirthdayParty
358
\skipto display:
359
\printuntil }
360
\dots
361
\skipto /^}/
362
\printuntil /^}/
363
364
Setting the \c announcement property of the BirthdayParty now sends the
365
message to the fancy display instead of printing it itself.
366
\quotefromfile tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/birthdayparty.cpp
367
\skipto setAnnouncement
368
\printuntil /^}/
369
370
The output then looks like this over and over similar to the previous section.
371
\badcode
372
[Fancy ThirdPartyDisplay] Happy birthday to you,
373
[Fancy ThirdPartyDisplay] Happy birthday to you,
374
[Fancy ThirdPartyDisplay] Happy birthday dear Bob Jones,
375
[Fancy ThirdPartyDisplay] Happy birthday to you!
376
\endcode
377
378
\sa {Registering Foreign Types}
379
*/
qtdeclarative
src
qml
doc
src
cppintegration
extending-tutorial-advanced.qdoc
Generated on
for Qt by
1.14.0