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
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
25The base project defines the \c Person class and the \c BirthdayParty class,
26which 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
41All the information about the party can then be stored in the corresponding QML
42file.
43\quotefromfile tutorials/extending-qml-advanced/advanced1-Base-project/Main.qml
44 \skipto BirthdayParty
45 \printuntil /^\}/
46
47The \c main.cpp file creates a simple shell application that displays whose
48birthday 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
53The app outputs the following summary of the party.
54\badcode
55"Bob Jones" is having a birthday!
56They are inviting:
57 "Leo Hodges"
58 "Jack Smith"
59 "Anne Brown"
60\endcode
61
62The following sections go into how to add support for \c Boy and \c Girl
63attendees instead of just \c Person by using inheritance and coercion, how to
64make use of default properties to implicitly assign attendees of the party as
65guests, how to assign properties as groups instead of one by one, how to use
66attached objects to keep track of invited guests' reponses, how to use a
67property value source to display the lyrics of the happy birthday song over
68time, 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
75Right now, each attendant is being modelled as a person. This is a bit too
76generic and it would be nice to be able to know more about the attendees. By
77specializing them as boys and girls, we can already get a better idea of who's
78coming.
79
80To 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
90The \c Person class remains unaltered and the \c Boy and \c Girl C++ classes
91are trivial extensions of it. The types and their QML name are registered with
92the QML engine with \l QML_ELEMENT.
93
94Notice that the \c host and \c guests properties in \c BirthdayParty still take
95instances 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
103The implementation of the \c Person class itself has not been changed. However,
104as 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
117While we want to disallow instantiating \c Person from within QML, it still
118needs to be registered with the QML engine so that it can be used as a property
119type and other types can be coerced to it. This is what the QML_UNCREATABLE
120macro does. As all three types, \c Person, \c Boy and \c Girl, have been
121registered with the QML system, on assignment, QML automatically (and type-safely)
122converts the \c Boy and \c Girl objects into a \c Person.
123
124With these changes in place, we can now specify the birthday party with the
125extra 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
135Currently, in the QML file, each property is assigned explicitly. For example,
136the \c host property is assigned a \c Boy and the \c guests property is
137assigned a list of \c Boy or \c Girl. This is easy but it can be made a bit
138simpler for this specific use case. Instead of assigning the \c guests property
139explicitly, we can add \c Boy and \c Girl objects inside the party directly
140and have them assigned to \c guests implicitly. It makes sense that all the
141attendees that we specify, and that are not the host, are guests. This change
142is purely syntactical but it can add a more natural feel in many situations.
143
144The \c guests property can be designated as the default property of
145\c BirthdayParty. Meaning that each object created inside of a \c BirthdayParty
146is implicitly appended to the default property \c guests. The resulting QML
147looks like this.
148\quotefromfile tutorials/extending-qml-advanced/advanced3-Default-properties/Main.qml
149 \skipto BirthdayParty
150 \printuntil /^\}/
151
152The only change required to enable this behavior is to add the \c DefaultProperty
153class info annotation to \c BirthdayParty to designate \c guests as its default
154property.
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
162You may already be familiar with this mechanism. The default property for all
163descendants of \c Item in QML is the \c data property. All elements not
164explicitly added to a property of an \c Item will be added to \c data. This
165makes 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
174More information is needed about the shoes of the guests. Aside from their
175size, we also want to store the shoes' color, brand, and price. This
176information 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
184Each 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
192Specifying the values for each element of the shoe description works but is a
193bit repetitive.
194\quotefromfile tutorials/extending-qml-advanced/advanced4-Grouped-properties/Main.qml
195 \skipto Girl
196 \printuntil }
197
198Grouped properties provide a more elegant way of assigning these properties.
199Instead of assigning the values to each property one-by-one, the individual
200values can be passed as a group to the \c shoe property making the code more
201readable. No changes are required to enable this feature as it is available by
202default 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
214The time has come for the host to send out invitations. To keep track of which
215guests have responded to the invitation and when, we need somewhere to store
216that information. Storing it in the \c BirthdayParty object iself would not
217really fit. A better way would be to store the responses as attached objects to
218the party object.
219
220First, 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
228And 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
242Now, 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
247Finally, 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
252The program outputs the following summary of the party to come.
253\badcode
254"Jack Smith" is having a birthday!
255He 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
267During the party the guests have to sing for the host. It would be handy if the
268program could display the lyrics customized for the occasion to help the
269guests. To this end, a property value source is used to generate the verses of
270the 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
280The class \c HappyBirthdaySong is added as a value source. It must inherit from
281\c QQmlPropertyValueSource and implement the QQmlPropertyValueSource interface
282with the Q_INTERFACES macro. The \c setTarget() function is used to define
283which property this source acts upon. In this case, the value source writes to
284the \c announcement property of the \c BirthdayParty to display the lyrics
285over time. It has an internal timer that causes the \c announcement
286property of the party to be set to the next line of the lyrics repeatedly.
287
288In 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
290source 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
292the 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
300The program displays the time at which the party started using the
301\c partyStarted signal and then prints the following happy birthday verses
302over and over.
303\badcode
304Happy birthday to you,
305Happy birthday to you,
306Happy birthday dear Bob Jones,
307Happy 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
317Instead of just printing the lyrics out to the console, the attendees would
318like to use a more fancy display with support for colors. They would like to
319integrate it in the project but currently it is not possible to configure the
320screen from QML because it comes from a third party library. To solve this, the
321necessary types need to be exposed to the QML engine so its properties are
322available for modification in QML directly.
323
324The display can be controlled by the \c ThirdPartyDisplay class. It has
325properties to define the content and the foreground and background colors of the text
326to 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
334To expose this type to QML, we can register it with the engine with
335QML_ELEMENT. However, since the class isn't accessible for modification,
336QML_ELEMENT cannot simply be added to it. To register the type with the engine,
337the type needs to be registered from the outside. This is what QML_FOREIGN is
338for. When used in a type in conjunction with other QML macros, the other macros
339apply not to the type they reside in but to the foreign type designated by
340QML_FOREIGN.
341\quotefromfile tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreigndisplay.h
342 \skipto ForeignDisplay
343 \printuntil };
344
345This 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
354And, 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
364Setting the \c announcement property of the BirthdayParty now sends the
365message 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
370The 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*/