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
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
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
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 an existing object as a singleton
132
133
Sometimes, you have an existing object that might have been created via
134
some third-party API. Often, the right choice in this case is to have one
135
singleton, which exposes those objects as its properties (see
136
\l{Grouping together related data}).
137
But if that is not the case, for example because there is only a single object that needs
138
to be exposed, use the following approach to expose an instance of type
139
\c{MySingleton} to the engine.
140
We first expose the Singleton as a \l{QML_FOREIGN}{foreign type}:
141
\code
142
struct SingletonForeign
143
{
144
Q_GADGET
145
QML_FOREIGN(MySingleton)
146
QML_SINGLETON
147
QML_NAMED_ELEMENT(MySingleton)
148
public:
149
150
inline static MySingleton *s_singletonInstance = nullptr;
151
152
static MySingleton *create(QQmlEngine *, QJSEngine *engine)
153
{
154
// The instance has to exist before it is used. We cannot replace it.
155
Q_ASSERT(s_singletonInstance);
156
157
// The engine has to have the same thread affinity as the singleton.
158
Q_ASSERT(engine->thread() == s_singletonInstance->thread());
159
160
// There can only be one engine accessing the singleton.
161
if (s_engine)
162
Q_ASSERT(engine == s_engine);
163
else
164
s_engine = engine;
165
166
// Explicitly specify C++ ownership so that the engine doesn't delete
167
// the instance.
168
QJSEngine::setObjectOwnership(s_singletonInstance,
169
QJSEngine::CppOwnership);
170
return s_singletonInstance;
171
}
172
173
private:
174
inline static QJSEngine *s_engine = nullptr;
175
};
176
\endcode
177
Then we set \c{SingletonForeign::s_singletonInstance} before we start
178
the first engine
179
\code
180
SingletonForeign::s_singletonInstance = getSingletonInstance();
181
QQmlApplicationEngine engine;
182
engine.loadFromModule("MyModule", "Main");
183
\endcode
184
185
\note It can be very tempting to simply use \l{qmlRegisterSingletonInstance} in
186
this case. However, be wary of the pitfalls of imperative type registration
187
listed in the next section.
188
189
\section3 Imperative type registration
190
Before Qt 5.15, all types, including singletons were registered via the
191
\c{qmlRegisterType} API. Singletons specifically were registered via either
192
\l{qmlRegisterSingletonType} or \l{qmlRegisterSingletonInstance}. Besides the
193
minor annoyance of having to repeat the module name for each type and the forced
194
decoupling of the class declaration and its registration, the major problem with
195
that approach was that it is tooling unfriendly: It was not statically possible
196
to extract all the necessary information about the types of a module at compile
197
time. The declarative registration solved this issue.
198
199
\note There is one remaining use case for the imperative \c{qmlRegisterType} API:
200
It is a way to expose a singleton of non-QObject type as a \c{var} property via
201
\l{qmlRegisterSingletonType}{the QJSValue based \c{qmlRegisterSingletonType} overload}
202
. Prefer the alternative: Expose that value as the property of a (\c{QObject}) based
203
singleton, so that type information will be available.
204
205
\section2 Accessing singletons
206
Singletons can be accessed both from QML as well as from C++. In QML, you need
207
to import the containing module. Afterwards, you can access the singleton via its
208
name. Reading its properties and writing to them inside JavaScript contexts is
209
done in the same way as with normal objects:
210
211
\code
212
import QtQuick
213
import MyModule
214
215
Item {
216
x: MySingleton.posX
217
Component.onCompleted: MySingleton.ready = true;
218
}
219
\endcode
220
221
Setting up bindings on a singletons properties is not possible; however, if it
222
is needed, a \l{Binding} element can be used to achieve the same result:
223
\code
224
import QtQuick
225
import MyModule
226
227
Item {
228
id: root
229
Binding {
230
target: MySingleton
231
property: "posX"
232
value: root.x
233
}
234
}
235
\endcode
236
237
\note Care must be taken when installing a binding on a singleton property: If
238
done by more than one file, the results are not defined.
239
240
\section1 Guidelines for (not) using singletons
241
242
Singletons allow you to expose data which needs to be accessed in multiple places
243
to the engine. That can be globally shared settings, like the spacing between
244
elements, or data models which need to be displayed in multiple places.
245
Compared to context properties which can solve a similar use case,
246
they have the benefit of being typed, being supported by tooling like the
247
\l{\QMLLS}, and they are also generally faster at runtime.
248
249
It is recommended not to register too many singletons in a module: Singletons,
250
once created, stay alive until the engine itself gets destroyed
251
and come with the drawbacks of shared state as they are part of the global state.
252
Thus consider using the following techniques to reduce the amount of singletons
253
in your application:
254
255
\section2 Grouping together related data
256
Adding one singleton for each object which you want to expose adds quite some boiler plate.
257
Most of the time, it makes more sense to group data you want to expose together as properties
258
of a single singleton. Assume for instance that you want to create an ebook reader
259
where you need to expose three \l{QAbstractItemModel}{abstract item models}, one
260
for local books, and two for remote sources. Instead of repeating the process
261
for \l{Exposing an existing object as a singleton}{exposing existing objects}
262
three times, you can instead create one singleton and set it up before starting
263
the main application:
264
\code
265
class GlobalState : QObject
266
{
267
Q_OBJECT
268
QML_ELEMENT
269
QML_SINGLETON
270
Q_PROPERTY(QAbstractItemModel* localBooks MEMBER localBooks)
271
Q_PROPERTY(QAbstractItemModel* digitalStoreFront MEMBER digitalStoreFront)
272
Q_PROPERTY(QAbstractItemModel* publicLibrary MEMBER publicLibrary)
273
public:
274
QAbstractItemModel* localBooks;
275
QAbstractItemModel* digitalStoreFront;
276
QAbstractItemModel* publicLibrary
277
};
278
279
int main() {
280
QQmlApplicationEngine engine;
281
auto globalState = engine.singletonInstance<GlobalState *>("MyModule", "GlobalState");
282
globalState->localBooks = getLocalBooks();
283
globalState->digitalStoreFront = setupLoalStoreFront();
284
globalState->publicLibrary = accessPublicLibrary();
285
engine.loadFromModule("MyModule", "Main");
286
}
287
\endcode
288
289
\section2 Use object instances
290
In the last section, we had the example of exposing three models as members of a
291
singleton. That can be useful when either the models need to be used in multiple
292
places, or when they are provided by some external API over which we have no
293
control. However, if we need the models only in a single place it might make
294
more sense have them as an instantiable type. Coming back to the previous example,
295
we can add an instantiable RemoteBookModel class, and then instantiate it inside
296
the book browser QML file:
297
298
299
\code
300
// remotebookmodel.h
301
class RemoteBookModel : public QAbstractItemModel
302
{
303
Q_OBJECT
304
QML_ELEMENT
305
Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged)
306
// ...
307
};
308
309
// bookbrowser.qml
310
Row {
311
ListView {
312
model: RemoteBookModel { url: "www.public-lib.example"}
313
}
314
ListView {
315
model: RemoteBookModel { url: "www.store-front.example"}
316
}
317
}
318
319
\endcode
320
321
\section2 Passing initial state
322
323
While singletons can be used to pass state to QML, they are wasteful when the
324
state is only needed for the initial setup of the application. In that case, it
325
is often possible to use \l{QQmlApplicationEngine::setInitialProperties}.
326
You might for instance want to set \l{Window::visibility} to fullscreen if
327
a corresponding command line flag has been set:
328
\code
329
QQmlApplicationEngine engine;
330
if (parser.isSet(fullScreenOption)) {
331
// assumes root item is ApplicationWindow
332
engine.setInitialProperties(
333
{ "visibility", QVariant::fromValue(QWindow::FullScreen)}
334
);
335
}
336
engine.loadFromModule("MyModule, "Main");
337
\endcode
338
339
*/
qtdeclarative
src
qml
doc
src
qmlsingletons.qdoc
Generated on
for Qt by
1.14.0