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