1// Copyright (C) 2023 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GFDL-1.3-no-invariants-only
5\page qml-singleton.html
6\title Singletons in QML
7\brief A guide for using singletons in QML
9In 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
12and \l{Accessing singletons}{how to use them}. We'll also provide some
13best practices for working with singletons.
15\section1 How can singletons be created in QML?
17There are two separate ways of creating singletons in QML. You can either define
18the singleton in a QML file, or register it from C++.
20\section2 Defining singletons in QML
21To define a singleton in QML, you first have to add
25to the top of your file.
26There's one more step: You will need to add an entry to the QML module's
27\l{Module Definition qmldir Files}{qmldir file}.
29\section3 Using qt_add_qml_module (CMake)
30When using CMake, the qmldir is automatically created by \l{qt_add_qml_module}.
31To indicate that the QML file should be turned into a singleton, you need to set
32the \c{QT_QML_SINGLETON_TYPE}
35set_source_files_properties(MySingleton.qml
36 PROPERTIES QT_QML_SINGLETON_TYPE TRUE)
39You can pass multiple files at once to \c{set_source_files_properties}:
50set_source_files_properties(${qml_singletons}
51 PROPERTIES QT_QML_SINGLETON_TYPE TRUE)
52qt_add_qml_module(myapp
54 QML_FILES ${plain_qml_files} ${qml_singletons}
58\note set_source_files_properties needs to be called before \c{qt_add_qml_module}
60\section3 Without qt_add_qml_module
61If you aren't using \c{qt_add_qml_module}, you'll need to manually create a
62\l{Module Definition qmldir Files}{qmldir file}.
63There, you'll need to mark your singletons accordingly:
66singleton MySingleton 1.0 MySingleton.qml
67singleton MyOtherSingleton 1.0 MyOtherSingleton.qml
69See also \l{Object Type Declaration} for more details.
72\section2 Defining singletons in C++
74There are multiple ways of exposing singletons to QML from C++. The main
75difference depends on whether a new instance of a class should be created when
76needed by the QML engine; or if some existing object needs to be exposed to a
79\section3 Registering a class to provide singletons
81The simplest way of defining a singleton is to have a default-constructible
82class, which derives from QObject and mark it with the \l{QML_SINGLETON} and
83\l{QML_ELEMENT} macros.
85class MySingleton : public QObject
91 MySingleton(QObject *parent = nullptr) : QObject(parent) {
96This will register the \c{MySingleton} class under the name \c{MySingleton} in
97the QML module to which the file belongs.
98If you want to expose it under a different name, you can use \l{QML_NAMED_ELEMENT}
101If the class can't be made default-constructible, or if you need access to
102the \l{QQmlEngine} in which the singleton is instantiated, it is possible to
103use a static create function instead. It must have the signature
104\c{MySingleton *create(QQmlEngine *, QJSEngine *)}, where \c{MySingleton} is
105the type of the class that gets registered.
107class MyNonDefaultConstructibleSingleton : public QObject
111 QML_NAMED_ELEMENT(MySingleton)
113 MyNonDefaultConstructibleSingleton(QJSValue id, QObject *parent = nullptr)
115 , m_symbol(std::move(id))
118 static MyNonDefaultConstructibleSingleton *create(QQmlEngine *qmlEngine, QJSEngine *)
120 return new MyNonDefaultConstructibleSingleton(qmlEngine->newSymbol(u"MySingleton"_s));
128\note The create function takes both a \l{QJSEngine} and a \l{QQmlEngine} parameter. That is
129for historical reasons. They both point to the same object which is in fact a QQmlEngine.
131\section3 Exposing an existing object as a singleton
133Sometimes, you have an existing object that might have been created via
134some third-party API. Often, the right choice in this case is to have one
135singleton, which exposes those objects as its properties (see
136\l{Grouping together related data}).
137But if that is not the case, for example because there is only a single object that needs
138to be exposed, use the following approach to expose an instance of type
139\c{MySingleton} to the engine.
140We first expose the Singleton as a \l{QML_FOREIGN}{foreign type}:
142struct SingletonForeign
145 QML_FOREIGN(MySingleton)
147 QML_NAMED_ELEMENT(MySingleton)
150 inline static MySingleton *s_singletonInstance = nullptr;
152 static MySingleton *create(QQmlEngine *, QJSEngine *engine)
154 // The instance has to exist before it is used. We cannot replace it.
155 Q_ASSERT(s_singletonInstance);
157 // The engine has to have the same thread affinity as the singleton.
158 Q_ASSERT(engine->thread() == s_singletonInstance->thread());
160 // There can only be one engine accessing the singleton.
162 Q_ASSERT(engine == s_engine);
166 // Explicitly specify C++ ownership so that the engine doesn't delete
168 QJSEngine::setObjectOwnership(s_singletonInstance,
169 QJSEngine::CppOwnership);
170 return s_singletonInstance;
174 inline static QJSEngine *s_engine = nullptr;
177Then we set \c{SingletonForeign::s_singletonInstance} before we start
180SingletonForeign::s_singletonInstance = getSingletonInstance();
181QQmlApplicationEngine engine;
182engine.loadFromModule("MyModule", "Main");
185\note It can be very tempting to simply use \l{qmlRegisterSingletonInstance} in
186this case. However, be wary of the pitfalls of imperative type registration
187listed in the next section.
189\section3 Imperative type registration
190Before 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
193minor annoyance of having to repeat the module name for each type and the forced
194decoupling of the class declaration and its registration, the major problem with
195that approach was that it is tooling unfriendly: It was not statically possible
196to extract all the necessary information about the types of a module at compile
197time. The declarative registration solved this issue.
199\note There is one remaining use case for the imperative \c{qmlRegisterType} API:
200It 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
203singleton, so that type information will be available.
205\section2 Accessing singletons
206Singletons can be accessed both from QML as well as from C++. In QML, you need
207to import the containing module. Afterwards, you can access the singleton via its
208name. Reading its properties and writing to them inside JavaScript contexts is
209done in the same way as with normal objects:
217 Component.onCompleted: MySingleton.ready = true;
221Setting up bindings on a singletons properties is not possible; however, if it
222is needed, a \l{Binding} element can be used to achieve the same result:
237\note Care must be taken when installing a binding on a singleton property: If
238done by more than one file, the results are not defined.
240\section1 Guidelines for (not) using singletons
242Singletons allow you to expose data which needs to be accessed in multiple places
243to the engine. That can be globally shared settings, like the spacing between
244elements, or data models which need to be displayed in multiple places.
245Compared to context properties which can solve a similar use case,
246they have the benefit of being typed, being supported by tooling like the
247\l{\QMLLS Reference}{\QMLLS}, and they are also generally faster at runtime.
249It is recommended not to register too many singletons in a module: Singletons,
250once created, stay alive until the engine itself gets destroyed
251and come with the drawbacks of shared state as they are part of the global state.
252Thus consider using the following techniques to reduce the amount of singletons
255\section2 Grouping together related data
256Adding one singleton for each object which you want to expose adds quite some boiler plate.
257Most of the time, it makes more sense to group data you want to expose together as properties
258of a single singleton. Assume for instance that you want to create an ebook reader
259where you need to expose three \l{QAbstractItemModel}{abstract item models}, one
260for local books, and two for remote sources. Instead of repeating the process
261for \l{Exposing an existing object as a singleton}{exposing existing objects}
262three times, you can instead create one singleton and set it up before starting
265class GlobalState : QObject
270 Q_PROPERTY(QAbstractItemModel* localBooks MEMBER localBooks)
271 Q_PROPERTY(QAbstractItemModel* digitalStoreFront MEMBER digitalStoreFront)
272 Q_PROPERTY(QAbstractItemModel* publicLibrary MEMBER publicLibrary)
274 QAbstractItemModel* localBooks;
275 QAbstractItemModel* digitalStoreFront;
276 QAbstractItemModel* publicLibrary
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");
289\section2 Use object instances
290In the last section, we had the example of exposing three models as members of a
291singleton. That can be useful when either the models need to be used in multiple
292places, or when they are provided by some external API over which we have no
293control. However, if we need the models only in a single place it might make
294more sense have them as an instantiable type. Coming back to the previous example,
295we can add an instantiable RemoteBookModel class, and then instantiate it inside
296the book browser QML file:
301class RemoteBookModel : public QAbstractItemModel
305 Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged)
312 model: RemoteBookModel { url: "www.public-lib.example"}
315 model: RemoteBookModel { url: "www.store-front.example"}
321\section2 Passing initial state
323While singletons can be used to pass state to QML, they are wasteful when the
324state is only needed for the initial setup of the application. In that case, it
325is often possible to use \l{QQmlApplicationEngine::setInitialProperties}.
326You might for instance want to set \l{Window::visibility} to fullscreen if
327a corresponding command line flag has been set:
329QQmlApplicationEngine engine;
330if (parser.isSet(fullScreenOption)) {
331 // assumes root item is ApplicationWindow
332 engine.setInitialProperties(
333 { "visibility", QVariant::fromValue(QWindow::FullScreen)}
336engine.loadFromModule("MyModule, "Main");