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
exposecppattributes.qdoc
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
3/*!
4\page qtqml-cppintegration-exposecppattributes.html
5\title Exposing Attributes of C++ Types to QML
6\brief Description of how to expose the attributes of a C++ type to QML.
7
8QML can easily be extended with functionality defined in C++ code. Due to the
9tight integration of the QML engine with the \l{The Meta-Object System}{Qt
10meta-object system}, any functionality that is appropriately exposed by a
11QObject-derived class or a Q_GADGET type is accessible from QML code. This
12enables C++ data and functions to be accessible directly from QML, often with
13little or no modification.
14
15The QML engine has the ability to introspect QObject instances through the
16meta-object system. This means any QML code can access the following members of
17an instance of a QObject-derived class:
18
19\list
20\li Properties
21\li Methods (providing they are public slots or flagged with Q_INVOKABLE)
22\li Signals
23\endlist
24
25(Additionally, enums are available if they have been declared with Q_ENUM.
26See \l{qtqml-cppintegration-data.html}{Data Type Conversion Between QML and C++}
27for more details.)
28
29In general, these are accessible from QML regardless of whether a
30QObject-derived class has been \l{Registering C++ Types with the QML Type
31System}{registered with the QML type system}. However, if a class is to be
32used in a way that requires the engine to access additional type information
33— for example, if the class itself is to be used as a method parameter or
34property, or if one of its enum types is to be used in this way — then the
35class may need to be registered. Registration is recommended for all types you
36use in QML, as only registered types can be analyzed at compile time.
37
38Registration is required for Q_GADGET types, as they don't derive from a known
39common base and can't be made available automatically. Without registration,
40their properties and methods are inaccessible.
41
42You can make C++ types from a different module available in your own module by
43adding a dependency to your \l{qt_add_qml_module} call using the \e DEPENDENCIES
44option. You may, for example, want to depend on \l[Cpp]{QtQuick} so that your
45QML-exposed C++ types can use \l QColor as method arguments and return values.
46\l[Cpp]{QtQuick} exposes \l QColor as a \l {QML Value Types}{value type} \e color.
47Such dependencies may be automatically inferred at run time, but you should not
48rely on this.
49
50Also note that a number of the important concepts covered in this document are
51demonstrated in the \l{Writing QML Extensions with C++} tutorial.
52
53For more information about C++ and the different QML integration methods,
54see the
55\l {Overview - QML and C++ Integration} {C++ and QML integration overview} page.
56
57\section1 Data Type Handling and Ownership
58
59Any data that is transferred from C++ to QML, whether as a property value, a
60method parameter or return value, or a signal parameter value, must be of a
61type that is supported by the QML engine.
62
63By default, the engine supports a number of Qt C++ types and can automatically
64convert them as appropriately when used from QML. Additionally, C++ classes
65that are \l{Registering C++ Types with the QML Type System}{registered} with
66the QML type system can be used as data types, as can their enums if
67appropriately registered. See \l{qtqml-cppintegration-data.html}{Data Type
68Conversion Between QML and C++} for further information.
69
70Additionally, data ownership rules are taken into consideration when data is
71transferred from C++ to QML. See \l {Data Ownership} for more details.
72
73
74\section1 Exposing Properties
75
76A \e property can be specified for any QObject-derived class using the
77Q_PROPERTY() macro. A property is a class data member with an associated read
78function and optional write function.
79
80All properties of a QObject-derived or Q_GADGET class are accessible from QML.
81
82For example, below is a \c Message class with an \c author property. As
83specified by the Q_PROPERTY macro call, this property is readable through
84the \c author() method, and writable through the \c setAuthor() method:
85
86\note Do not use \e typedef or \e using for Q_PROPERTY types as these
87will confuse moc. This may make certain type comparisons fail.
88
89Instead of:
90
91\badcode
92using FooEnum = Foo::Enum;
93
94class Bar : public QObject
95{
96 Q_OBJECT
97 Q_PROPERTY(FooEnum enum READ enum WRITE setEnum NOTIFY enumChanged)
98};
99\endcode
100
101Refer to the type directly:
102
103\code
104class Bar : public QObject
105{
106 Q_OBJECT
107 Q_PROPERTY(Foo::Enum enum READ enum WRITE setEnum NOTIFY enumChanged)
108};
109\endcode
110
111In order to make \c Message available you need to use \l{QML_ELEMENT} in C++
112and \l{qt_add_qml_module} in CMake.
113
114\code
115class Message : public QObject
116{
117 Q_OBJECT
118 QML_ELEMENT
119 Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
120public:
121 void setAuthor(const QString &a)
122 {
123 if (a != m_author) {
124 m_author = a;
125 emit authorChanged();
126 }
127 }
128
129 QString author() const
130 {
131 return m_author;
132 }
133
134signals:
135 void authorChanged();
136
137private:
138 QString m_author;
139};
140\endcode
141
142An instance of \c Message can be passed as required property to a file called
143\c MyItem.qml to make it available:
144
145\code
146 int main(int argc, char *argv[]) {
147 QGuiApplication app(argc, argv);
148
149 QQuickView view;
150 Message msg;
151 view.setInitialProperties({{"msg", &msg}});
152 view.setSource(QUrl::fromLocalFile("MyItem.qml"));
153 view.show();
154
155 return app.exec();
156 }
157\endcode
158
159Then, the \c author property could be read from \c MyItem.qml:
160
161\qml
162// MyItem.qml
163import QtQuick
164
165Text {
166 required property Message msg
167
168 width: 100; height: 100
169 text: msg.author // invokes Message::author() to get this value
170
171 Component.onCompleted: {
172 msg.author = "Jonah" // invokes Message::setAuthor()
173 }
174}
175\endqml
176
177For maximum interoperability with QML, \b {any property that is writable should
178have an associated NOTIFY signal} that is emitted whenever the property value
179has changed. This allows the property to be used with \l{Property
180Binding}{property binding}, which is an essential feature of QML that enforces
181relationships between properties by automatically updating a property whenever
182any of its dependencies change in value.
183
184In the above example, the associated NOTIFY signal for the \c author property is
185\c authorChanged, as specified in the Q_PROPERTY() macro call. This means that
186whenever the signal is emitted — as it is when the author changes
187in Message::setAuthor() — this notifies the QML engine that any
188bindings involving the \c author property must be updated, and in turn, the
189engine will update the \c text property by calling \c Message::author() again.
190
191If the \c author property was writable but did not have an associated NOTIFY
192signal, the \c text value would be initialized with the initial value returned
193by \c Message::author() but would not be updated with any later changes to this
194property. In addition, any attempts to bind to the property from QML will
195produce a runtime warning from the engine.
196
197\note It is recommended that the NOTIFY signal be named \e <property>Changed
198where \c <property> is the name of the property. The associated property
199change signal handler generated by the QML engine will always take the form
200\c on<Property>Changed, regardless of the name of the related C++ signal, so
201it is recommended that the signal name follows this convention to avoid any
202confusion.
203
204
205\section3 Notes on Use of Notify Signals
206
207To prevent loops or excessive evaluation, developers should ensure that the
208property change signal is only emitted when the property value has actually
209changed. Also, if a property or group of properties is infrequently used, it
210is permitted to use the same NOTIFY signal for several properties. This should
211be done with care to ensure that performance doesn't suffer.
212
213The presence of a NOTIFY signal does incur a small overhead. There are cases
214where a property's value is set at object construction time, and does not
215subsequently change. The most common case of this is a read-only property that
216holds a sub-object, typically accessed through
217\l{#Grouped Properties}{grouped property syntax}, where the sub-object is
218allocated once, and only freed when the owner is deleted. In these cases,
219the CONSTANT attribute may be added to the property declaration instead of a
220NOTIFY signal.
221
222The CONSTANT attribute should only be used for properties whose value is set,
223and finalized, only in the class constructor. All other properties that want
224to be used in bindings should have a NOTIFY signal instead.
225
226
227\section2 Properties with Object Types
228
229Object-type properties are accessible from QML providing that the object type
230has been appropriately \l{Registering C++ Types with the QML Type
231System}{registered} with the QML type system.
232
233For example, the \c Message type might have a \c body property of type
234\c MessageBody*:
235
236\code
237class Message : public QObject
238{
239 Q_OBJECT
240 Q_PROPERTY(MessageBody* body READ body WRITE setBody NOTIFY bodyChanged)
241public:
242 MessageBody* body() const;
243 void setBody(MessageBody* body);
244};
245
246class MessageBody : public QObject
247{
248 Q_OBJECT
249 Q_PROPERTY(QString text READ text WRITE text NOTIFY textChanged)
250// ...
251}
252\endcode
253
254Suppose the \c Message type was \l{Registering C++ Types with the QML Type
255System}{registered} with the QML type system, allowing it to be used as an
256object type from QML code:
257
258\qml
259Message {
260 // ...
261}
262\endqml
263
264If the \c MessageBody type was also registered with the type system, it would be
265possible to assign \c MessageBody to the \c body property of a \c Message, all
266from within QML code:
267
268\qml
269Message {
270 body: MessageBody {
271 text: "Hello, world!"
272 }
273}
274\endqml
275
276
277\section2 Properties with Object-List Types
278
279Properties containing lists of QObject-derived types can also be exposed to
280QML. For this purpose, however, one should use QQmlListProperty rather than
281QList<T> as the property type. This is because QList is not a QObject-derived
282type, and so cannot provide the necessary QML property characteristics
283through the Qt meta object system, such as signal notifications when a list
284is modified.
285
286For example, the \c MessageBoard class below has a \c messages property of
287type QQmlListProperty that stores a list of \c Message instances:
288
289\code
290class MessageBoard : public QObject
291{
292 Q_OBJECT
293 Q_PROPERTY(QQmlListProperty<Message> messages READ messages)
294public:
295 QQmlListProperty<Message> messages();
296
297private:
298 static void append_message(QQmlListProperty<Message> *list, Message *msg);
299
300 QList<Message *> m_messages;
301};
302\endcode
303
304The MessageBoard::messages() function simply creates and returns a
305QQmlListProperty from its QList<T> \c m_messages member, passing the
306appropriate list modification functions as required by the QQmlListProperty
307constructor:
308
309\code
310QQmlListProperty<Message> MessageBoard::messages()
311{
312 return QQmlListProperty<Message>(this, 0, &MessageBoard::append_message);
313}
314
315void MessageBoard::append_message(QQmlListProperty<Message> *list, Message *msg)
316{
317 MessageBoard *msgBoard = qobject_cast<MessageBoard *>(list->object);
318 if (msg)
319 msgBoard->m_messages.append(msg);
320}
321\endcode
322
323Note that the template class type for the QQmlListProperty — in this case,
324\c Message — must be \l{Registering C++ Types with the QML Type System}
325{registered} with the QML type system.
326
327
328\section2 Grouped Properties
329\keyword Integrating QML and C++ - Grouped Properties
330
331Any property whose type has sub-properties of its own can be manipulated using
332the \l{QML Object Attributes#Grouped Properties}{grouped property syntax}, no
333matter whether that type is a value type such as \c font or an object type.
334Grouped properties are useful to expose a group of related properties that
335describe a set of attributes for a type.
336
337For example, suppose the \c Message::author property was of type
338\c MessageAuthor rather than a simple string, with sub-properties
339of \c name and \c email:
340
341\code
342class MessageAuthor : public QObject
343{
344 Q_PROPERTY(QString name READ name WRITE setName)
345 Q_PROPERTY(QString email READ email WRITE setEmail)
346public:
347 ...
348};
349
350class Message : public QObject
351{
352 Q_OBJECT
353 Q_PROPERTY(MessageAuthor* author READ author)
354public:
355 Message(QObject *parent)
356 : QObject(parent), m_author(new MessageAuthor(this))
357 {
358 }
359 MessageAuthor *author() const {
360 return m_author;
361 }
362private:
363 MessageAuthor *m_author;
364};
365\endcode
366
367The \c author property could be written to using the
368\l{QML Object Attributes#Grouped Properties}{grouped property syntax} in QML,
369like this:
370
371\qml
372Message {
373 author.name: "Alexandra"
374 author.email: "alexandra@mail.com"
375}
376\endqml
377
378Since \c author is an \l{Properties with Object Types}{object-type property},
379the grouped assignments do not create the \c MessageAuthor object. They are
380written to whatever object \c Message::author() returns. This has a few
381consequences:
382
383\list
384\li The sub-property names are resolved against the \e declared type of the
385 property, here \c MessageAuthor, not against the type of the object that
386 happens to be stored in it at run time. Different instantiations of
387 \c Message could produce \c author objects of different types. The only
388 thing we can rely on when creating the QML component is the declared type.
389\li The object has to exist when the grouped assignments are applied.
390 Otherwise the engine throws an error along the lines of
391 \c {Cannot set properties on author as it is null}. Creating the object in
392 the owner's constructor, as above, is the simplest way to guarantee this,
393 but a getter that creates the object on first access works just as well.
394\li The lifetime of the object is whatever the C++ implementation makes it.
395 Grouped property syntax neither creates nor destroys objects.
396\endlist
397
398Declaring \c author read-only, as in the example above, additionally prevents
399QML code from replacing the \c MessageAuthor object. If \c author had a
400\c WRITE accessor, you could equally well assign a new object to it:
401
402\qml
403Message {
404 author: MessageAuthor {
405 name: "Alexandra"
406 email: "alexandra@mail.com"
407 }
408}
409\endqml
410
411You cannot combine the two forms for the same property in the same
412object definition. Assigning an object to \c author and also writing
413\c author.name in the same \c Message produces an error. You can subvert that
414by having the grouped property in a different scope. However, doing so produces
415confusing results because now you have two \c MessageAuthor objects, the one
416created by the constructor and the one assigned explicitly. Only binding
417evaluation order determines which one the grouped property applies to.
418
419This means that the good practice is:
420
421\list
422\li Keep objects used as grouped properties read-only.
423\li Avoid grouped property syntax if you also manipulate the base property of
424 the group itself.
425\endlist
426
427For a \l {QML Value Types}{value type} property such as \c font, grouped
428assignments require the property to be writable, since the engine reads the
429value, modifies it, and writes it back. The potential confusion is still a
430problem, though. If you re-assign the whole \c font in place and then manipulate
431the \c font.bold property in a different place, binding evaluation order
432determines which one is done first, and what value of \c bold will result from
433it.
434
435
436\section1 Exposing Methods (Including Qt Slots)
437
438Any method of a QObject-derived type is accessible from QML code if it is:
439
440\list
441\li A public method flagged with the Q_INVOKABLE() macro
442\li A method that is a public Qt \l{Signals & Slots}{slot}
443\endlist
444
445For example, the \c MessageBoard class below has a \c postMessage() method that
446has been flagged with the Q_INVOKABLE macro, as well as a \c refresh() method
447that is a public slot:
448
449\code
450 class MessageBoard : public QObject
451 {
452 Q_OBJECT
453 QML_ELEMENT
454
455 public:
456 Q_INVOKABLE bool postMessage(const QString &msg) {
457 qDebug() << "Called the C++ method with" << msg;
458 return true;
459 }
460
461 public slots:
462 void refresh() {
463 qDebug() << "Called the C++ slot";
464 }
465 };
466\endcode
467
468If an instance of \c MessageBoard was set as the required property for a file \c
469MyItem.qml, then \c MyItem.qml could invoke the two methods as shown in the
470examples below:
471
472\table
473\row
474\li C++
475\li
476\code
477 int main(int argc, char *argv[]) {
478 QGuiApplication app(argc, argv);
479
480 MessageBoard msgBoard;
481 QQuickView view;
482 view.setInitialProperties({{"msgBoard", &msgBoard}});
483 view.setSource(QUrl::fromLocalFile("MyItem.qml"));
484 view.show();
485
486 return app.exec();
487 }
488\endcode
489\row
490\li QML
491\li
492\qml
493// MyItem.qml
494import QtQuick 2.0
495
496Item {
497 required property MessageBoard msgBoard
498
499 width: 100; height: 100
500
501 MouseArea {
502 anchors.fill: parent
503 onClicked: {
504 var result = msgBoard.postMessage("Hello from QML")
505 console.log("Result of postMessage():", result)
506 msgBoard.refresh();
507 }
508 }
509}
510\endqml
511\endtable
512
513If a C++ method has a parameter with a \c QObject* type, the parameter value
514can be passed from QML using an object \c id or a JavaScript \l var value
515that references the object.
516
517QML supports the calling of overloaded C++ functions. If there are multiple C++
518functions with the same name but different arguments, the correct function will
519be called according to the number and the types of arguments that are provided.
520
521Values returned from C++ methods are converted to JavaScript values when
522accessed from JavaScript expressions in QML.
523
524\section2 C++ methods and the 'this' object
525
526You may want to retrieve a C++ method from one object and call it on a different
527object. Consider the following example, within a QML module called \c{Example}:
528
529\table
530\row
531\li C++
532\li
533\code
534class Invokable : public QObject
535{
536 Q_OBJECT
537 QML_ELEMENT
538public:
539 Invokable(QObject *parent = nullptr) : QObject(parent) {}
540
541 Q_INVOKABLE void invoke() { qDebug() << "invoked on " << objectName(); }
542};
543\endcode
544\row
545\li QML
546\li
547\qml
548import QtQml
549import Example
550
551Invokable {
552 objectName: "parent"
553 property Invokable child: Invokable {}
554 Component.onCompleted: child.invoke.call(this)
555}
556\endqml
557\endtable
558
559If you load the QML code from a suitable main.cpp, it should print
560"invoked on parent". However, due to a long standing bug, it doesn't.
561Historically, the 'this' object of C++-based methods is inseparably bound to
562the method. Changing this behavior for existing code would cause subtle errors
563since the 'this' object is implicit in many places. Since Qt 6.5 you can
564explicitly opt into the correct behavior and allow C++ methods to accept a
565'this' object. To do so, add the following pragma to your QML documents:
566
567\qml
568pragma NativeMethodBehavior: AcceptThisObject
569\endqml
570
571With this line added, the example above will work as expected.
572
573\section2 Overriding toString()
574
575If you provide a Q_INVOKABLE method called \e{toString} (with no arguments), that
576method will be used to convert the object to a string in place of JavaScript's
577native \e{toString} implementation.
578
579\section1 Exposing Signals
580
581Any public \l{Signals & Slots}{signal} of a QObject-derived type is accessible
582from QML code.
583
584The QML engine automatically creates a \l{Signal and Handler Event
585System}{signal handler} for any signal of a QObject-derived type that is used
586from QML. Signal handlers are always named \e on<Signal> where \c <Signal> is
587the name of the signal, with the first letter capitalized. All parameters passed
588by the signal are available in the signal handler through the parameter names.
589
590For example, suppose the \c MessageBoard class has a \c newMessagePosted()
591signal with a single parameter, \c subject:
592
593\code
594 class MessageBoard : public QObject
595 {
596 Q_OBJECT
597 public:
598 // ...
599 signals:
600 void newMessagePosted(const QString &subject);
601 };
602\endcode
603
604If the \c MessageBoard type was \l{Registering C++ Types with the QML Type
605System}{registered} with the QML type system, then a \c MessageBoard object
606declared in QML could receive the \c newMessagePosted() signal using a signal
607handler named \c onNewMessagePosted, and examine the \c subject parameter
608value:
609
610\qml
611MessageBoard {
612 onNewMessagePosted: (subject)=> console.log("New message received:", subject)
613}
614\endqml
615
616As with property values and method parameters, a signal parameter must have a
617type that is supported by the QML engine; see
618\l {Data Type Conversion Between QML and C++}. (Using an
619unregistered type will not generate an error, but the parameter value will
620not be accessible from the handler.)
621
622Classes may have multiple signals with the same name, but only the final
623signal is accessible as a QML signal. Note that signals with the same name
624but different parameters cannot be distinguished from one another.
625
626\sa {qqmlintegration.h}{QML Type Registration Macros}, {Defining QML Types from C++}
627*/