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
qmlsingletons.qdoc
Go to the documentation of this file.
1
// Copyright (C) 2023 The Qt Company Ltd.
2
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
3
4
/*!
5
\page qml-singleton.html
6
\title Singletons in QML
7
\brief A guide for using singletons in QML
8
9
In QML, a singleton is an object which is created at most once per
10
\l{QQmlEngine}{engine}. In this guide, we'll
11
\l{How can singletons be created in QML}{explain how to create} singletons
12
and \l{Accessing singletons}{how to use them}. We'll also provide some
13
best practices for working with singletons.
14
15
\section1 How can singletons be created in QML?
16
17
There are two separate ways of creating singletons in QML. You can either define
18
the singleton in a QML file, or register it from C++.
19
20
\section2 Defining singletons in QML
21
To define a singleton in QML, you first have to add
22
\code
23
pragma Singleton
24
\endcode
25
to the top of your file.
26
There's one more step: You will need to add an entry to the QML module's
27
\l{Module Definition qmldir Files}{qmldir file}.
28
29
\section3 Using qt_add_qml_module (CMake)
30
When using CMake, the qmldir is automatically created by \l{qt_add_qml_module}.
31
To indicate that the QML file should be turned into a singleton, you need to set
32
the \c{QT_QML_SINGLETON_TYPE}
33
file property on it:
34
\code [cmake]
35
set_source_files_properties(MySingleton.qml
36
PROPERTIES QT_QML_SINGLETON_TYPE TRUE)
37
\endcode
38
39
You can pass multiple files at once to \c{set_source_files_properties}:
40
\code [cmake]
41
set(plain_qml_files
42
MyItem1.qml
43
MyItem2.qml
44
FancyButton.qml
45
)
46
set(qml_singletons
47
MySingleton.qml
48
MyOtherSingleton.qml
49
)
50
set_source_files_properties(${qml_singletons}
51
PROPERTIES QT_QML_SINGLETON_TYPE TRUE)
52
qt_add_qml_module(myapp
53
URI MyModule
54
QML_FILES ${plain_qml_files} ${qml_singletons}
55
)
56
\endcode
57
58
\note set_source_files_properties needs to be called before \c{qt_add_qml_module}
59
60
\section3 Without qt_add_qml_module
61
If you aren't using \c{qt_add_qml_module}, you'll need to manually create a
62
\l{Module Definition qmldir Files}{qmldir file}.
63
There, you'll need to mark your singletons accordingly:
64
\code [text]
65
module MyModule
66
singleton MySingleton 1.0 MySingleton.qml
67
singleton MyOtherSingleton 1.0 MyOtherSingleton.qml
68
\endcode
69
See also \l{Object Type Declaration} for more details.
70
71
72
\section2 Defining singletons in C++
73
74
There are multiple ways of exposing singletons to QML from C++. The main
75
difference depends on whether a new instance of a class should be created when
76
needed by the QML engine; or if some existing object needs to be exposed to a
77
QML program.
78
79
\section3 Registering a class to provide singletons
80
81
The simplest way of defining a singleton is to have a default-constructible
82
class, which derives from QObject and mark it with the \l{QML_SINGLETON} and
83
\l{QML_ELEMENT} macros.
84
\code
85
class MySingleton : public QObject
86
{
87
Q_OBJECT
88
QML_SINGLETON
89
QML_ELEMENT
90
public:
91
MySingleton(QObject *parent = nullptr) : QObject(parent) {
92
// ...
93
}
94
};
95
\endcode
96
This will register the \c{MySingleton} class under the name \c{MySingleton} in
97
the QML module to which the file belongs.
98
If you want to expose it under a different name, you can use \l{QML_NAMED_ELEMENT}
99
instead.
100
101
If the class can't be made default-constructible, or if you need access to
102
the \l{QQmlEngine} in which the singleton is instantiated, it is possible to
103
use a static create function instead. It must have the signature
104
\c{MySingleton *create(QQmlEngine *, QJSEngine *)}, where \c{MySingleton} is
105
the type of the class that gets registered.
106
\code
107
class MyNonDefaultConstructibleSingleton : public QObject
108
{
109
Q_OBJECT
110
QML_SINGLETON
111
QML_NAMED_ELEMENT(MySingleton)
112
public:
113
MyNonDefaultConstructibleSingleton(QJSValue id, QObject *parent = nullptr)
114
: QObject(parent)
115
, m_symbol(std::move(id))
116
{}
117
118
static MyNonDefaultConstructibleSingleton *create(QQmlEngine *qmlEngine, QJSEngine *)
119
{
120
return new MyNonDefaultConstructibleSingleton(qmlEngine->newSymbol(u"MySingleton"_s));
121
}
122
123
private:
124
QJSValue m_symbol;
125
};
126
\endcode
127
128
\note The create function takes both a \l{QJSEngine} and a \l{QQmlEngine} parameter. That is
129
for historical reasons. They both point to the same object which is in fact a QQmlEngine.
130
131
\section3 Exposing a C++-instantiated object as a singleton
132
133
It can be useful to control the instantiation of a singleton from C++, instead
134
of letting the \l{QQmlEngine} instantiate the instance. Such singletons do not need
135
to be instantiatable by the engine, so you can forgo having a default constructor
136
or a static \c{create} function. If you do, you must include the \l{QML_UNCREATABLE}
137
macro in the singleton declaration:
138
\code
139
class MyNonDefaultConstructibleSingleton : public QObject
140
{
141
Q_OBJECT
142
QML_SINGLETON
143
QML_NAMED_ELEMENT(MySingleton)
144
QML_UNCREATABLE("Provided by C++")
145
public:
146
MyNonDefaultConstructibleSingleton(BackendObject* backend, QObject *parent = nullptr)
147
: QObject(parent)
148
, m_backend(backend)
149
{}
150
151
private:
152
class BackendObject* backend;
153
};
154
\endcode
155
156
Then, we set the instance to use on the engine, before we start it:
157
158
\code
159
MyNonDefaultConstructibleSingleton singleton(backend);
160
QQmlApplicationEngine engine;
161
engine.setExternalSingletonInstance("MyModule", "MySingleton", &singleton);
162
engine.loadFromModule("MyModule", "Main");
163
\endcode
164
165
166
\section3 Exposing an existing object as a singleton
167
168
Sometimes, you have an existing object that might have been created via
169
some third-party API. Often, the right choice in this case is to have one
170
singleton, which exposes those objects as its properties (see
171
\l{Grouping together related data}).
172
But if that is not the case, for example because there is only a single object that needs
173
to be exposed, use the following approach to expose an instance of type
174
\c{MySingleton} to the engine.
175
We first expose the Singleton as a \l{QML_FOREIGN}{foreign type}:
176
\code
177
struct SingletonForeign
178
{
179
Q_GADGET
180
QML_FOREIGN(MySingleton)
181
QML_SINGLETON
182
QML_NAMED_ELEMENT(MySingleton)
183
QML_UNCREATABLE("Provided from C++")
184
};
185
\endcode
186
Then we simply instantiate the MySingleton, and set it to the engine:
187
\code
188
MySingleton instance = getSingletonInstance();
189
QQmlApplicationEngine engine;
190
engine.setExternalSingletonInstance("MyModule", "MySingleton", &instance);
191
engine.loadFromModule("MyModule", "Main");
192
\endcode
193
194
\note It can be very tempting to simply use \l{qmlRegisterSingletonInstance} in
195
this case. However, be wary of the pitfalls of imperative type registration
196
listed in the next section.
197
198
\section3 Imperative type registration
199
Before Qt 5.15, all types, including singletons were registered via the
200
\c{qmlRegisterType} API. Singletons specifically were registered via either
201
\l{qmlRegisterSingletonType} or \l{qmlRegisterSingletonInstance}. Besides the
202
minor annoyance of having to repeat the module name for each type and the forced
203
decoupling of the class declaration and its registration, the major problem with
204
that approach was that it is tooling unfriendly: It was not statically possible
205
to extract all the necessary information about the types of a module at compile
206
time. The declarative registration solved this issue.
207
208
\note There is one remaining use case for the imperative \c{qmlRegisterType} API:
209
It is a way to expose a singleton of non-QObject type as a \c{var} property via
210
\l{qmlRegisterSingletonType}{the QJSValue based \c{qmlRegisterSingletonType} overload}
211
. Prefer the alternative: Expose that value as the property of a (\c{QObject}) based
212
singleton, so that type information will be available.
213
214
\section2 Accessing singletons
215
Singletons can be accessed both from QML as well as from C++. In QML, you need
216
to import the containing module. Afterwards, you can access the singleton via its
217
name. Reading its properties and writing to them inside JavaScript contexts is
218
done in the same way as with normal objects:
219
220
\qml
221
import QtQuick
222
import MyModule
223
224
Item {
225
x: MySingleton.posX
226
Component.onCompleted: MySingleton.ready = true;
227
}
228
\endqml
229
230
Setting up bindings on a singletons properties is not possible; however, if it
231
is needed, a \l{Binding} element can be used to achieve the same result:
232
\qml
233
import QtQuick
234
import MyModule
235
236
Item {
237
id: root
238
Binding {
239
target: MySingleton
240
property: "posX"
241
value: root.x
242
}
243
}
244
\endqml
245
246
\note Care must be taken when installing a binding on a singleton property: If
247
done by more than one file, the results are not defined.
248
249
\section1 Guidelines for (not) using singletons
250
251
Singletons allow you to expose data which needs to be accessed in multiple places
252
to the engine. That can be globally shared settings, like the spacing between
253
elements, or data models which need to be displayed in multiple places.
254
Compared to context properties which can solve a similar use case,
255
they have the benefit of being typed, being supported by tooling like the
256
\l{\QMLLS}, and they are also generally faster at runtime.
257
258
It is recommended not to register too many singletons in a module: Singletons,
259
once created, stay alive until the engine itself gets destroyed
260
and come with the drawbacks of shared state as they are part of the global state.
261
Thus consider using the following techniques to reduce the amount of singletons
262
in your application:
263
264
\section2 Grouping together related data
265
Adding one singleton for each object which you want to expose adds quite some boiler plate.
266
Most of the time, it makes more sense to group data you want to expose together as properties
267
of a single singleton. Assume for instance that you want to create an ebook reader
268
where you need to expose three \l{QAbstractItemModel}{abstract item models}, one
269
for local books, and two for remote sources. Instead of repeating the process
270
for \l{Exposing an existing object as a singleton}{exposing existing objects}
271
three times, you can instead create one singleton and set it up before starting
272
the main application:
273
\code
274
class GlobalState : QObject
275
{
276
Q_OBJECT
277
QML_ELEMENT
278
QML_SINGLETON
279
Q_PROPERTY(QAbstractItemModel* localBooks MEMBER localBooks)
280
Q_PROPERTY(QAbstractItemModel* digitalStoreFront MEMBER digitalStoreFront)
281
Q_PROPERTY(QAbstractItemModel* publicLibrary MEMBER publicLibrary)
282
public:
283
QAbstractItemModel* localBooks;
284
QAbstractItemModel* digitalStoreFront;
285
QAbstractItemModel* publicLibrary
286
};
287
288
int main() {
289
QQmlApplicationEngine engine;
290
auto globalState = engine.singletonInstance<GlobalState *>("MyModule", "GlobalState");
291
globalState->localBooks = getLocalBooks();
292
globalState->digitalStoreFront = setupLoalStoreFront();
293
globalState->publicLibrary = accessPublicLibrary();
294
engine.loadFromModule("MyModule", "Main");
295
}
296
\endcode
297
298
\section2 Use object instances
299
In the last section, we had the example of exposing three models as members of a
300
singleton. That can be useful when either the models need to be used in multiple
301
places, or when they are provided by some external API over which we have no
302
control. However, if we need the models only in a single place it might make
303
more sense have them as an instantiable type. Coming back to the previous example,
304
we can add an instantiable RemoteBookModel class, and then instantiate it inside
305
the book browser QML file:
306
307
308
\code
309
// remotebookmodel.h
310
class RemoteBookModel : public QAbstractItemModel
311
{
312
Q_OBJECT
313
QML_ELEMENT
314
Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged)
315
// ...
316
};
317
\endcode
318
319
\qml
320
// bookbrowser.qml
321
Row {
322
ListView {
323
model: RemoteBookModel { url: "www.public-lib.example"}
324
}
325
ListView {
326
model: RemoteBookModel { url: "www.store-front.example"}
327
}
328
}
329
330
\endqml
331
332
\section2 Passing initial state
333
334
While singletons can be used to pass state to QML, they are wasteful when the
335
state is only needed for the initial setup of the application. In that case, it
336
is often possible to use \l{QQmlApplicationEngine::setInitialProperties}.
337
You might for instance want to set \l{Window::visibility} to fullscreen if
338
a corresponding command line flag has been set:
339
\code
340
QQmlApplicationEngine engine;
341
if (parser.isSet(fullScreenOption)) {
342
// assumes root item is ApplicationWindow
343
engine.setInitialProperties(
344
{ "visibility", QVariant::fromValue(QWindow::FullScreen)}
345
);
346
}
347
engine.loadFromModule("MyModule, "Main");
348
\endcode
349
350
*/
qtdeclarative
src
qml
doc
src
qmlsingletons.qdoc
Generated on
for Qt by
1.16.1