Definieren von QML-Typen aus C++
Bei der Erweiterung von QML mit C++-Code kann eine C++-Klasse im QML-Typsystem registriert werden, damit die Klasse als Datentyp in QML-Code verwendet werden kann. Während die Eigenschaften, Methoden und Signale jeder von QObject abgeleiteten Klasse von QML aus zugänglich sind, wie in Attribute von C++-Typen für QML verfügbar machen beschrieben, kann eine solche Klasse erst dann als Datentyp von QML aus verwendet werden, wenn sie im Typsystem registriert ist. Darüber hinaus kann die Registrierung weitere Funktionen bereitstellen, wie z. B. die Möglichkeit, eine Klasse als instanzierbaren QML-Objekttyp von QML aus zu verwenden, oder die Möglichkeit, eine Singleton-Instanz der Klasse zu importieren und von QML aus zu verwenden.
Zusätzlich bietet das Qt Qml Modul Mechanismen für die Implementierung von QML-spezifischen Funktionen wie angehängte Eigenschaften und Standardeigenschaften in C++.
(Beachten Sie, dass eine Reihe der in diesem Dokument behandelten wichtigen Konzepte im Tutorial Writing QML Extensions with C++ demonstriert werden).
HINWEIS: Alle Header, die QML-Typen deklarieren, müssen ohne Präfix über den Include-Pfad des Projekts zugänglich sein.
Weitere Informationen über C++ und die verschiedenen QML-Integrationsmethoden finden Sie auf der Übersichtsseite über C++ und QML-Integration.
Registrierung von C++-Typen mit dem QML-Typesystem
Eine von QObject abgeleitete Klasse kann im QML-Typsystem registriert werden, damit der Typ als Datentyp in QML-Code verwendet werden kann.
Die Engine erlaubt die Registrierung von instanzierbaren und nicht instanzierbaren Typen. Die Registrierung eines instanzierbaren Typs ermöglicht die Verwendung einer C++-Klasse als Definition eines QML-Objekttyps, so dass dieser in Objektdeklarationen von QML-Code verwendet werden kann, um Objekte dieses Typs zu erstellen. Die Registrierung liefert der Engine auch zusätzliche Typ-Metadaten, so dass der Typ (und alle von der Klasse deklarierten Enums) als Datentyp für Eigenschaftswerte, Methodenparameter und Rückgabewerte sowie Signalparameter verwendet werden kann, die zwischen QML und C++ ausgetauscht werden.
Durch die Registrierung eines nicht-instantierbaren Typs wird die Klasse auf diese Weise ebenfalls als Datentyp registriert, aber der Typ kann nicht als QML-Objekttyp von QML aus instanziiert verwendet werden. Dies ist z. B. nützlich, wenn ein Typ über Enums verfügt, die für QML zugänglich sein sollen, der Typ selbst aber nicht instanzierbar sein soll.
Eine Kurzanleitung zur Auswahl des richtigen Ansatzes für die Bereitstellung von C++-Typen für QML finden Sie unter Auswahl der richtigen Integrationsmethode zwischen C++ und QML.
Vorbedingungen
Alle unten genannten Makros sind im Header von qqmlregistration.h
verfügbar. Sie müssen den folgenden Code zu den Dateien hinzufügen, die sie verwenden, um die Makros verfügbar zu machen:
#include <QtQml/qqmlregistration.h>
Außerdem müssen sich Ihre Klassendeklarationen in Headern befinden, die über den Include-Pfad Ihres Projekts erreichbar sind. Die Deklarationen werden verwendet, um zur Kompilierungszeit Registrierungscode zu erzeugen, und der Registrierungscode muss die Header enthalten, die die Deklarationen enthalten.
Registrierung eines instanzierbaren Objekttyps
Jede von QObject abgeleiteteC++-Klasse kann als Definition eines QML-Objekttyps registriert werden. Sobald eine Klasse im QML-Typsystem registriert ist, kann die Klasse wie jeder andere Objekttyp aus QML-Code deklariert und instanziiert werden. Wie in Exposing Attributes of C++ Types to QML erläutert, sind die Eigenschaften, Methoden und Signale jeder von QObject abgeleiteten Klasse von QML-Code aus zugänglich.
Um eine von QObject abgeleitete Klasse als instanzierbaren QML-Objekttyp zu registrieren, fügen Sie QML_ELEMENT
oder QML_NAMED_ELEMENT(<name>)
zur Klassendeklaration hinzu. Sie müssen auch Anpassungen im Build-System vornehmen. Für qmake fügen Sie CONFIG += qmltypes
, QML_IMPORT_NAME
und QML_IMPORT_MAJOR_VERSION
zu Ihrer Projektdatei hinzu. Für CMake sollte die Datei, die die Klasse enthält, Teil eines Ziels sein, das mit qt_add_qml_module() eingerichtet wurde. Dadurch wird die Klasse in den Typ-Namensraum unter der angegebenen Hauptversion registriert, wobei entweder der Klassenname oder ein explizit angegebener Name als QML-Typname verwendet wird. Die Minor-Version(en) werden von allen Revisionen abgeleitet, die an Eigenschaften, Methoden oder Signale angehängt sind. Die Standard-Nebenversion ist 0
. Sie können den Typ explizit so einschränken, dass er nur in bestimmten Unterversionen verfügbar ist, indem Sie das Makro QML_ADDED_IN_VERSION()
zur Klassendeklaration hinzufügen. Clients können geeignete Versionen des Namespaces importieren, um den Typ zu verwenden.
Nehmen wir zum Beispiel an, es gibt eine Klasse Message
mit den Eigenschaften author
und creationDate
:
class Message : public QObject { Q_OBJECT Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged) Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged) QML_ELEMENT public: // ... };
Dieser Typ kann registriert werden, indem ein geeigneter Namensraum und eine entsprechende Versionsnummer in die Projektdatei aufgenommen werden. Zum Beispiel, um den Typ im Namespace com.mycompany.messaging
mit der Version 1.0 verfügbar zu machen:
qt_add_qml_module(messaging URI com.mycompany.messaging VERSION 1.0 SOURCES message.cpp message.h )
CONFIG += qmltypes QML_IMPORT_NAME = com.mycompany.messaging QML_IMPORT_MAJOR_VERSION = 1
Wenn der Header, in dem die Klasse deklariert ist, nicht über den Include-Pfad Ihres Projekts zugänglich ist, müssen Sie möglicherweise den Include-Pfad ändern, damit der generierte Registrierungscode kompiliert werden kann.
INCLUDEPATH += com/mycompany/messaging
Der Typ kann in einer Objektdeklaration von QML verwendet werden, und seine Eigenschaften können gelesen und beschrieben werden, wie im folgenden Beispiel dargestellt:
import com.mycompany.messaging Message { author: "Amelie" creationDate: new Date() }
Registrierung von Wertetypen
Jeder Typ mit einem Q_GADGET Makro kann als QML-Wertetyp registriert werden. Sobald ein solcher Typ im QML-Typsystem registriert ist, kann er als Eigenschaftstyp in QML-Code verwendet werden. Eine solche Instanz kann von QML aus manipuliert werden; wie in Exposing Attributes of C++ Types to QML erläutert, sind die Eigenschaften und Methoden eines beliebigen Wertetyps von QML-Code aus zugänglich.
Im Gegensatz zu Objekttypen erfordern Wertetypen klein geschriebene Namen. Der bevorzugte Weg, sie zu registrieren, ist die Verwendung der Makros QML_VALUE_TYPE oder QML_ANONYMOUS. Es gibt keine Entsprechung zu QML_ELEMENT, da Ihre C++-Klassen in der Regel Namen in Großbuchstaben haben werden. Ansonsten ist die Registrierung sehr ähnlich wie die Registrierung von Objekttypen.
Nehmen wir zum Beispiel an, Sie möchten einen Wertetyp person
registrieren, der aus zwei Zeichenketten für Vor- und Nachname besteht:
class Person { Q_GADGET Q_PROPERTY(QString firstName READ firstName WRITE setFirstName) Q_PROPERTY(QString lastName READ lastName WRITE setLastName) QML_VALUE_TYPE(person) public: // ... };
Es gibt noch einige weitere Einschränkungen, was Sie mit Werttypen tun können:
- Werttypen können keine Singletons sein.
- Wertetypen müssen standardmäßig konstruierbar und kopierfähig sein.
- Die Verwendung von QProperty als Mitglied eines Wertetyps ist problematisch. Werttypen werden kopiert, und Sie müssen entscheiden, was mit den Bindungen auf QProperty zu diesem Zeitpunkt geschehen soll. Sie sollten QProperty nicht in Werttypen verwenden.
- Wertetypen können keine angehängten Eigenschaften bereitstellen.
- Die API zur Definition von Erweiterungen für Werttypen (QML_EXTENDED) ist nicht öffentlich und unterliegt zukünftigen Änderungen.
Wertetypen mit Aufzählungen
Um Aufzählungen eines Wertetyps in QML zu verwenden, sind einige zusätzliche Schritte erforderlich.
Wertetypen haben in QML Namen in Kleinbuchstaben und Typen mit Kleinbuchstaben sind im Allgemeinen in JavaScript-Code nicht adressierbar (es sei denn, Sie geben pragma ValueTypeBehavior: Addressable an). Wenn Sie einen Wertetyp in C++ mit einer Aufzählung haben, die Sie QML zur Verfügung stellen möchten, müssen Sie die Aufzählung separat zur Verfügung stellen.
Dies kann durch die Verwendung von QML_FOREIGN_NAMESPACE gelöst werden. Leiten Sie zunächst von Ihrem Wertetyp ab, um einen separaten C++-Typ zu erstellen:
class Person { Q_GADGET Q_PROPERTY(QString firstName READ firstName WRITE setFirstName) Q_PROPERTY(QString lastName READ lastName WRITE setLastName) QML_VALUE_TYPE(person) public: enum TheEnum { A, B, C }; Q_ENUM(TheEnum) //... }; class PersonDerived: public Person { Q_GADGET };
Dann stellen Sie den abgeleiteten Typ als fremden Namespace dar:
namespace PersonDerivedForeign
{
Q_NAMESPACE
QML_NAMED_ELEMENT(Person)
QML_FOREIGN_NAMESPACE(PersonDerived)
}
Dies erzeugt einen QML-Namensraum namens Person
(Großbuchstaben) mit einer Aufzählung namens TheEnum
und den Werten A
, B
und C
. Dann können Sie das Folgende in QML schreiben:
someProperty: Person.A
Gleichzeitig können Sie Ihren Wertetyp mit dem Namen person
(Kleinbuchstaben) genau wie zuvor verwenden.
Registrierung nicht-instantierbarer Typen
Manchmal muss eine von QObject abgeleitete Klasse zwar im QML-Typsystem registriert werden, aber nicht als instanzierbarer Typ. Dies ist zum Beispiel der Fall, wenn eine C++-Klasse:
- ein Schnittstellentyp ist, der nicht instanzierbar sein sollte
- ein Basisklassentyp ist, der nicht in QML offengelegt werden muss
- ein Enum deklariert, auf das von QML aus zugegriffen werden kann, das aber ansonsten nicht instanzierbar sein sollte
- ist ein Typ, der QML über eine Singleton-Instanz zur Verfügung gestellt werden sollte und nicht von QML aus instanzierbar sein sollte
Das Qt Qml Modul bietet mehrere Makros zur Registrierung von nicht instanzierbaren Typen:
- QML_ANONYMOUS registriert einen C++-Typ, der nicht instanzierbar ist und auf den von QML aus nicht verwiesen werden kann. Dies ermöglicht es der Engine, geerbte Typen, die von QML aus instanzierbar sind, zu erzwingen.
- QML_INTERFACE registriert einen bestehenden Qt-Schnittstellentyp. Der Typ ist in QML nicht instanzierbar und Sie können keine QML-Eigenschaften mit ihm deklarieren. Die Verwendung von C++-Eigenschaften dieses Typs aus QML führt jedoch die erwarteten Schnittstellen-Casts aus.
- QML_UNCREATABLE(reason) kombiniert mit mit QML_ELEMENT oder QML_NAMED_ELEMENT registriert einen benannten C++-Typ, der nicht instanzierbar ist, aber für das QML-Typsystem als Typ identifizierbar sein sollte. Dies ist nützlich, wenn die Enums oder angehängten Eigenschaften eines Typs von QML aus zugänglich sein sollen, der Typ selbst aber nicht instanzierbar sein soll. Der Parameter sollte eine Fehlermeldung sein, die ausgegeben wird, wenn ein Versuch, eine Instanz des Typs zu erzeugen, erkannt wird.
- QML_SINGLETON kombiniert mit QML_ELEMENT oder QML_NAMED_ELEMENT registriert einen Singleton-Typ, der aus QML importiert werden kann, wie unten beschrieben.
Beachten Sie, dass alle C++-Typen, die im QML-Typsystem registriert sind, QObject-abgeleitet sein müssen, auch wenn sie nicht instanzierbar sind.
Registrierung von Singleton-Objekten mit einem Singleton-Typ
Ein Singleton-Typ ermöglicht die Bereitstellung von Eigenschaften, Signalen und Methoden in einem Namespace, ohne dass der Client manuell eine Objektinstanz instanziieren muss. QObject Singleton-Typen sind insbesondere eine effiziente und bequeme Möglichkeit, Funktionalität oder globale Eigenschaftswerte bereitzustellen.
Beachten Sie, dass Singleton-Typen nicht mit QQmlContext verknüpft sind, da sie von allen Kontexten in einer Engine gemeinsam genutzt werden. QObject Singleton-Typ-Instanzen werden von der QQmlEngine erstellt und gehören ihr, und werden zerstört, wenn die Engine zerstört wird.
Mit einem QObject Singleton-Typ kann auf ähnliche Weise interagiert werden wie mit jedem anderen QObject oder instanziierten Typ, mit der Ausnahme, dass nur eine (von der Engine konstruierte und besessene) Instanz existiert, und dass auf sie über den Typnamen und nicht über die id verwiesen werden muss. Q_PROPERTYs von QObject Singleton-Typen können gebunden werden, und Q_INVOKABLE Funktionen von QObject Modul-APIs können in Signalhandler-Ausdrücken verwendet werden. Dies macht Singleton-Typen zu einem idealen Weg, um Styling oder Theming zu implementieren, und sie können auch anstelle von ".pragma library"-Skriptimporten verwendet werden, um globale Zustände zu speichern oder um globale Funktionalität bereitzustellen.
Einmal registriert, kann ein QObject Singleton-Typ importiert und wie jede andere QObject Instanz in QML verwendet werden. Das folgende Beispiel geht davon aus, dass ein QObject Singleton-Typ im Namespace "MyThemeModule" mit der Version 1.0 registriert wurde, wobei QObject eine QColor "color" Q_PROPERTY hat:
import MyThemeModule 1.0 as Theme Rectangle { color: Theme.color // binding. }
Ein QJSValue kann auch als Singleton-Typ dargestellt werden, jedoch sollten sich die Kunden darüber im Klaren sein, dass die Eigenschaften eines solchen Singleton-Typs nicht gebunden werden können.
Siehe QML_SINGLETON für weitere Informationen darüber, wie man einen neuen Singleton-Typ implementiert und registriert, und wie man einen bestehenden Singleton-Typ verwendet. Siehe Singletons in QML für weitere Informationen über Singletons.
Hinweis: Enum-Werte für registrierte Typen in QML sollten mit einem Großbuchstaben beginnen.
Endgültige Eigenschaften
Eigenschaften, die mit dem Modifikator FINAL
auf Q_PROPERTY als endgültig deklariert werden, können nicht überschrieben werden. Das bedeutet, dass alle gleichnamigen Eigenschaften oder Funktionen, die entweder in QML oder in C++ auf abgeleiteten Typen deklariert sind, von der QML-Engine ignoriert werden. Wenn möglich, sollten Sie Eigenschaften FINAL
deklarieren, um versehentliche Überschreibungen zu vermeiden. Eine Überschreibung einer Eigenschaft ist nicht nur in abgeleiteten Klassen sichtbar, sondern auch für QML-Code, der im Kontext der Basisklasse ausgeführt wird. Solcher QML-Code erwartet jedoch normalerweise die ursprüngliche Eigenschaft. Dies ist eine häufige Quelle von Fehlern.
Eigenschaften, die unter FINAL
deklariert sind, können auch nicht durch Funktionen in QML oder durch Q_INVOKABLE Methoden in C++ überschrieben werden.
Typrevisionen und -versionen
Viele der Funktionen zur Typregistrierung erfordern die Angabe von Versionen für den registrierten Typ. Typrevisionen und -versionen ermöglichen es, dass neue Eigenschaften oder Methoden in der neuen Version existieren, während sie mit früheren Versionen kompatibel bleiben.
Betrachten Sie diese beiden QML-Dateien:
// main.qml import QtQuick 1.0 Item { id: root MyType {} }
// MyType.qml import MyTypes 1.0 CppType { value: root.x }
wobei CppType
der C++-Klasse CppType
entspricht.
Wenn der Autor von CppType in einer neuen Version seiner Typdefinition eine Eigenschaft root
zu CppType hinzufügt, wird root.x
nun zu einem anderen Wert aufgelöst, da root
auch die id
der Komponente der obersten Ebene ist. Der Autor könnte festlegen, dass die neue Eigenschaft root
ab einer bestimmten Unterversion verfügbar ist. Auf diese Weise können neue Eigenschaften und Funktionen zu bestehenden Typen hinzugefügt werden, ohne dass bestehende Programme dadurch beeinträchtigt werden.
Das REVISION-Tag wird verwendet, um die Eigenschaft root
als in Revision 1 des Typs hinzugefügt zu kennzeichnen. Methoden wie Q_INVOKABLE, Signale und Slots können ebenfalls mit dem Makro Q_REVISION(x)
für eine Revision gekennzeichnet werden:
class CppType : public BaseType { Q_OBJECT Q_PROPERTY(int root READ root WRITE setRoot NOTIFY rootChanged REVISION 1) QML_ELEMENT signals: Q_REVISION(1) void rootChanged(); };
Die auf diese Weise angegebenen Revisionen werden automatisch als Unterversionen der in der Projektdatei angegebenen Hauptversion interpretiert. In diesem Fall ist root
nur verfügbar, wenn MyTypes
Version 1.1 oder höher importiert wird. Importe von MyTypes
Version 1.0 bleiben davon unberührt.
Aus demselben Grund sollten neue Typen, die in späteren Versionen eingeführt werden, mit dem Makro QML_ADDED_IN_VERSION gekennzeichnet werden.
Diese Eigenschaft der Sprache ermöglicht es, Verhaltensänderungen vorzunehmen, ohne bestehende Anwendungen zu zerstören. Daher sollten die Autoren von QML-Modulen immer daran denken, zu dokumentieren, was sich zwischen den Unterversionen geändert hat, und die Benutzer von QML-Modulen sollten überprüfen, ob ihre Anwendung noch korrekt läuft, bevor sie eine aktualisierte Importanweisung einsetzen.
Überarbeitungen einer Basisklasse, von der Ihr Typ abhängt, werden automatisch registriert, wenn der Typ selbst registriert wird. Dies ist nützlich, wenn von Basisklassen abgeleitet wird, die von anderen Autoren bereitgestellt werden, z. B. bei der Erweiterung von Klassen aus dem Modul Qt Quick.
Hinweis: Die QML-Engine unterstützt keine Revisionen für Eigenschaften oder Signale von gruppierten und angehängten Eigenschaftsobjekten.
Registrierung von Erweiterungsobjekten
Bei der Integration bestehender Klassen und Technologien in QML müssen die APIs oft angepasst werden, damit sie besser in die deklarative Umgebung passen. Obwohl die besten Ergebnisse in der Regel durch eine direkte Änderung der Originalklassen erzielt werden, erlauben Erweiterungsobjekte begrenzte Erweiterungsmöglichkeiten ohne direkte Änderungen, wenn dies entweder nicht möglich ist oder durch andere Bedenken erschwert wird.
Erweiterungsobjekte fügen zusätzliche Eigenschaften zu einem bestehenden Typ hinzu. Eine erweiterte Typdefinition ermöglicht es dem Programmierer, bei der Registrierung der Klasse einen zusätzlichen Typ, den so genannten Erweiterungstyp, anzugeben. Seine Mitglieder werden transparent mit der ursprünglichen Zielklasse verschmolzen, wenn sie in QML verwendet werden. Ein Beispiel:
QLineEdit { leftMargin: 20 }
Die Eigenschaft leftMargin
ist eine neue Eigenschaft, die einem bestehenden C++-Typ, QLineEdit, hinzugefügt wurde, ohne dass der Quellcode geändert wurde.
Das Makro QML_EXTENDED(extension) dient zur Registrierung von erweiterten Typen. Das Argument ist der Name einer anderen Klasse, die als Erweiterung verwendet werden soll.
Sie können auch QML_EXTENDED_NAMESPACE(namespace) verwenden, um einen Namespace und insbesondere die darin deklarierten Aufzählungen als Erweiterung für einen Typ zu registrieren. Wenn der Typ, den Sie erweitern, selbst ein Namespace ist, müssen Sie stattdessen QML_NAMESPACE_EXTENDED(namespace) verwenden.
Eine Erweiterungsklasse ist eine reguläre QObject, mit einem Konstruktor, der einen QObject Zeiger annimmt. Die Erstellung der Erweiterungsklasse wird jedoch verzögert, bis auf die erste erweiterte Eigenschaft zugegriffen wird. Die Erweiterungsklasse wird erstellt, und das Zielobjekt wird als Elternteil übergeben. Wenn auf die Eigenschaft des Originals zugegriffen wird, wird stattdessen die entsprechende Eigenschaft des Erweiterungsobjekts verwendet.
Registrierung fremder Typen
Es kann C++-Typen geben, die nicht so verändert werden können, dass sie die oben genannten Makros enthalten. Dabei kann es sich um Typen aus Bibliotheken von Drittanbietern handeln, oder um Typen, die einen Vertrag erfüllen müssen, der dem Vorhandensein dieser Makros widerspricht. Sie können diese Typen aber trotzdem mit dem QML_FOREIGN Makro in QML einbinden. Dazu erstellen Sie eine separate Struktur, die ausschließlich aus den Registrierungsmakros besteht, etwa so:
// Contains class Immutable3rdParty #include <3rdpartyheader.h> struct Foreign { Q_GADGET QML_FOREIGN(Immutable3rdParty) QML_NAMED_ELEMENT(Accessible3rdParty) QML_ADDED_IN_VERSION(2, 4) // QML_EXTENDED, QML_SINGLETON ... };
Mit diesem Code erhalten Sie einen QML-Typ mit den Methoden und Eigenschaften von Immutable3rdParty und den in Foreign angegebenen QML-Traits (z. B.: singleton, extended).
Definieren von QML-spezifischen Typen und Attributen
Bereitstellen von angehängten Eigenschaften
In der QML-Sprachsyntax gibt es den Begriff der angehängten Eigenschaften und der angehängten Signalhandler, bei denen es sich um zusätzliche Attribute handelt, die an ein Objekt angehängt werden. Im Wesentlichen werden solche Attribute durch einen anhängenden Typ implementiert und bereitgestellt, und diese Attribute können an ein Objekt eines anderen Typs angehängt werden. Dies steht im Gegensatz zu gewöhnlichen Objekteigenschaften, die vom Objekttyp selbst (oder dem geerbten Typ des Objekts) bereitgestellt werden.
Das folgende Beispiel Item verwendet angehängte Eigenschaften und angehängte Handler:
import QtQuick 2.0 Item { width: 100; height: 100 focus: true Keys.enabled: false Keys.onReturnPressed: console.log("Return key was pressed") }
Hier ist das Objekt Item in der Lage, auf die Werte von Keys.enabled
und Keys.onReturnPressed
zuzugreifen und diese zu setzen. Dies ermöglicht dem Objekt Item den Zugriff auf diese zusätzlichen Attribute als Erweiterung seiner eigenen bestehenden Attribute.
Schritte zur Implementierung von angehängten Objekten
Bei der Betrachtung des obigen Beispiels sind mehrere Parteien beteiligt:
- Es gibt eine Instanz eines anonymen angehängten Objekttyps mit einem
enabled
und einemreturnPressed
Signal, das an das Item Objekt angehängt wurde, damit es auf diese Attribute zugreifen und sie setzen kann. - Das Item Objekt ist der Attachee, an den die Instanz des angehängten Objekttyps angehängt wurde.
- Keys ist der anhängende Typ, der dem angehängten Objekt einen benannten Qualifier, "Keys", zur Verfügung stellt, über den er auf die Attribute des angehängten Objekttyps zugreifen kann.
Wenn die QML-Engine diesen Code verarbeitet, erstellt sie eine einzelne Instanz des angehängten Objekttyps und fügt diese Instanz an das Objekt Item an, wodurch sie Zugriff auf die Attribute enabled
und returnPressed
der Instanz erhält.
Die Mechanismen zur Bereitstellung von angehängten Objekten können in C++ implementiert werden, indem Klassen für den Typ des angehängten Objekts und den Typ des Anhängens bereitgestellt werden. Für den Typ des angehängten Objekts ist eine von QObject abgeleitete Klasse bereitzustellen, die die Attribute definiert, die den angehängten Objekten zugänglich gemacht werden sollen. Für den Typ des angehängten Objekts muss eine von QObject abgeleitete Klasse bereitgestellt werden, die:
- eine statische qmlAttachedProperties() mit der folgenden Signatur implementiert:
static <AttachedPropertiesType> *qmlAttachedProperties(QObject *object);
Diese Methode sollte eine Instanz des angehängten Objekttyps zurückgeben.
Die QML-Engine ruft diese Methode auf, um eine Instanz des angehängten Objekttyps an den durch den
object
-Parameter angegebenen Attachee anzuhängen. Es ist üblich, wenn auch nicht unbedingt erforderlich, dass diese Methodenimplementierung die zurückgegebene Instanz anobject
übergibt, um Speicherlecks zu vermeiden.Diese Methode wird von der Engine höchstens einmal für jede Attachee-Objektinstanz aufgerufen, da die Engine den zurückgegebenen Instanzzeiger für nachfolgende Zugriffe auf angehängte Eigenschaften zwischenspeichert. Folglich kann das Attachment-Objekt nicht gelöscht werden, bis der Attachee
object
zerstört ist. - wird als anhängender Typ deklariert, indem das Makro QML_ATTACHED(attached) zur Klassendeklaration hinzugefügt wird. Das Argument ist der Name des angehängten Objekttyps
Implementierung von angehängten Objekten: Ein Beispiel
Nehmen wir zum Beispiel den Typ Message
, der in einem früheren Beispiel beschrieben wurde:
class Message : public QObject { Q_OBJECT Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged) Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged) QML_ELEMENT public: // ... };
Angenommen, es ist notwendig, ein Signal auf Message
auszulösen, wenn es auf einem Message Board veröffentlicht wird, und auch zu verfolgen, wann die Nachricht auf dem Message Board abgelaufen ist. Da es nicht sinnvoll ist, diese Attribute direkt einem Message
hinzuzufügen, da die Attribute eher für den Message-Board-Kontext relevant sind, könnten sie als angehängte Attribute an ein Message
-Objekt implementiert werden, die über einen "MessageBoard"-Qualifier bereitgestellt werden. In Bezug auf die zuvor beschriebenen Konzepte sind die beteiligten Parteien hier:
- Eine Instanz eines anonymen angehängten Objekttyps, der ein
published
Signal und eine abgelaufene Eigenschaft bereitstellt. Dieser Typ wird im Folgenden durchMessageBoardAttachedType
implementiert - Ein
Message
-Objekt, das der Attachee sein wird - Der
MessageBoard
Typ, der der anhängende Typ sein wird, der vonMessage
Objekten verwendet wird, um auf die angehängten Attribute zuzugreifen
Es folgt eine Beispielimplementierung. Zunächst muss ein angehängter Objekttyp mit den erforderlichen Eigenschaften und Signalen vorhanden sein, auf die der Attachee zugreifen kann:
class MessageBoardAttachedType : public QObject { Q_OBJECT Q_PROPERTY(bool expired READ expired WRITE setExpired NOTIFY expiredChanged) QML_ANONYMOUS public: MessageBoardAttachedType(QObject *parent); bool expired() const; void setExpired(bool expired); signals: void published(); void expiredChanged(); };
Dann muss der anhängende Typ, MessageBoard
, eine Methode qmlAttachedProperties()
deklarieren, die eine Instanz des angehängten Objekttyps zurückgibt, wie er von MessageBoardAttachedType implementiert wird. Zusätzlich muss MessageBoard
über das Makro QML_ATTACHED() als anhängender Typ deklariert werden:
class MessageBoard : public QObject { Q_OBJECT QML_ATTACHED(MessageBoardAttachedType) QML_ELEMENT public: static MessageBoardAttachedType *qmlAttachedProperties(QObject *object) { return new MessageBoardAttachedType(object); } };
Nun kann ein Message
Typ auf die Eigenschaften und Signale des angehängten Objekttyps zugreifen:
Message { author: "Amelie" creationDate: new Date() MessageBoard.expired: creationDate < new Date("January 01, 2015 10:45:00") MessageBoard.onPublished: console.log("Message by", author, "has been published!") }
Außerdem kann die C++-Implementierung auf die angehängte Objektinstanz zugreifen, die an ein beliebiges Objekt angehängt wurde, indem sie die Funktion qmlAttachedPropertiesObject() aufruft.
Zum Beispiel:
Message *msg = someMessageInstance(); MessageBoardAttachedType *attached = qobject_cast<MessageBoardAttachedType*>(qmlAttachedPropertiesObject<MessageBoard>(msg)); qDebug() << "Value of MessageBoard.expired:" << attached->expired();
Weitergabe von angehängten Eigenschaften
QQuickAttachedPropertyPropagator kann als Unterklasse definiert werden, um angehängte Eigenschaften von einem übergeordneten Objekt an seine Kinder weiterzugeben, ähnlich wie bei font und palette. Sie unterstützt die Weitergabe durch items, popups und windows.
Eigenschaftsmodifikator-Typen
Ein Eigenschaftsmodifikatortyp ist eine besondere Art von QML-Objekttyp. Eine Property Modifier Type Instanz beeinflusst eine Eigenschaft (einer QML-Objektinstanz), auf die sie angewendet wird. Es gibt zwei verschiedene Arten von Property Modifier Typen:
- Abfangjäger zum Schreiben von Eigenschaftswerten
- Quellen für Eigenschaftswerte
Ein Property Value Write Interceptor kann verwendet werden, um Werte zu filtern oder zu modifizieren, während sie in Properties geschrieben werden. Derzeit wird nur der Typ Behavior unterstützt, der durch den QtQuick
-Import bereitgestellt wird.
Eine Eigenschaftswertquelle kann verwendet werden, um den Wert einer Eigenschaft im Laufe der Zeit automatisch zu aktualisieren. Clients können ihre eigenen Eigenschaftswertquellentypen definieren. Die verschiedenen Typen von Eigenschaftsanimationen, die durch den QtQuick
import bereitgestellt werden, sind Beispiele für Eigenschaftswertquellen.
Instanzen von Eigenschaftsmodifikatoren können erstellt und auf eine Eigenschaft eines QML-Objekts über die Syntax "<Modifikatortyp> auf <Eigenschaftsname>" angewendet werden, wie das folgende Beispiel zeigt:
import QtQuick 2.0 Item { width: 400 height: 50 Rectangle { width: 50 height: 50 color: "red" NumberAnimation on x { from: 0 to: 350 loops: Animation.Infinite duration: 2000 } } }
Dies wird gemeinhin als "on"-Syntax bezeichnet.
Clients können ihre eigenen Eigenschaftswert-Quellentypen registrieren, aber derzeit keine Eigenschaftswert-Schreibinterceptoren.
Eigenschaftswert-Quellen
Eigenschaftswertquellen sind QML-Typen, die den Wert einer Eigenschaft im Laufe der Zeit automatisch aktualisieren können, indem sie die <PropertyValueSource> on <property>
-Syntax verwenden. Die verschiedenen Typen von Eigenschaftsanimationen, die vom Modul QtQuick
bereitgestellt werden, sind Beispiele für Eigenschaftswertquellen.
Eine Eigenschaftswertquelle kann in C++ implementiert werden, indem QQmlPropertyValueSource untergeordnet wird und eine Implementierung bereitgestellt wird, die im Laufe der Zeit verschiedene Werte in eine Eigenschaft schreibt. Wenn die Eigenschaftswertquelle mit der <PropertyValueSource> on <property>
Syntax in QML auf eine Eigenschaft angewendet wird, erhält sie von der Engine einen Verweis auf diese Eigenschaft, so dass der Eigenschaftswert aktualisiert werden kann.
Angenommen, es gibt eine Klasse RandomNumberGenerator
, die als Eigenschaftswertquelle zur Verfügung gestellt werden soll, so dass sie bei Anwendung auf eine QML-Eigenschaft den Eigenschaftswert alle 500 Millisekunden auf eine andere Zufallszahl aktualisiert. Zusätzlich kann diesem Zufallszahlengenerator ein maxValue übergeben werden. Diese Klasse kann wie folgt implementiert werden:
class RandomNumberGenerator : public QObject, public QQmlPropertyValueSource { Q_OBJECT Q_INTERFACES(QQmlPropertyValueSource) Q_PROPERTY(int maxValue READ maxValue WRITE setMaxValue NOTIFY maxValueChanged); QML_ELEMENT public: RandomNumberGenerator(QObject *parent) : QObject(parent), m_maxValue(100) { QObject::connect(&m_timer, SIGNAL(timeout()), SLOT(updateProperty())); m_timer.start(500); } int maxValue() const; void setMaxValue(int maxValue); virtual void setTarget(const QQmlProperty &prop) { m_targetProperty = prop; } signals: void maxValueChanged(); private slots: void updateProperty() { m_targetProperty.write(QRandomGenerator::global()->bounded(m_maxValue)); } private: QQmlProperty m_targetProperty; QTimer m_timer; int m_maxValue; };
Wenn die QML-Engine auf eine Verwendung von RandomNumberGenerator
als Eigenschaftswertquelle stößt, ruft sie RandomNumberGenerator::setTarget()
auf, um den Typ mit der Eigenschaft zu versehen, auf die die Wertquelle angewendet wurde. Wenn der interne Timer in RandomNumberGenerator
alle 500 Millisekunden ausgelöst wird, schreibt er einen neuen Zahlenwert in die angegebene Eigenschaft.
Sobald die Klasse RandomNumberGenerator
im QML-Typsystem registriert wurde, kann sie von QML aus als Quelle für Eigenschaftswerte verwendet werden. Im Folgenden wird sie verwendet, um die Breite von Rectangle alle 500 Millisekunden zu ändern:
import QtQuick 2.0 Item { width: 300; height: 300 Rectangle { RandomNumberGenerator on width { maxValue: 300 } height: 100 color: "red" } }
In jeder anderen Hinsicht sind Eigenschaftswertquellen normale QML-Typen, die Eigenschaften, Signalmethoden usw. haben können, aber mit der zusätzlichen Fähigkeit, dass sie zum Ändern von Eigenschaftswerten unter Verwendung der <PropertyValueSource> on <property>
-Syntax verwendet werden können.
Wenn ein Objekt der Eigenschaftswertquelle einer Eigenschaft zugewiesen wird, versucht QML zunächst, es normal zuzuweisen, als wäre es ein normaler QML-Typ. Nur wenn diese Zuweisung fehlschlägt, ruft die Engine die Methode setTarget() auf. Dadurch kann der Typ auch in anderen Kontexten als nur als Wertquelle verwendet werden.
Festlegen von Standard- und Elterneigenschaften für QML-Objekttypen
Jeder QObject-abgeleitete Typ, der als instanzierbarer QML-Objekttyp registriert ist, kann optional eine Standardeigenschaft für den Typ angeben. Eine Standardeigenschaft ist die Eigenschaft, der die Kinder eines Objekts automatisch zugewiesen werden, wenn sie keiner bestimmten Eigenschaft zugewiesen sind.
Die Standardeigenschaft kann durch den Aufruf des Makros Q_CLASSINFO() für eine Klasse mit einem bestimmten "DefaultProperty"-Wert festgelegt werden. Zum Beispiel legt die Klasse MessageBoard
unten ihre Eigenschaft messages
als Standardeigenschaft für die Klasse fest:
class MessageBoard : public QObject { Q_OBJECT Q_PROPERTY(QQmlListProperty<Message> messages READ messages) Q_CLASSINFO("DefaultProperty", "messages") QML_ELEMENT public: QQmlListProperty<Message> messages(); private: QList<Message *> m_messages; };
Dies ermöglicht es, dass Kinder eines MessageBoard
-Objekts automatisch seiner messages
-Eigenschaft zugewiesen werden, wenn sie nicht einer bestimmten Eigenschaft zugewiesen sind. Ein Beispiel:
MessageBoard { Message { author: "Naomi" } Message { author: "Clancy" } }
Wenn messages
nicht als Standardeigenschaft festgelegt wäre, müssten alle Message
-Objekte stattdessen explizit der messages
-Eigenschaft zugewiesen werden, wie folgt:
MessageBoard { messages: [ Message { author: "Naomi" }, Message { author: "Clancy" } ] }
(Im Übrigen ist die Eigenschaft Item::data die Standardeigenschaft. Alle Item -Objekte, die dieser data
-Eigenschaft hinzugefügt werden, werden auch der Liste Item::children hinzugefügt. Die Verwendung der Standardeigenschaft ermöglicht es also, visuelle Kinder für ein Element zu deklarieren, ohne sie explizit der children -Eigenschaft zuzuordnen).
Zusätzlich können Sie eine "ParentProperty" Q_CLASSINFO() deklarieren, um der QML-Engine mitzuteilen, welche Eigenschaft das übergeordnete Objekt in der QML-Hierarchie bezeichnen soll. Der Message-Typ könnte zum Beispiel wie folgt deklariert werden:
class Message : public QObject { Q_OBJECT Q_PROPERTY(QObject* board READ board BINDABLE boardBindable) Q_PROPERTY(QString author READ author BINDABLE authorBindable) Q_CLASSINFO("ParentProperty", "board") QML_ELEMENT public: Message(QObject *parent = nullptr) : QObject(parent) { m_board = parent; } QObject *board() const { return m_board.value(); } QBindable<QObject *> boardBindable() { return QBindable<QObject *>(&m_board); } QString author() const { return m_author.value(); } QBindable<QString> authorBindable() { return QBindable<QString>(&m_author); } private: QProperty<QObject *> m_board; QProperty<QString> m_author; };
Die Definition der Parent-Property ermöglicht qmllint und anderen Tools einen besseren Einblick in die Intention Ihres Codes und vermeidet falsch positive Warnungen bei manchen Property-Zugriffen.
Definieren von visuellen Elementen mit dem Modul Qt Quick
Bei der Erstellung von Benutzeroberflächen mit dem Qt Quick müssen alle QML-Objekte, die visuell gerendert werden sollen, vom Typ Item abgeleitet werden, da dies der Basistyp für alle visuellen Objekte in Qt Quick. Dieser Typ Item wird von der C++-Klasse QQuickItem implementiert, die vom Qt Quick Modul bereitgestellt wird. Daher sollte diese Klasse unterklassifiziert werden, wenn ein visueller Typ in C++ implementiert werden muss, der in eine QML-basierte Benutzeroberfläche integriert werden kann.
Weitere Informationen finden Sie in der Dokumentation QQuickItem. Darüber hinaus zeigt das Tutorial Writing QML Extensions with C++, wie ein QQuickItem-basiertes visuelles Element in C++ implementiert und in eine Qt Quick-basierte Benutzeroberfläche integriert werden kann.
Empfangen von Benachrichtigungen bei der Objektinitialisierung
Bei einigen benutzerdefinierten QML-Objekttypen kann es von Vorteil sein, die Initialisierung bestimmter Daten zu verzögern, bis das Objekt erstellt wurde und alle seine Eigenschaften festgelegt wurden. Dies kann zum Beispiel der Fall sein, wenn die Initialisierung aufwendig ist oder wenn die Initialisierung erst erfolgen soll, wenn alle Eigenschaftswerte initialisiert wurden.
Das Qt Qml Modul stellt die QQmlParserStatus zur Verfügung, die für diese Zwecke subklassifiziert werden kann. Es definiert eine Reihe virtueller Methoden, die in verschiedenen Phasen der Komponenteninitialisierung aufgerufen werden. Um diese Benachrichtigungen zu erhalten, sollte eine C++-Klasse QQmlParserStatus erben und auch das Qt-Metasystem mit dem Makro Q_INTERFACES() benachrichtigen.
Zum Beispiel:
class MyQmlType : public QObject, public QQmlParserStatus { Q_OBJECT Q_INTERFACES(QQmlParserStatus) QML_ELEMENT public: virtual void componentComplete() { // Perform some initialization here now that the object is fully created } };
© 2025 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.