1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
5\example tutorials/extending-qml-advanced
6\meta tags{qml,extensions,advanced}
8\title Writing advanced QML Extensions with C++
9\brief Tutorial about advanced extensions to QML with Qt C++.
12\section1 BirthdayParty Base Project
13\c extending-qml-advanced/advanced1-Base-project
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.
24\image extending-qml-advanced-word-cloud.png
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
30 \printuntil QML_ELEMENT
35\quotefromfile tutorials/extending-qml-advanced/advanced1-Base-project/birthdayparty.h
37 \printuntil QML_ELEMENT
39 \skipto Person *m_host = nullptr;
42All the information about the party can then be stored in the corresponding QML
44\quotefromfile tutorials/extending-qml-advanced/advanced1-Base-project/Main.qml
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
54The app outputs the following summary of the party.
56"Bob Jones" is having a birthday!
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.
73\section1 Inheritance and Coercion
74\c extending-qml-advanced/advanced2-Inheritance-and-coercion
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
81To do this, the \c Boy and \c Girl classes are introduced, both inheriting from
83\quotefromfile tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.h
87\quotefromfile tutorials/extending-qml-advanced/advanced2-Inheritance-and-coercion/person.h
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.
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
99 \printuntil QML_ELEMENT
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
113 \printuntil QML_UNCREATABLE
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.
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
133\section1 Default Properties
134\c extending-qml-advanced/advanced3-Default-properties
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.
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
149\quotefromfile tutorials/extending-qml-advanced/advanced3-Default-properties/Main.qml
150 \skipto BirthdayParty
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
156\quotefromfile tutorials/extending-qml-advanced/advanced3-Default-properties/birthdayparty.h
158 \printuntil QML_ELEMENT
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.
168\sa {Specifying Default and Parent Properties for QML Object Types}
172\section1 Grouped Properties
173\c extending-qml-advanced/advanced4-Grouped-properties
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
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
193Specifying the values for each element of the shoe description works but is a
195\quotefromfile tutorials/extending-qml-advanced/advanced4-Grouped-properties/Main.qml
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
208\sa {Grouped Properties}
212\section1 Attached Properties
213\c extending-qml-advanced/advanced5-Attached-properties
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
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
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/
236 \printuntil QML_ATTACHED
238 \skipto qmlAttachedProperties
239 \printuntil qmlAttachedProperties
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
248Finally, the information can be accessed in the following way.
249\quotefromfile tutorials/extending-qml-advanced/advanced5-Attached-properties/main.cpp
251 \printuntil attached->property("rsvp").toDate();
253The program outputs the following summary of the party to come.
255"Jack Smith" is having a birthday!
257 "Robert Campbell" RSVP date: "Wed Mar 1 2023"
258 "Leo Hodges" RSVP date: "Mon Mar 6 2023"
261\sa {Providing Attached Properties}
265\section1 Property Value Source
266\c extending-qml-advanced/advanced6-Property-value-source
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
272\quotefromfile tutorials/extending-qml-advanced/advanced6-Property-value-source/happybirthdaysong.h
274 \printuntil Q_INTERFACES
277 \printuntil setTarget
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.
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
301The program displays the time at which the party started using the
302\c partyStarted signal and then prints the following happy birthday verses
305Happy birthday to you,
306Happy birthday to you,
307Happy birthday dear Bob Jones,
308Happy birthday to you!
311\sa {Property Value Sources}
315\section1 Foreign objects integration
316\c extending-qml-advanced/advanced7-Foreign-objects-integration
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.
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
328\quotefromfile tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/library/ThirdPartyDisplay.h
329 \skipto ThirdPartyDisplay
330 \printuntil backgroundColor
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
342\quotefromfile tutorials/extending-qml-advanced/advanced7-Foreign-objects-integration/foreigndisplay.h
343 \skipto ForeignDisplay
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
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
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
371The output then looks like this over and over similar to the previous section.
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!
379\sa {Registering Foreign Types}