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