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
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 QtQuick so that your QML-exposed
45C++ types can use \l QColor as method arguments and return values. QtQuick
46exposes \l QColor as a \l {QML Value Types}{value type} \e color. Such
47dependencies may be automatically inferred at run time, but you should not rely
48on 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 when a type uses \l
216{Grouped Properties}, and the grouped property object is allocated once, and
217only freed when the object is deleted. In these cases, the CONSTANT
218attribute may be added to the property declaration instead of a NOTIFY
219signal.
220
221The CONSTANT attribute should only be used for properties whose value is set,
222and finalized, only in the class constructor. All other properties that want
223to be used in bindings should have a NOTIFY signal instead.
224
225
226\section2 Properties with Object Types
227
228Object-type properties are accessible from QML providing that the object type
229has been appropriately \l{Registering C++ types with the QML type
230system}{registered} with the QML type system.
231
232For example, the \c Message type might have a \c body property of type
233\c MessageBody*:
234
235\code
236class Message : public QObject
237{
238 Q_OBJECT
239 Q_PROPERTY(MessageBody* body READ body WRITE setBody NOTIFY bodyChanged)
240public:
241 MessageBody* body() const;
242 void setBody(MessageBody* body);
243};
244
245class MessageBody : public QObject
246{
247 Q_OBJECT
248 Q_PROPERTY(QString text READ text WRITE text NOTIFY textChanged)
249// ...
250}
251\endcode
252
253Suppose the \c Message type was \l{Registering C++ types with the QML type
254system}{registered} with the QML type system, allowing it to be used as an
255object type from QML code:
256
257\qml
258Message {
259 // ...
260}
261\endqml
262
263If the \c MessageBody type was also registered with the type system, it would be
264possible to assign \c MessageBody to the \c body property of a \c Message, all
265from within QML code:
266
267\qml
268Message {
269 body: MessageBody {
270 text: "Hello, world!"
271 }
272}
273\endqml
274
275
276\section2 Properties with Object-List Types
277
278Properties containing lists of QObject-derived types can also be exposed to
279QML. For this purpose, however, one should use QQmlListProperty rather than
280QList<T> as the property type. This is because QList is not a QObject-derived
281type, and so cannot provide the necessary QML property characteristics
282through the Qt meta object system, such as signal notifications when a list
283is modified.
284
285For example, the \c MessageBoard class below has a \c messages property of
286type QQmlListProperty that stores a list of \c Message instances:
287
288\code
289class MessageBoard : public QObject
290{
291 Q_OBJECT
292 Q_PROPERTY(QQmlListProperty<Message> messages READ messages)
293public:
294 QQmlListProperty<Message> messages();
295
296private:
297 static void append_message(QQmlListProperty<Message> *list, Message *msg);
298
299 QList<Message *> m_messages;
300};
301\endcode
302
303The MessageBoard::messages() function simply creates and returns a
304QQmlListProperty from its QList<T> \c m_messages member, passing the
305appropriate list modification functions as required by the QQmlListProperty
306constructor:
307
308\code
309QQmlListProperty<Message> MessageBoard::messages()
310{
311 return QQmlListProperty<Message>(this, 0, &MessageBoard::append_message);
312}
313
314void MessageBoard::append_message(QQmlListProperty<Message> *list, Message *msg)
315{
316 MessageBoard *msgBoard = qobject_cast<MessageBoard *>(list->object);
317 if (msg)
318 msgBoard->m_messages.append(msg);
319}
320\endcode
321
322Note that the template class type for the QQmlListProperty — in this case,
323\c Message — must be \l{Registering C++ types with the QML type system}
324{registered} with the QML type system.
325
326
327\section2 Grouped Properties
328\keyword Integrating QML and C++ - Grouped Properties
329
330Any read-only object-type property is accessible from QML code as a
331\e {grouped property}. This can be used to expose a group of related
332properties that describe a set of attributes for a type.
333
334For example, suppose the \c Message::author property was of type
335\c MessageAuthor rather than a simple string, with sub-properties
336of \c name and \c email:
337
338\code
339class MessageAuthor : public QObject
340{
341 Q_PROPERTY(QString name READ name WRITE setName)
342 Q_PROPERTY(QString email READ email WRITE setEmail)
343public:
344 ...
345};
346
347class Message : public QObject
348{
349 Q_OBJECT
350 Q_PROPERTY(MessageAuthor* author READ author)
351public:
352 Message(QObject *parent)
353 : QObject(parent), m_author(new MessageAuthor(this))
354 {
355 }
356 MessageAuthor *author() const {
357 return m_author;
358 }
359private:
360 MessageAuthor *m_author;
361};
362\endcode
363
364The \c author property could be written to using the
365\l{qtqml-syntax-objectattributes.html#grouped-properties}{grouped property
366syntax}
367in QML, like this:
368
369\qml
370Message {
371 author.name: "Alexandra"
372 author.email: "alexandra@mail.com"
373}
374\endqml
375
376A type that is exposed as a grouped property differs from an \l{Properties with
377Object Types}{object-type property} in that the grouped property is read-only,
378and is initialized to a valid value by the parent object at construction. The
379grouped property's sub-properties may be modified from QML but the grouped
380property object itself will never change, whereas an object-type property may be
381assigned a new object value from QML at any time. Thus, the lifetime of a
382grouped property object is controlled strictly by the C++ parent
383implementation, whereas an object-type property can be freely created and
384destroyed through QML code.
385
386
387\section1 Exposing Methods (Including Qt Slots)
388
389Any method of a QObject-derived type is accessible from QML code if it is:
390
391\list
392\li A public method flagged with the Q_INVOKABLE() macro
393\li A method that is a public Qt \l{Signals & Slots}{slot}
394\endlist
395
396For example, the \c MessageBoard class below has a \c postMessage() method that
397has been flagged with the Q_INVOKABLE macro, as well as a \c refresh() method
398that is a public slot:
399
400\code
401 class MessageBoard : public QObject
402 {
403 Q_OBJECT
404 QML_ELEMENT
405
406 public:
407 Q_INVOKABLE bool postMessage(const QString &msg) {
408 qDebug() << "Called the C++ method with" << msg;
409 return true;
410 }
411
412 public slots:
413 void refresh() {
414 qDebug() << "Called the C++ slot";
415 }
416 };
417\endcode
418
419If an instance of \c MessageBoard was set as the required property for a file \c
420MyItem.qml, then \c MyItem.qml could invoke the two methods as shown in the
421examples below:
422
423\table
424\row
425\li C++
426\li
427\code
428 int main(int argc, char *argv[]) {
429 QGuiApplication app(argc, argv);
430
431 MessageBoard msgBoard;
432 QQuickView view;
433 view.setInitialProperties({{"msgBoard", &msgBoard}});
434 view.setSource(QUrl::fromLocalFile("MyItem.qml"));
435 view.show();
436
437 return app.exec();
438 }
439\endcode
440\row
441\li QML
442\li
443\qml
444// MyItem.qml
445import QtQuick 2.0
446
447Item {
448 required property MessageBoard msgBoard
449
450 width: 100; height: 100
451
452 MouseArea {
453 anchors.fill: parent
454 onClicked: {
455 var result = msgBoard.postMessage("Hello from QML")
456 console.log("Result of postMessage():", result)
457 msgBoard.refresh();
458 }
459 }
460}
461\endqml
462\endtable
463
464If a C++ method has a parameter with a \c QObject* type, the parameter value
465can be passed from QML using an object \c id or a JavaScript \l var value
466that references the object.
467
468QML supports the calling of overloaded C++ functions. If there are multiple C++
469functions with the same name but different arguments, the correct function will
470be called according to the number and the types of arguments that are provided.
471
472Values returned from C++ methods are converted to JavaScript values when
473accessed from JavaScript expressions in QML.
474
475\section2 C++ methods and the 'this' object
476
477You may want to retrieve a C++ method from one object and call it on a different
478object. Consider the following example, within a QML module called \c{Example}:
479
480\table
481\row
482\li C++
483\li
484\code
485class Invokable : public QObject
486{
487 Q_OBJECT
488 QML_ELEMENT
489public:
490 Invokable(QObject *parent = nullptr) : QObject(parent) {}
491
492 Q_INVOKABLE void invoke() { qDebug() << "invoked on " << objectName(); }
493};
494\endcode
495\row
496\li QML
497\li
498\qml
499import QtQml
500import Example
501
502Invokable {
503 objectName: "parent"
504 property Invokable child: Invokable {}
505 Component.onCompleted: child.invoke.call(this)
506}
507\endqml
508\endtable
509
510If you load the QML code from a suitable main.cpp, it should print
511"invoked on parent". However, due to a long standing bug, it doesn't.
512Historically, the 'this' object of C++-based methods is inseparably bound to
513the method. Changing this behavior for existing code would cause subtle errors
514since the 'this' object is implicit in many places. Since Qt 6.5 you can
515explicitly opt into the correct behavior and allow C++ methods to accept a
516'this' object. To do so, add the following pragma to your QML documents:
517
518\qml
519pragma NativeMethodBehavior: AcceptThisObject
520\endqml
521
522With this line added, the example above will work as expected.
523
524\section1 Exposing Signals
525
526Any public \l{Signals & Slots}{signal} of a QObject-derived type is accessible
527from QML code.
528
529The QML engine automatically creates a \l{Signal and Handler Event
530System}{signal handler} for any signal of a QObject-derived type that is used
531from QML. Signal handlers are always named \e on<Signal> where \c <Signal> is
532the name of the signal, with the first letter capitalized. All parameters passed
533by the signal are available in the signal handler through the parameter names.
534
535For example, suppose the \c MessageBoard class has a \c newMessagePosted()
536signal with a single parameter, \c subject:
537
538\code
539 class MessageBoard : public QObject
540 {
541 Q_OBJECT
542 public:
543 // ...
544 signals:
545 void newMessagePosted(const QString &subject);
546 };
547\endcode
548
549If the \c MessageBoard type was \l{Registering C++ types with the QML type
550system}{registered} with the QML type system, then a \c MessageBoard object
551declared in QML could receive the \c newMessagePosted() signal using a signal
552handler named \c onNewMessagePosted, and examine the \c subject parameter
553value:
554
555\qml
556MessageBoard {
557 onNewMessagePosted: (subject)=> console.log("New message received:", subject)
558}
559\endqml
560
561As with property values and method parameters, a signal parameter must have a
562type that is supported by the QML engine; see
563\l {Data Type Conversion Between QML and C++}. (Using an
564unregistered type will not generate an error, but the parameter value will
565not be accessible from the handler.)
566
567Classes may have multiple signals with the same name, but only the final
568signal is accessible as a QML signal. Note that signals with the same name
569but different parameters cannot be distinguished from one another.
570
571
572*/