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
interactqmlfromcpp.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-interactqmlfromcpp.html
5\title Interacting with QML Objects from C++
6\brief Description of how to load and access QML objects from C++ code
7
8All QML object types are QObject-derived types, whether they are internally
9implemented by the engine or \l
10{qtqml-cppintegration-definetypes.html}{defined by third-party
11sources}. This means the QML engine can use the Qt \l{Meta Object System} to
12dynamically instantiate any QML object type and inspect the created objects.
13
14This is useful for creating QML objects from C++ code, whether to display a QML
15object that can be visually rendered, or to integrate non-visual QML object data
16into a C++ application. Once a QML object is created, it can be inspected from
17C++ in order to read and write to properties, invoke methods and receive signal
18notifications.
19
20For more information about C++ and the different QML integration methods,
21see the
22\l {Overview - QML and C++ Integration} {C++ and QML integration overview} page.
23
24\section1 Loading QML Objects from C++
25
26A QML document can be loaded with QQmlComponent or QQuickView. QQmlComponent
27loads a QML document as a C++ object that can then be modified from C++ code.
28QQuickView also does this, but as QQuickView is a QWindow-derived class, the
29loaded object will also be rendered into a visual display; QQuickView is
30generally used to integrate a displayable QML object into an application's
31user interface.
32
33For example, suppose there is a \c MyItem.qml file that looks like this:
34
35\snippet qml/qtbinding/loading/MyItem.qml start
36\snippet qml/qtbinding/loading/MyItem.qml end
37
38This QML document can be loaded with QQmlComponent or QQuickView with the
39following
40C++ code. Using a QQmlComponent requires calling QQmlComponent::create() to
41create
42a new instance of the component, while a QQuickView automatically creates an
43instance of the
44component, which is accessible via QQuickView::rootObject():
45
46\table
47\row
48\li
49\snippet qml/qtbinding/loading/main.cpp QQmlComponent-a
50\dots 0
51\snippet qml/qtbinding/loading/main.cpp QQmlComponent-b
52\li
53\snippet qml/qtbinding/loading/main.cpp QQuickView
54\endtable
55
56This \c object is the instance of the \c MyItem.qml component that has been
57created. You can now modify the item's properties using
58\l QObject::setProperty() or \l QQmlProperty::write():
59
60\snippet qml/qtbinding/loading/main.cpp properties
61
62The difference between \c QObject::setProperty() and \c QQmlProperty::write()
63is that the latter will also remove the binding in addition to setting the
64property value. For example, suppose the \c width assignment above had been a
65binding to \c height:
66
67\code
68 width: height
69\endcode
70
71If the \c height of the \c Item changed after the
72\c {object->setProperty("width", 500)} call, the \c width would be updated
73again, as the binding remains active. However, if the \c height changes after the
74\c {QQmlProperty(object, "width").write(500)} call, the \c width will not be
75changed, as the binding does not exist anymore.
76
77Alternatively, you can cast the object to its actual type and call methods with
78compile-time safety. In this case the base object of \c MyItem.qml is an
79\l Item, which is defined by the QQuickItem class:
80
81\snippet qml/qtbinding/loading/main.cpp cast
82
83You can also connect to any signals or call methods defined in the component
84using QMetaObject::invokeMethod() and QObject::connect(). See \l {Invoking QML Methods}
85and \l {Connecting to QML Signals} below for further details.
86
87\section1 Accessing QML Objects via well-defined C++ Interfaces
88
89The best way of interacting with QML from C++ is to define an interface for
90doing so in C++ and accessing it in QML itself. With other methods, refactoring
91your QML code can easily lead to your QML / C++ interaction breaking. It also
92helps to reason about the interaction of QML and C++ code, as having it driven
93via QML can be more easily reasoned about by both users and tooling such as
94qmllint. Accessing QML from C++ will lead to QML code that cannot be understood
95without manually verifying that no outside C++ code is modifying a given QML
96component, and even then the extent of the access might change over time, making
97continued use of this strategy a maintenance burden.
98
99To let QML drive the interaction, first you need to define a C++ interface:
100
101\code
102class CppInterface : public QObject
103{
104 Q_OBJECT
105 QML_ELEMENT
106 // ...
107};
108\endcode
109
110Using a QML-driven approach, this interface can be interacted with in two ways:
111
112\section2 Singletons
113
114One option is to register the interface as a singleton by adding the \l
115QML_SINGLETON macro to the interface, exposing it to all components. Following
116that, the interface becomes available via a simple import statement:
117
118\code
119import my.company.module
120
121Item {
122 Component.onCompleted: {
123 CppInterface.foo();
124 }
125}
126\endcode
127
128Use this approach if you need your interface in more places than the root component, as
129simply passing down an object would require explicitly passing it on to other
130components via a property or utilizing the slow and not recommended method of
131using \l {Unqualified access}{unqualified access}.
132
133\section2 Initial properties
134
135Another option is to mark the interface as uncreatable via \l QML_UNCREATABLE
136and supplying it to the root QML Component by using \l
137QQmlComponent::createWithInitialProperties() and a \l {Required
138Properties}{required property} on the QML end.
139
140Your root component may look something like this:
141
142\code
143import QtQuick
144
145Item {
146 required property CppInterface interface
147 Component.onCompleted: {
148 interface.foo();
149 }
150}
151\endcode
152
153Marking the property as required here protects the component against being
154created without the interface property being set.
155
156You can then initialize your component in the same way as outlined in \l
157{Loading QML Objects from C++} except using \c {createWithInitialProperties()}:
158
159\code
160 component.createWithInitialProperties(QVariantMap{{u"interface"_s, QVariant::fromValue<CppInterface *>(new CppInterface)}});
161\endcode
162
163This method is to be preferred if you know that your interface only needs to be
164available to the root component. It also allows for connecting to signals and
165slots of the interface more easily on the C++ side.
166
167If neither of these methods suit your needs you may want to investigate the usage of
168\l {Using C++ Models with Qt Quick Views}{C++ models} instead.
169
170\section1 Accessing Loaded QML Objects by Object Name
171
172QML components are essentially object trees with children that have siblings and
173their own children. Child objects of QML components can be located using the
174QObject::objectName property with QObject::findChild(). For example, if the root
175item in \c MyItem.qml had a child \l Rectangle item:
176
177\snippet qml/qtbinding/loading/MyItem.qml start
178\codeline
179\snippet qml/qtbinding/loading/MyItem.qml child
180\snippet qml/qtbinding/loading/MyItem.qml end
181
182The child could be located like this:
183
184\snippet qml/qtbinding/loading/main.cpp findChild
185
186Note that an object may have multiple children with the same \c objectName.
187For example, \l ListView creates multiple instances of its delegate, so if its
188delegate is declared with a particular objectName, the \l ListView will have
189multiple children with the same \c objectName. In this case,
190QObject::findChildren() can be used to find all children with a matching
191\c objectName.
192
193\include warning.qdocinc
194
195\section1 Accessing Members of a QML Object Type from C++
196
197\section2 Properties
198
199Any properties declared in a QML object are automatically accessible from C++.
200Given a QML item like this:
201
202\snippet qml/qtbinding/properties-qml/MyItem.qml 0
203
204The value of the \c someNumber property can be set and read using QQmlProperty,
205or QObject::setProperty() and QObject::property():
206
207\snippet qml/qtbinding/properties-qml/main.cpp 0
208
209You should always use QObject::setProperty(), QQmlProperty or
210QMetaProperty::write() to change a QML property value, to ensure the QML
211engine is made aware of the property change. For example, say you have a
212custom type \c PushButton with a \c buttonText property that internally
213reflects the value of a \c m_buttonText member variable. Modifying the member
214variable directly like this is not a good idea:
215
216\code
217//bad code
218QQmlComponent component(engine, "MyButton.qml");
219PushButton *button = qobject_cast<PushButton*>(component.create());
220button->m_buttonText = "Click me";
221\endcode
222
223Since the value is changed directly, this bypasses Qt's \l{The Meta-Object
224System}{meta-object system} and the QML engine is not made aware of the
225property change. This means property bindings to \c buttonText would not be
226updated, and any \c onButtonTextChanged handlers would not be called.
227
228\section2 Invoking QML Methods
229
230All QML methods are exposed to the meta-object system and can be called from
231C++ using QMetaObject::invokeMethod(). You can specify types for the parameters
232and the return value after the colon character, as shown in the code snippet
233below. This can be useful, for example, when you want to connect a signal in
234C++ with a certain signature to a QML-defined method. If you omit the types,
235the C++ signature will use QVariant.
236
237Here is a C++ application that calls a QML method using
238QMetaObject::invokeMethod():
239
240\table
241\row
242\li QML
243\li \snippet qml/qtbinding/functions-qml/MyItem.qml 0
244\row
245\li C++
246\li \snippet qml/qtbinding/functions-qml/main.cpp 0
247\endtable
248
249Notice the parameter and return type specified after the colon. You can use \l
250{QML Value Types}{value types} and \l {QML Object Types}{object types} as type
251names.
252
253If the type is omitted or specified as \c var in QML, then you must pass
254QVariant as type with Q_RETURN_ARG() and Q_ARG() when calling
255QMetaObject::invokeMethod.
256
257\section2 Connecting to QML Signals
258
259All QML signals are automatically available to C++, and can be connected to
260using QObject::connect() like any ordinary Qt C++ signal. In return, any C++
261signal can be received by a QML object using
262\l {qtqml-syntax-signals.html}{signal handlers}.
263
264Here is a QML component with a signal named \c qmlSignal that is emitted with
265a string-type parameter. This signal is connected to a C++ object's slot using
266QObject::connect(), so that the \c cppSlot() method is called whenever the
267\c qmlSignal is emitted:
268
269\table
270\row
271\li
272\snippet qml/qtbinding/signals-qml/MyItem.qml 0
273\row
274\li
275\snippet qml/qtbinding/signals-qml/myclass.h 0
276\codeline
277\snippet qml/qtbinding/signals-qml/main.cpp 0
278\endtable
279
280A QML object type in a signal parameter is translated to a pointer to the class
281in C++:
282
283\table
284\row
285\li
286
287\qml
288 // MyItem.qml
289 import QtQuick 2.0
290
291 Item {
292 id: item
293 width: 100; height: 100
294
295 signal qmlSignal(anObject: Item)
296
297 MouseArea {
298 anchors.fill: parent
299 onClicked: item.qmlSignal(item)
300 }
301 }
302\endqml
303
304\li
305\code
306 class MyClass : public QObject
307 {
308 Q_OBJECT
309 public slots:
310 void cppSlot(QQuickItem *item) {
311 qDebug() << "Called the C++ slot with item:" << item;
312
313 qDebug() << "Item dimensions:" << item->width()
314 << item->height();
315 }
316 };
317
318 int main(int argc, char *argv[]) {
319 QGuiApplication app(argc, argv);
320
321 QQuickView view(QUrl::fromLocalFile("MyItem.qml"));
322 QObject *item = view.rootObject();
323
324 MyClass myClass;
325 QObject::connect(item, SIGNAL(qmlSignal(QVariant)),
326 &myClass, SLOT(cppSlot(QVariant)));
327
328 view.show();
329 return app.exec();
330 }
331\endcode
332\endtable
333
334*/