Singletons in QML
In QML ist ein Singleton ein Objekt, das pro „ engine “ höchstens einmal erstellt wird. In diesem Leitfaden erklären wir, wie man Singletons erstellt und wie man sie verwendet. Außerdem stellen wir einige Best Practices für die Arbeit mit Singletons vor.
Wie lassen sich Singletons in QML erstellen?
Es gibt zwei verschiedene Möglichkeiten, Singletons in QML zu erstellen. Sie können das Singleton entweder in einer QML-Datei definieren oder es über C++ registrieren.
Singletons in QML definieren
Um ein Singleton in QML zu definieren, müssen Sie zunächst
pragma Singletonam Anfang Ihrer Datei einfügen. Es gibt noch einen weiteren Schritt: Sie müssen einen Eintrag in die qmldir-Datei des QML-Moduls einfügen.
Verwendung von `qt_add_qml_module` (CMake)
Bei Verwendung von CMake wird die `qmldir`-Datei automatisch von `qt_add_qml_module` erstellt. Um anzugeben, dass die QML-Datei in ein Singleton umgewandelt werden soll, müssen Sie die Dateieigenschaft ` QT_QML_SINGLETON_TYPE ` dafür setzen:
set_source_files_properties(MySingleton.qml
PROPERTIES QT_QML_SINGLETON_TYPE TRUE)Sie können mehrere Dateien gleichzeitig an ` set_source_files_properties` übergeben:
set(plain_qml_files
MyItem1.qml
MyItem2.qml
FancyButton.qml
)
set(qml_singletons
MySingleton.qml
MyOtherSingleton.qml
)
set_source_files_properties(${qml_singletons}
PROPERTIES QT_QML_SINGLETON_TYPE TRUE)
qt_add_qml_module(myapp
URI MyModule
QML_FILES ${plain_qml_files} ${qml_singletons}
)Hinweis: `set_source_files_properties ` muss vor qt_add_qml_module
Ohne `qt_add_qml_module`
Wenn Sie ` qt_add_qml_module` nicht verwenden, müssen Sie manuell eine `qmldir`-Datei erstellen. Dort müssen Sie Ihre Singletons entsprechend kennzeichnen:
module MyModule
singleton MySingleton 1.0 MySingleton.qml
singleton MyOtherSingleton 1.0 MyOtherSingleton.qmlWeitere Details finden Sie unter „Objekttyp-Deklaration “.
Definition von Singletons in C++
Es gibt mehrere Möglichkeiten, Singletons aus C++ für QML verfügbar zu machen. Der Hauptunterschied besteht darin, ob bei Bedarf durch die QML-Engine eine neue Instanz einer Klasse erstellt werden soll oder ob ein bereits vorhandenes Objekt einem QML-Programm zur Verfügung gestellt werden soll.
Registrieren einer Klasse zur Bereitstellung von Singletons
Die einfachste Art, ein Singleton zu definieren, besteht darin, eine Klasse zu erstellen, die über einen Standardkonstruktor verfügt, von ` QObject ` abgeleitet ist und mit den Makros `QML_SINGLETON` und ` QML_ELEMENT ` gekennzeichnet ist.
class MySingleton : public QObject
{
Q_OBJECT
QML_SINGLETON
QML_ELEMENT
public:
MySingleton(QObject *parent = nullptr) : QObject(parent) {
// ...
}
};Dadurch wird die Klasse „ MySingleton “ unter dem Namen „ MySingleton “ in dem QML-Modul registriert, zu dem die Datei gehört. Wenn Sie sie unter einem anderen Namen verfügbar machen möchten, können Sie stattdessen „ QML_NAMED_ELEMENT “ verwenden.
Wenn die Klasse nicht standardmäßig konstruierbar gemacht werden kann oder wenn Sie Zugriff auf die ` QQmlEngine ` benötigen, in der das Singleton instanziiert wird, können Sie stattdessen eine statische `create`-Funktion verwenden. Diese muss die Signatur ` MySingleton *create(QQmlEngine *, QJSEngine *)` haben, wobei ` MySingleton ` der Typ der Klasse ist, die registriert wird.
class MyNonDefaultConstructibleSingleton : public QObject
{
Q_OBJECT
QML_SINGLETON
QML_NAMED_ELEMENT(MySingleton)
public:
MyNonDefaultConstructibleSingleton(QJSValue id, QObject *parent = nullptr)
: QObject(parent)
, m_symbol(std::move(id))
{}
static MyNonDefaultConstructibleSingleton *create(QQmlEngine *qmlEngine, QJSEngine *)
{
return new MyNonDefaultConstructibleSingleton(qmlEngine->newSymbol(u"MySingleton"_s));
}
private:
QJSValue m_symbol;
};Hinweis: Die `create`-Funktion nimmt sowohl einen ` QJSEngine `- als auch einen ` QQmlEngine `-Parameter entgegen. Dies hat historische Gründe. Beide verweisen auf dasselbe Objekt, bei dem es sich tatsächlich um ein ` QQmlEngine` handelt.
Ein in C++ instanziiertes Objekt als Singleton bereitstellen
Es kann sinnvoll sein, die Instanziierung eines Singletons von C++ aus zu steuern, anstatt die Instanz vom „ QQmlEngine “ instanziieren zu lassen. Solche Singletons müssen nicht von der Engine instanziierbar sein, sodass Sie auf einen Standardkonstruktor oder eine statische „ create “-Funktion verzichten können. Wenn Sie dies tun, müssen Sie das Makro „ QML_UNCREATABLE “ in die Singleton-Deklaration einfügen:
class MyNonDefaultConstructibleSingleton : public QObject
{
Q_OBJECT
QML_SINGLETON
QML_NAMED_ELEMENT(MySingleton)
QML_UNCREATABLE("Provided by C++")
public:
MyNonDefaultConstructibleSingleton(BackendObject* backend, QObject *parent = nullptr)
: QObject(parent)
, m_backend(backend)
{}
private:
class BackendObject* backend;
};Anschließend legen wir die Instanz fest, die von der Engine verwendet werden soll, bevor wir diese starten:
MyNonDefaultConstructibleSingleton singleton(backend);
QQmlApplicationEngine engine;
engine.setExternalSingletonInstance("MyModule", "MySingleton", &singleton);
engine.loadFromModule("MyModule", "Main");Ein bestehendes Objekt als Singleton bereitstellen
Manchmal verfügen Sie über ein vorhandenes Objekt, das möglicherweise über eine API eines Drittanbieters erstellt wurde. Oft ist es in diesem Fall die richtige Wahl, ein Singleton zu verwenden, das diese Objekte als seine Eigenschaften bereitstellt (siehe „Zusammenfassen verwandter Daten“). Ist dies jedoch nicht der Fall – beispielsweise, weil nur ein einziges Objekt bereitgestellt werden muss –, verwenden Sie den folgenden Ansatz, um eine Instanz vom Typ „ MySingleton “ für die Engine bereitzustellen. Zunächst stellen wir das Singleton als „ foreign type “ bereit:
struct SingletonForeign
{
Q_GADGET
QML_FOREIGN(MySingleton)
QML_SINGLETON
QML_NAMED_ELEMENT(MySingleton)
QML_UNCREATABLE("Provided from C++")
};Anschließend instanziieren wir einfach das `MySingleton` und übergeben es an die Engine:
MySingleton instance = getSingletonInstance();
QQmlApplicationEngine engine;
engine.setExternalSingletonInstance("MyModule", "MySingleton", &instance);
engine.loadFromModule("MyModule", "Main");Hinweis: Es kann sehr verlockend sein, in diesem Fall einfach ` qmlRegisterSingletonInstance ` zu verwenden. Seien Sie jedoch vorsichtig hinsichtlich der Fallstricke der imperativen Typregistrierung, die im nächsten Abschnitt aufgeführt sind.
Imperative Typregistrierung
Vor Qt 5.15 wurden alle Typen, einschließlich Singletons, über die qmlRegisterType -API registriert. Singletons wurden speziell entweder über ` qmlRegisterSingletonType ` oder ` qmlRegisterSingletonInstance` registriert. Abgesehen von der kleinen Unannehmlichkeit, den Modulnamen für jeden Typ wiederholen zu müssen, und der erzwungenen Entkopplung der Klassendeklaration von ihrer Registrierung bestand das Hauptproblem dieses Ansatzes darin, dass er tooling-unfreundlich war: Es war statisch nicht möglich, alle notwendigen Informationen über die Typen eines Moduls zur Kompilierungszeit zu extrahieren. Die deklarative Registrierung löste dieses Problem.
Hinweis: Es gibt noch einen Anwendungsfall für die imperative „ qmlRegisterType “-API: Sie bietet eine Möglichkeit, ein Singleton eines nicht-QObject -Typs als „ var “-Eigenschaft über the QJSValue based qmlRegisterSingletonType overload . Bevorzugen Sie die Alternative: Stellen Sie diesen Wert als Eigenschaft eines auf (QObject) basierenden Singletons bereit, damit Typinformationen verfügbar sind.
Zugriff auf Singletons
Auf Singletons kann sowohl von QML als auch von C++ aus zugegriffen werden. In QML müssen Sie das enthaltene Modul importieren. Anschließend können Sie über den Namen auf das Singleton zugreifen. Das Lesen und Schreiben seiner Eigenschaften in JavaScript-Kontexten erfolgt auf dieselbe Weise wie bei normalen Objekten:
import QtQuick
import MyModule
Item {
x: MySingleton.posX
Component.onCompleted: MySingleton.ready = true;
}Das Einrichten von Bindungen für die Eigenschaften eines Singletons ist nicht möglich; falls dies jedoch erforderlich ist, kann ein ` Binding `-Element verwendet werden, um das gleiche Ergebnis zu erzielen:
import QtQuick
import MyModule
Item {
id: root
Binding {
target: MySingleton
property: "posX"
value: root.x
}
}Hinweis: Bei der Einrichtung einer Bindung für eine Singleton-Eigenschaft istVorsicht geboten: Wenn dies von mehr als einer Datei aus erfolgt, sind die Ergebnisse nicht definiert.
Richtlinien für die (Nicht-)Verwendung von Singletons
Singletons ermöglichen es Ihnen, Daten, auf die an mehreren Stellen zugegriffen werden muss, der Engine zur Verfügung zu stellen. Dabei kann es sich um global gemeinsam genutzte Einstellungen handeln, wie beispielsweise den Abstand zwischen Elementen, oder um Datenmodelle, die an mehreren Stellen angezeigt werden müssen. Im Vergleich zu Kontexteigenschaften, die einen ähnlichen Anwendungsfall lösen können, haben sie den Vorteil, dass sie typisiert sind, von Tools wie dem QML Language Server, und sie sind in der Regel auch zur Laufzeit schneller.
Es wird empfohlen, nicht zu viele Singletons in einem Modul zu registrieren: Singletons bleiben nach ihrer Erstellung so lange bestehen, bis die Engine selbst zerstört wird, und bringen die Nachteile eines gemeinsam genutzten Zustands mit sich, da sie Teil des globalen Zustands sind. Ziehen Sie daher die folgenden Techniken in Betracht, um die Anzahl der Singletons in Ihrer Anwendung zu reduzieren:
Zusammenfassung verwandter Daten
Das Hinzufügen eines Singletons für jedes Objekt, das Sie verfügbar machen möchten, verursacht einen erheblichen Boilerplate-Aufwand. Meistens ist es sinnvoller, die Daten, die Sie verfügbar machen möchten, als Eigenschaften eines einzigen Singletons zusammenzufassen. Nehmen wir zum Beispiel an, Sie möchten einen E-Book-Reader erstellen, bei dem Sie drei „ abstract item models “ bereitstellen müssen – eine für lokale Bücher und zwei für entfernte Quellen. Anstatt den Vorgang zum Bereitstellen bestehender Objekte dreimal zu wiederholen, können Sie stattdessen ein Singleton erstellen und es vor dem Start der Hauptanwendung einrichten:
class GlobalState : QObject
{
Q_OBJECT
QML_ELEMENT
QML_SINGLETON
Q_PROPERTY(QAbstractItemModel* localBooks MEMBER localBooks)
Q_PROPERTY(QAbstractItemModel* digitalStoreFront MEMBER digitalStoreFront)
Q_PROPERTY(QAbstractItemModel* publicLibrary MEMBER publicLibrary)
public:
QAbstractItemModel* localBooks;
QAbstractItemModel* digitalStoreFront;
QAbstractItemModel* publicLibrary
};
int main() {
QQmlApplicationEngine engine;
auto globalState = engine.singletonInstance<GlobalState *>("MyModule", "GlobalState");
globalState->localBooks = getLocalBooks();
globalState->digitalStoreFront = setupLoalStoreFront();
globalState->publicLibrary = accessPublicLibrary();
engine.loadFromModule("MyModule", "Main");
}Objektinstanzen verwenden
Im letzten Abschnitt hatten wir das Beispiel, drei Modelle als Mitglieder eines Singletons bereitzustellen. Das kann nützlich sein, wenn die Modelle entweder an mehreren Stellen verwendet werden müssen oder wenn sie von einer externen API bereitgestellt werden, über die wir keine Kontrolle haben. Wenn wir die Modelle jedoch nur an einer einzigen Stelle benötigen, ist es möglicherweise sinnvoller, sie als instanziierbaren Typ zu definieren. Um auf das vorherige Beispiel zurückzukommen: Wir können eine instanziierbare Klasse „RemoteBookModel“ hinzufügen und diese dann in der QML-Datei des Buchbrowsers instanziieren:
// remotebookmodel.h
class RemoteBookModel : public QAbstractItemModel
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(QUrl url READ url WRITE setUrl NOTIFY urlChanged)
// ...
};// bookbrowser.qml
Row {
ListView {
model: RemoteBookModel { url: "www.public-lib.example"}
}
ListView {
model: RemoteBookModel { url: "www.store-front.example"}
}
}Übergabe des Anfangszustands
Zwar lassen sich Singletons zur Übergabe des Zustands an QML verwenden, doch sind sie verschwenderisch, wenn der Zustand nur für die Ersteinrichtung der Anwendung benötigt wird. In diesem Fall ist es oft möglich, „ QQmlApplicationEngine::setInitialProperties “ zu nutzen. Man möchte beispielsweise „ Window::visibility “ auf Vollbild umstellen, wenn ein entsprechendes Befehlszeilenflag gesetzt wurde:
QQmlApplicationEngine engine;
if (parser.isSet(fullScreenOption)) {
// assumes root item is ApplicationWindow
engine.setInitialProperties(
{ "visibility", QVariant::fromValue(QWindow::FullScreen)}
);
}
engine.loadFromModule("MyModule, "Main");
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.