Wie integriert man C++ und QML? Registrierung einer C++-Klasse als Singleton in QML

Qt QML-Entwicklung
2022-06-09
8 Minuten
Registrieren einer C++-Klasse als Singleton in QML

In unserem vorherigen Beitrag über die Registrierung einer C++-Klasse als Singleton in QML haben wir erklärt, warum die Integration von QML und C++ wichtig ist, und die Grundlagen dieses Mechanismus erläutert. In diesem Artikel gehen wir detaillierter auf ein spezifisches Praxisbeispiel ein, das die Integration von Elementen behandelt, die nur einmal instanziiert werden können (sogenannte Singletons).

Wenn Sie auf diesen Beitrag gestoßen sind, weil Sie Unterstützung in der Softwareentwicklung suchen, empfehlen wir Ihnen, mehr über unsere C++-Entwicklungsdienstleistungen zu erfahren.

 

Was ist ein Singleton?

Bevor wir beginnen, lassen Sie uns kurz erklären, was ein Singleton ist, für diejenigen, die diesen Begriff zum ersten Mal hören. Ein Singleton ist ein Software-Entwurfsmuster, das die Erstellung eines Objekts einer Klasse auf eine einzige Instanz beschränkt. Es gibt viele Anwendungsfälle für Singletons, z. B. ein Objekt, das den Zustand eines Programms speichert und aus verschiedenen Teilen der Anwendung aufgerufen werden kann.

Es ist üblich, dieses Muster auch in QML zu nutzen. Ein Controller, der dynamische (nicht nur) Eigenschaften wie Signale und Slots enthält und Utility-Funktionen bereitstellt, ist ein hervorragender Kandidat für ein Singleton-Objekt. Allerdings sollten Sie bei der Verwendung dieses Musters in Anwendungen, die Multithreading anwenden, vorsichtig sein.

Das Qt-Framework bietet mehrere Möglichkeiten, C++-Klassen in QML zu registrieren, und jede davon hat ihren Anwendungsfall. Auch QML-JavaScript kann dafür verwendet werden. Heute besprechen wir die aktuellste Methode mit qt_add_qml_module(). Diese wurde in Qt 6.2 eingeführt und wird häufig zur Registrierung von QML-Typen empfohlen.

Zuerst müssen wir eine neue C++-Klasse erstellen, die wir in QML verfügbar machen möchten.

 

#include <QObject>
#include <QtQml/qqml.h>

class ImportantClass : public QObject
{
    Q_OBJECT
    QML_SINGLETON
    QML_ELEMENT
    Q_PROPERTY(int importantNumber READ importantNumber WRITE setImportantNumber NOTIFY importantNumberChanged)

public:
    explicit ImportantClass(QObject *parent = nullptr);

    void setImportantNumber(int num) {
        if (num != m_importantNumber) {
            m_importantNumber = num;
            emit importantNumberChanged();
        }
    }

    int importantNumber() const {
        return m_importantNumber;
    }

public slots:
    double convertFahrenheit(int value) const {
        return value * 1.8 + 32;
    }

signals:
    void importantNumberChanged();

private:
    int m_importantNumber = 10;

};

Alles sollte Ihnen vertraut vorkommen, wenn Sie unseren vorherigen Beitrag über das Bereitstellen von Objekten und die Registrierung einer C++-Klasse in QML gelesen haben. Der einzige Unterschied ist das Makro Q_SINGLETON, das wichtig ist, um einen Singleton-Typ korrekt zu registrieren.

In der Klasse ImportantClass definieren wir eine importantNumber-Eigenschaft und eine Funktion convertFahrenheit(value), die, wie der Name vermuten lässt, den übergebenen Wert in die Fahrenheit-Temperaturskala umrechnet.

Als Nächstes müssen wir zu unserer Datei CMakeLists.txt wechseln und den Befehl qt_add_qml_module() verwenden, um unsere Klasse zu registrieren.

 

qt_add_qml_module(appSingletonExample
    URI SingletonExample
    VERSION 1.0
    QML_FILES
        main.qml
    SOURCES
        ImportantClass.h
        ImportantClass.cpp
)

Geben Sie eine URI an, die Sie in der QML-Importanweisung verwenden möchten. Legen Sie anschließend die C++-Dateien fest, in denen sich Ihre Klasse befindet. Herzlichen Glückwunsch, Sie haben erfolgreich ein Singleton in QML registriert!

Erstellen wir nun ein Beispiel, um zu überprüfen, ob alles korrekt funktioniert. Hier ist eine einfache QML-Anwendung, die aus zwei Buttons besteht.

 

import QtQuick
import QtQuick.Controls

import SingletonExample

Window {
  width: 640
  height: 480
  visible: true
  title: qsTr("Hello World")

  Button {
    id: importantNumberButton

    anchors.centerIn: parent

    width: 100
    height: 50

    text: ImportantClass.importantNumber

    onClicked: {
      ImportantClass.importantNumber += 1
    }
  }

  Button {
    anchors {
      top: importantNumberButton.bottom
      horizontalCenter: importantNumberButton.horizontalCenter
    }

    width: 100
    height: 50

    text: "Click to convert"

    onClicked: {
      text = ImportantClass.convertFahrenheit(ImportantClass.importantNumber)
    }
  }
}

Einer der Buttons erhöht den oben erwähnten Wert von importantNumber, während der andere die Funktion convertFahrenheit() auf diesen Wert anwendet. Qt Creator kann manchmal fälschlicherweise über falsche Importe klagen, aber das ist nicht korrekt. Stellen Sie sicher, dass Sie Qt Creator auf die neueste Version aktualisieren oder das QML-Typmodell zurücksetzen. Dadurch ist es wahrscheinlicher, dass Ihre eigenen QML-Module im Code-Editor korrekt erkannt werden.

Zusätzlich bietet die IDE keine Autovervollständigung für registrierte Typen. Wir hoffen, dass dieses Problem bald behoben wird, da es Sie nicht davor bewahrt, subtile Rechtschreibfehler zu machen, die zwar die Ausführung des QML-Codes nicht stoppen, aber zu undefined-Werten führen können.

Als Nächstes müssen wir den registrierten Typ mit import URI importieren (in unserem Fall import SingletonExample). Jetzt können wir ImportantClass in dieser QML-Datei verwenden: Ein Klick auf den ersten Button erhöht den Wert von ImportantNumber um 1:

 

onClicked: {
  ImportantClass.importantNumber += 1
}

Und ein Klick auf den zweiten Button setzt seinen Text auf den Fahrenheit-Wert von importantNumber.

 

onClicked: {
  text = ImportantClass.convertFahrenheit(ImportantClass.importantNumber)
}

Es ist nicht erforderlich, Objekte der ImportantClass zu initialisieren, da dies intern erledigt wird. Versuche, dies zu tun, führen zu einem Fehler. Erinnern Sie sich an das Q_SINGLETON-Makro? Das ist das „magische Wort“, das Ihren QML-Code um ein in QML sichtbares Singleton-Objekt erweitert.

Das war’s eigentlich schon. Sie haben gelernt, wie man Singletons in QML aus C++ mithilfe des CMake-Befehls qt_add_qml_module() registriert. Wenn Sie mehr über die Integration von QML und C++ erfahren möchten, empfehlen wir Ihnen die offizielle Qt-Dokumentation sowie unsere anderen Blogbeiträge in der Serie „How to integrate QML and C++”.

 

Wann sollte man eine Singleton-Klasse verwenden?

Die Entscheidung, ob das Singleton-Pattern geeignet ist, kann schwierig sein. Es mag zunächst eine großartige Idee erscheinen, aber es kann später zu unerwarteten Problemen führen.

 

Die Gang of Four empfiehlt die Nutzung von Singleton in folgenden Fälle n:

Es sollte genau eine Instanz einer Klasse geben, die von einem zentralen oder clientseitigen Zugangspunkt aus zugänglich ist.

Es sollte möglich sein, die einzelne Instanz durch Sub-Klassen zu erweitern, ohne den Client-Code zu ändern.

Diese Punkte bieten jedoch möglicherweise nicht genügend praktische Orientierung, um zu beurteilen, ob ein Singleton wirklich für Ihr Projekt geeignet ist. Sie müssen Ihre spezifischen Umstände berücksichtigen.

 

Vorteile der Verwendung von Singleton-C++-Klassen

 

Globaler Zugriffspunkt

Ein Singleton kann als globales Objekt fungieren und den Zugriff von überall innerhalb des Programms ermöglichen. Da es global ist, kann es jedoch nicht von außen verändert werden. Dadurch wird es als Mittel eingesetzt, um globale Variablen vor äußeren Eingriffen zu schützen.

 

Einzigartiges Entitätsmodell

Wenn wir reale Entitäten in unseren Programmen modellieren, wird das Verständnis dafür einfacher. Beispiele sind Registrierungsstellen, globale Timer und Fabriken für eindeutige IDs – all dies sind Singletons. Diese Entsprechung zwischen Programmierabstraktionen und der Realität erleichtert sowohl Kunden als auch Programmierern das Verständnis des Programms.

 

Nachteile

  • Globale Variablen können versehentlich an einer Stelle geändert und an anderer Stelle unbeabsichtigt verwendet werden, was zu unerwarteten Ergebnissen führt.

  • Singletons erzeugen ein einziges Objekt, sodass mehrere Referenzen auf dieselbe Instanz erstellt werden können. Dies kann zu einer engen Kopplung von Klassen führen, die sich auf globale Variablen stützen, da Änderungen an den Daten einer Klasse Auswirkungen auf eine andere haben können.

  • Unit-Tests werden durch Singletons erschwert – wenn ein Objekt und seine zugehörigen Methoden zu eng miteinander verknüpft sind, kann es schwierig sein, Tests durchzuführen, ohne eine dedizierte Klasse für das Singleton zu haben.

  • Singletons können zu versteckten Abhängigkeiten führen – da sie in den meisten Codebasen leicht verfügbar sind, ist ihre Referenz möglicherweise nicht offensichtlich, wenn sie durch verschiedene Methoden weitergereicht wird.

Um solche Probleme zu vermeiden, ist es wichtig sicherzustellen, dass die von Ihnen erstellte Klasse tatsächlich ein exaktes Singleton ist. Berücksichtigen Sie beim Entwerfen dieses Patterns mit Blick auf Tests, das Singleton möglichst als Parameter mithilfe von Dependency Injection weiterzugeben. So lassen sich Probleme frühzeitig erkennen und beheben.

 

Anwendungen von Singleton-C++-Klassen in QML

Singleton-Klassen haben viele potenzielle Anwendungsbereiche, darunter Cache-Speicher, Datenbankverbindungen, Treiber und Logging. Hier sind einige wichtige Anwendungen:

 

 Zugriff auf Hardware-Schnittstellen

Singleton-Klassen werden häufig verwendet, um gleichzeitigen Zugriff auf eine bestimmte Klasse zu verhindern. Dies ist besonders nützlich bei Hardware-Ressourcenbeschränkungen, z. B. bei Druckern. Ein Singleton-Druckspooler kann Deadlocks verhindern, die durch gleichzeitigen Zugriff entstehen.

 

 Logger

Eine Logger-Instanz, die mit einer Singleton-Klasse erstellt wurde, ist vorteilhaft, um Log-Dateien zu verwalten. Bei mehreren Clients, die auf dieselbe Logging-Utility-Klasse zugreifen, könnten ohne Singleton Probleme durch gleichzeitigen Zugriff entstehen.

 

Konfigurationsdateien

Singleton-Klassen eignen sich hervorragend für Konfigurationsdateien, da sie wiederholte Lesezugriffe von mehreren Benutzern verhindern und die Daten einmalig abrufen, um sie effizient wiederzuverwenden.

 

Cache

Caches profitieren ebenfalls von Singleton-Objekten, da sie einen globalen Zugriffspunkt für ihre hostenden Anwendungen bieten. Dies erleichtert den Zugriff auf gespeicherte Informationen und erhöht die Effizienz.

 

Sind Singletons speichereffizient?

Wenn eine C++-Klasse als Singleton in QML registriert wird, erstellt sie eine statische Instanz. Dies ist der am häufigsten verwendete Ansatz. Er ist jedoch möglicherweise nicht speichereffizient. Wenn die Klasse nicht aufgerufen wird, bleibt das Singleton bestehen, und wenn es „schwer“ ist, kann es unnötig viel Platz im RAM einnehmen.

 

Zusammenfassung

Das Paar Qt QML und C++ ist die perfekte Kombination für diejenigen, die eine flexible, innovative und zuverlässige Methode zur Entwicklung plattformübergreifender Anwendungen suchen. Mit Qt können Sie Ihre App problemlos mit einer einzigen Codebasis auf mehreren Plattformen bereitstellen, was eine effiziente Leistung und fehlerfreies Coding gewährleistet.

Unser Team aus Experten für die Qt-QML-Entwicklung kann Sie in jeder Phase unterstützen – von der Anforderungsanalyse über die eigentliche Entwicklung, das Debugging und die Migration bis hin zur Bereitstellung. Mit uns an Ihrer Seite brauchen Sie sich keine Sorgen um den Erfolg Ihrer Anwendung zu machen. Probieren Sie Scythe Studio aus und entdecken Sie unsere Qt-Beratungsdienste – Sie werden es nicht bereuen!

 

Scythe-Studio - Blog Redactors

Scythe Studio Blog Redactors

Brauchen Sie Qt QML-Entwicklungsdienste?

service partner

Kommen wir zur Sache: Es ist eine Herausforderung, Top-Qt-QML-Entwickler zu finden. Helfen Sie sich selbst und starten Sie die Zusammenarbeit mit Scythe Studio – echten Experten im Qt C++ Framework.

Entdecken Sie unsere Fähigkeiten!

Neueste Artikel

[ 156 ]