Definition von QML-Typen aus C++
Bei der Erweiterung von QML um C++-Code kann eine C++-Klasse im QML-Typsystem registriert werden, damit die Klasse als Datentyp innerhalb von QML-Code verwendet werden kann. Zwar sind die Eigenschaften, Methoden und Signale jeder von ` QObject` abgeleiteten Klasse von QML aus zugänglich, wie unter „Bereitstellung von Attributen von C++-Typen für QML“ erläutert, doch kann eine solche Klasse erst dann als Datentyp in QML verwendet werden, wenn sie im Typsystem registriert wurde. Darüber hinaus kann die Registrierung weitere Funktionen bieten, beispielsweise die Möglichkeit, eine Klasse von QML aus als instanziierbaren QML-Objekttyp zu verwenden, oder die Ermöglichung, eine Singleton-Instanz der Klasse aus QML zu importieren und zu nutzen.
Darüber hinaus bietet das Qt Qml Modul Mechanismen zur Implementierung von QML-spezifischen Funktionen wie „attached properties “ und „default properties“ in C++ bereit.
(Beachten Sie, dass eine Reihe der in diesem Dokument behandelten wichtigen Konzepte im Tutorial „QML-Erweiterungen mit C++ schreiben “ veranschaulicht werden.)
HINWEIS: Alle Header-Dateien, die QML-Typen deklarieren, müssen ohne Präfix über den Include-Pfad des Projekts zugänglich sein.
Weitere Informationen zu C++ und den verschiedenen Methoden zur QML-Integration finden Sie auf der Übersichtsseite zur C++- und QML-Integration.
Registrierung von C++-Typen im QML-Typsystem
Eine von ` QObject` abgeleitete Klasse kann beim QML-Typsystem registriert werden, damit der Typ als Datentyp innerhalb von QML-Code verwendet werden kann.
Die Engine ermöglicht die Registrierung sowohl instanziierbarer als auch nicht instanziierbarer Typen. Durch die Registrierung eines instanziierbaren Typs kann eine C++-Klasse als Definition eines QML-Objekttyps verwendet werden, sodass sie in Objektdeklarationen aus QML-Code genutzt werden kann, um Objekte dieses Typs zu erstellen. Durch die Registrierung erhält die Engine zudem zusätzliche Typ-Metadaten, wodurch der Typ (sowie alle von der Klasse deklarierten Enums) als Datentyp für Eigenschaftswerte, Methodenparameter und Rückgabewerte sowie für Signalparameter verwendet werden kann, die zwischen QML und C++ ausgetauscht werden.
Durch die Registrierung eines nicht instanziierbaren Typs wird die Klasse auf diese Weise ebenfalls als Datentyp registriert, jedoch kann der Typ nicht aus QML heraus als QML-Objekttyp instanziiert werden. Dies ist beispielsweise nützlich, wenn ein Typ Aufzählungen enthält, die für QML verfügbar sein sollen, der Typ selbst jedoch nicht instanziierbar sein darf.
Eine Kurzanleitung zur Wahl des richtigen Ansatzes, um C++-Typen für QML verfügbar zu machen, finden Sie unter „Auswahl der richtigen Integrationsmethode zwischen C++ und QML“.
Voraussetzungen
Alle unten genannten Makros sind in der Header-Datei „ qqmlintegration.h “ aus dem Modul „QtQmlIntegration“ verfügbar.
Sie müssen den folgenden Code in die Dateien einfügen, in denen diese Makros verwendet werden, um sie verfügbar zu machen:
#include <QtQmlIntegration/qqmlintegration.h>Wenn Sie bereits eine Verknüpfung zum Modul „ QtQml “ hergestellt haben, können Sie stattdessen die Header-Datei „ qqmlregistration.h “ verwenden, die „ qqmlintegration.h “ wie folgt einbindet:
#include <QtQml/qqmlregistration.h>Außerdem müssen sich Ihre Klassendeklarationen in Header-Dateien befinden, auf die über den Include-Pfad Ihres Projekts zugegriffen werden kann. Die Deklarationen werden verwendet, um zur Kompilierungszeit Registrierungscode zu generieren, und der Registrierungscode muss die Header-Dateien einbinden, die die Deklarationen enthalten.
Registrierung eines instanziierbaren Objekttyps
Jede von ` QObject` abgeleitete C++-Klasse kann als Definition eines QML-Objekttyps registriert werden. Sobald eine Klasse im QML-Typsystem registriert ist, kann sie wie jeder andere Objekttyp aus QML-Code deklariert und instanziiert werden. Nach ihrer Erstellung kann eine Klasseninstanz von QML aus manipuliert werden; wie in „Exponieren von Attributen von C++-Typen für QML“ erläutert, sind die Eigenschaften, Methoden und Signale jeder von ` QObject` abgeleiteten Klasse aus QML-Code zugänglich.
Um eine von ` QObject` abgeleitete Klasse als instanziierbaren QML-Objekttyp zu registrieren, fügen Sie ` QML_ELEMENT ` oder ` QML_NAMED_ELEMENT(<name>) ` zur Klassendeklaration hinzu. Außerdem müssen Sie 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. Bei CMake sollte die Datei, die die Klasse enthält, Teil eines mit ` qt_add_qml_module()` eingerichteten Ziels sein. Dadurch wird die Klasse im Typ-Namespace unter der angegebenen Hauptversion registriert, wobei entweder der Klassenname oder ein explizit angegebener Name als QML-Typname verwendet wird. Die Nebenversionen werden aus den Revisionen abgeleitet, die mit Eigenschaften, Methoden oder Signalen verknüpft sind. Die Standard-Nebenversion lautet „ 0 “. Sie können den Typ explizit so einschränken, dass er nur ab bestimmten Nebenversionen verfügbar ist, indem Sie das Makro „ QML_ADDED_IN_VERSION() “ zur Klassendeklaration hinzufügen. Clients können geeignete Versionen des Namensraums importieren, um den Typ zu verwenden.
Angenommen, es gibt beispielsweise 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 der Projektdatei ein entsprechender Typ-Namespace und eine Versionsnummer hinzugefügt werden. Um den Typ beispielsweise 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 = 1Falls die Header-Datei, in der die Klasse deklariert ist, über den Include-Pfad Ihres Projekts nicht zugänglich ist, müssen Sie den Include-Pfad möglicherweise anpassen, damit der generierte Registrierungscode kompiliert werden kann.
INCLUDEPATH += com/mycompany/messagingDer Typ kann in einer Objektdeklaration in QML verwendet werden, und seine Eigenschaften können gemäß dem folgenden Beispiel gelesen und geschrieben werden:
import com.mycompany.messaging
Message {
author: "Amelie"
creationDate: new Date()
}Registrierung von Werttypen
Jeder Typ mit einem „ Q_GADGET “-Makro kann als QML-Werttyp registriert werden. Sobald ein solcher Typ im QML-Typsystem registriert ist, kann er im QML-Code als Eigenschaftstyp verwendet werden. Eine solche Instanz lässt sich von QML aus bearbeiten; wie in „Exponieren von Attributen von C++-Typen für QML“ erläutert, sind die Eigenschaften und Methoden jedes Werttyps vom QML-Code aus zugänglich.
Im Gegensatz zu Objekttypen erfordern Wertetypen Namen in Kleinbuchstaben. Die bevorzugte Methode zur Registrierung ist die Verwendung der Makros „ QML_VALUE_TYPE “ oder „ QML_ANONYMOUS “. Es gibt kein Äquivalent zu „ QML_ELEMENT “, da Ihre C++-Klassen in der Regel Namen in Großbuchstaben haben. Ansonsten ähnelt die Registrierung stark der Registrierung von Objekttypen.
Angenommen, Sie möchten einen Werttyp „ person “ registrieren, der aus zwei Zeichenketten für Vor- und Nachnamen 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 einige weitere Einschränkungen hinsichtlich der Möglichkeiten, die Ihnen bei Werttypen zur Verfügung stehen:
- Werttypen dürfen keine Singletons sein.
- Werttypen müssen standardmäßig konstruierbar und kopierbar sein.
- Die Verwendung von „ QProperty “ als Mitglied eines Werttyps ist problematisch. Werttypen werden kopiert, und Sie müssten an dieser Stelle entscheiden, wie mit etwaigen Bindungen an „ QProperty “ verfahren werden soll. Sie sollten „ QProperty “ nicht in Werttypen verwenden.
- Werttypen können keine angehängten Eigenschaften bereitstellen.
- Die API zum Definieren von Erweiterungen für Werttypen (QML_EXTENDED) ist nicht öffentlich und kann sich in Zukunft ändern.
Werttypen mit Aufzählungen
Um Aufzählungen aus einem Werttyp für QML verfügbar zu machen, sind einige zusätzliche Schritte erforderlich.
Werttypen haben in QML Namen in Kleinbuchstaben, und Typen mit Namen in Kleinbuchstaben sind im JavaScript-Code in der Regel nicht adressierbar (es sei denn, Sie geben `pragma ValueTypeBehavior: Addressable` an). Wenn Sie in C++ einen Werttyp mit einer Aufzählung haben, die Sie für QML verfügbar machen möchten, müssen Sie die Aufzählung separat verfügbar machen.
Dies lässt sich mithilfe von „ QML_FOREIGN_NAMESPACE “ lösen. Leiten Sie zunächst von Ihrem Werttyp 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
};Stellen Sie anschließend den abgeleiteten Typ als fremden Namensraum bereit:
namespace PersonDerivedForeign
{
Q_NAMESPACE
QML_NAMED_ELEMENT(Person)
QML_FOREIGN_NAMESPACE(PersonDerived)
}Dadurch wird ein QML-Namespace namens „ Person “ (in Großbuchstaben) mit einer Enumeration namens „ TheEnum “ und den Werten „ A “, „ B “ und „ C “ erstellt. Anschließend können Sie in QML Folgendes schreiben:
someProperty: Person.AGleichzeitig können Sie Ihren Werttyp namens „ person “ (Kleinbuchstaben) weiterhin genau wie bisher verwenden.
Registrierung nicht instanziierbarer Typen
Manchmal muss eine von „ QObject “ abgeleitete Klasse möglicherweise im QML-Typsystem registriert werden, jedoch nicht als instanziierbarer Typ. Dies ist beispielsweise der Fall, wenn eine C++-Klasse:
- ein Schnittstellentyp ist, der nicht instanziierbar sein soll
- ein Basisklassentyp ist, der nicht für QML freigegeben werden muss
- eine Enumeration deklariert, auf die von QML aus zugegriffen werden kann, die aber ansonsten nicht instanziierbar sein soll
- ein Typ ist, der QML über eine Singleton-Instanz bereitgestellt werden soll und von QML aus nicht instanziierbar sein soll
Das Qt Qml Modul stellt mehrere Makros zur Registrierung nicht instanziierbarer Typen bereit:
- QML_ANONYMOUS Registriert einen C++-Typ, der nicht instanziierbar ist und auf den von QML aus nicht verwiesen werden kann. Dadurch kann die Engine alle geerbten Typen, die instanziierbar sind, von QML aus umwandeln.
- QML_INTERFACE registriert einen vorhandenen Qt-Schnittstellentyp. Der Typ ist von QML aus nicht instanziierbar, und Sie können damit keine QML-Eigenschaften deklarieren. Die Verwendung von C++-Eigenschaften dieses Typs aus QML führt jedoch zu den erwarteten Schnittstellenumwandlungen.
- QML_UNCREATABLE(Begründung) In Kombination mit ` QML_ELEMENT ` oder ` QML_NAMED_ELEMENT ` wird ein benannter C++-Typ registriert, der nicht instanziierbar 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 jedoch nicht instanziierbar sein darf. Der Parameter sollte eine Fehlermeldung sein, die ausgegeben wird, wenn ein Versuch erkannt wird, eine Instanz des Typs zu erstellen.
- QML_SINGLETON In Kombination mit ` QML_ELEMENT ` oder ` QML_NAMED_ELEMENT ` wird ein Singleton-Typ registriert, der aus QML importiert werden kann, wie weiter unten erläutert.
Beachten Sie, dass alle im QML-Typsystem registrierten C++-Typen von ` QObject` abgeleitet sein müssen, auch wenn sie nicht instanziierbar sind.
Registrierung von Singleton-Objekten mit einem Singleton-Typ
Ein Singleton-Typ ermöglicht es, Eigenschaften, Signale und Methoden in einem Namensraum verfügbar zu machen, ohne dass der Client eine Objektinstanz manuell instanziieren muss. Insbesondere Singleton-Typen vom Typ „ QObject “ sind eine effiziente und bequeme Möglichkeit, Funktionen oder globale Eigenschaftswerte bereitzustellen.
Beachten Sie, dass Singleton-Typen keinen zugehörigen „ QQmlContext “ haben, da sie von allen Kontexten einer Engine gemeinsam genutzt werden. Instanzen von Singleton-Typen vom Typ „ QObject “ werden von der „ QQmlEngine “ erstellt und verwaltet und werden zerstört, wenn die Engine zerstört wird.
Mit einem Singleton-Typ vom Typ „ QObject “ kann auf ähnliche Weise wie mit jedem anderen „ QObject “- oder instanziierten Typ interagiert werden, mit der Ausnahme, dass nur eine (von der Engine erstellte und verwaltete) Instanz existiert und diese über den Typnamen statt über die ID referenziert werden muss. Q_PROPERTYs von Singleton-Typen „ QObject “ können gebunden werden, und „ Q_INVOKABLE “-Funktionen der „ QObject “-Modul-APIs können in Signal-Handler-Ausdrücken verwendet werden. Dies macht Singleton-Typen zu einer idealen Möglichkeit, Styling oder Theming zu implementieren, und sie können auch anstelle von „.pragma library“-Skriptimporten verwendet werden, um globale Zustände zu speichern oder globale Funktionalität bereitzustellen.
Nach der Registrierung kann ein Singleton-Typ vom Typ „ QObject “ wie jede andere für QML zugängliche „ QObject “-Instanz importiert und verwendet werden. Das folgende Beispiel geht davon aus, dass ein Singleton-Typ vom Typ „ QObject “ mit der Version 1.0 im Namespace „MyThemeModule“ registriert wurde, wobei dieser QObject eine „color“- Q_PROPERTY des Moduls „ QColor “ aufweist:
import MyThemeModule 1.0 as Theme
Rectangle {
color: Theme.color // binding.
}Ein „ QJSValue “ kann ebenfalls als Singleton-Typ exponiert werden; Clients sollten sich jedoch bewusst sein, dass keine Bindungen an Eigenschaften eines solchen Singleton-Typs hergestellt werden können.
Weitere Informationen zur Implementierung und Registrierung eines neuen Singleton-Typs sowie zur Verwendung eines bestehenden Singleton-Typs finden Sie unter QML_SINGLETON. Ausführlichere Informationen zu Singletons finden Sie unter „Singletons in QML “.
Hinweis: Enum- Werte für in QML registrierte Typen sollten mit einem Großbuchstaben beginnen.
Final-Eigenschaften
Eigenschaften, die mit dem Modifikator „ FINAL “ für „ Q_PROPERTY “ als „final“ deklariert wurden, können nicht überschrieben werden. Das bedeutet, dass alle gleichnamigen Eigenschaften oder Funktionen, die entweder in QML oder in C++ für abgeleitete Typen deklariert wurden, von der QML-Engine ignoriert werden. Sie sollten Eigenschaften nach Möglichkeit als „ 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 in der Regel die ursprüngliche Eigenschaft. Dies ist eine häufige Fehlerquelle.
FINAL -deklarierte Eigenschaften 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, neue Eigenschaften oder Methoden in der neuen Version zu integrieren und gleichzeitig die Kompatibilität mit früheren Versionen zu gewährleisten.
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
}Dabei entspricht „ CppType “ der C++-Klasse „ CppType “.
Wenn der Autor von `CppType` in einer neuen Version seiner Typdefinition eine Eigenschaft ` root ` zu `CppType` hinzufügt, wird ` root.x ` nun auf einen anderen Wert aufgelöst, da ` root ` auch die ` id ` der obersten Komponente ist. Der Autor könnte festlegen, dass die neue Eigenschaft ` root ` ab einer bestimmten Nebenversion verfügbar ist. Dies ermöglicht es, bestehenden Typen neue Eigenschaften und Funktionen hinzuzufügen, ohne dass bestehende Programme beeinträchtigt werden.
Das REVISION-Tag wird verwendet, um die Eigenschaft „ root “ als in Revision 1 des Typs hinzugefügt zu kennzeichnen. Auch Methoden wie „ Q_INVOKABLE “, Signale und Slots können mithilfe des Makros „ Q_REVISION “ mit einer Revision versehen werden:
class CppType : public BaseType
{
Q_OBJECT
Q_PROPERTY(int root READ root WRITE setRoot NOTIFY rootChanged REVISION(1, 0))
QML_ELEMENT
signals:
Q_REVISION(1, 0) void rootChanged();
};Die auf diese Weise angegebenen Revisionen werden automatisch als Nebenversionen 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 wurden, mit dem Makro „ QML_ADDED_IN_VERSION “ gekennzeichnet werden.
Diese Eigenschaft der Sprache ermöglicht es, Verhaltensänderungen vorzunehmen, ohne dass bestehende Anwendungen beeinträchtigt werden. Daher sollten Autoren von QML-Modulen stets daran denken, zu dokumentieren, was sich zwischen den Nebenversionen geändert hat, und Nutzer von QML-Modulen sollten vor der Verwendung einer aktualisierten Importanweisung überprüfen, ob ihre Anwendung weiterhin korrekt läuft.
Änderungen an einer Basisklasse, von der Ihr Typ abhängt, werden automatisch registriert, wenn der Typ selbst registriert wird. Dies ist nützlich, wenn Sie von Basisklassen anderer Autoren ableiten, 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 APIs oft angepasst werden, damit sie besser in die deklarative Umgebung passen. Obwohl sich die besten Ergebnisse in der Regel durch direkte Änderung der Originalklassen erzielen lassen, bieten Erweiterungsobjekte – falls dies nicht möglich ist oder durch andere Faktoren erschwert wird – begrenzte Erweiterungsmöglichkeiten für Typen, die Sie kontrollieren, ohne dass direkte Änderungen erforderlich sind. Die Erweiterung von Qt-eigenen Typen wird nicht unterstützt.
Erweiterungsobjekte fügen einem bestehenden Typ zusätzliche Eigenschaften hinzu. Eine erweiterte Typdefinition ermöglicht es dem Programmierer, bei der Registrierung der Klasse einen zusätzlichen Typ – den sogenannten Erweiterungstyp – anzugeben. Dessen Mitglieder werden bei der Verwendung innerhalb von QML transparent mit der ursprünglichen Zielklasse zusammengeführt. Zum Beispiel:
QLineEdit {
leftMargin: 20
}Die Eigenschaft „ leftMargin “ ist eine neue Eigenschaft, die einem bestehenden C++-Typ, „ QLineEdit “, hinzugefügt wurde, ohne dessen Quellcode zu ändern.
Das Makro „ QML_EXTENDED “ (Erweiterung) dient zur Registrierung erweiterter 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 eines Typs zu registrieren. Wenn der zu erweiternde Typ 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 Zeiger auf ` QObject ` entgegennimmt. Die Erstellung der Erweiterungsklasse wird jedoch so lange verzögert, bis auf die erste erweiterte Eigenschaft zugegriffen wird. Die Erweiterungsklasse wird erstellt und das Zielobjekt wird als übergeordnetes Objekt ü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 geä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 bestimmte Anforderungen erfüllen müssen, die mit dem Vorhandensein dieser Makros unvereinbar sind. Sie können diese Typen jedoch mithilfe des Makros „ QML_FOREIGN “ dennoch für QML verfügbar machen. Erstellen Sie dazu eine separate Struktur, die ausschließlich aus den Registrierungsmakros besteht, wie folgt:
// 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 ...
};Aus diesem Code erhalten Sie einen QML-Typ mit den Methoden und Eigenschaften von `Immutable3rdParty` sowie den in `Foreign` angegebenen QML-Traits (z. B. `singleton`, `extended`).
Definition von QML-spezifischen Typen und Attributen
Bereitstellung von angehängten Eigenschaften
In der Syntax der Sprache QML gibt es den Begriff der angehängten Eigenschaften und angehängten Signal-Handler, bei denen es sich um zusätzliche Attribute handelt, die an ein Objekt angehängt werden. Im Wesentlichen werden solche Attribute von einem anfügenden 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.
Beispielsweise verwendet der unten stehende „ Item “ 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 kann das „ Item “-Objekt auf die Werte von „ Keys.enabled “ und „ Keys.onReturnPressed “ zugreifen und diese festlegen. Dadurch kann das „ Item “-Objekt auf diese zusätzlichen Attribute als Erweiterung seiner eigenen bestehenden Attribute zugreifen.
Schritte zur Implementierung von angehängten Objekten
Betrachtet man das obige Beispiel, so sind mehrere Parteien beteiligt:
- Es gibt eine Instanz eines anonymen, angehängten Objekttyps mit einer Eigenschaft „
enabled“ und einem Signal „returnPressed“, die an das Objekt „ Item “ angehängt wurde, damit dieses auf diese Attribute zugreifen und sie setzen kann. - Das Objekt ` Item ` ist das Zielobjekt, an das die Instanz des angehängten Objekttyps angehängt wurde.
- Keys ist der Anbindungstyp, der dem „attachee“ einen benannten Qualifizierer namens „Keys“ bereitstellt, über den dieser 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 hängt diese Instanz an das „ Item “-Objekt an, wodurch sie diesem Zugriff auf die Attribute „ enabled “ und „ returnPressed “ der Instanz gewährt.
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ängenden Objekts bereitgestellt werden. Für den Typ des angehängten Objekts muss eine von ` QObject` abgeleitete Klasse bereitgestellt werden, die die Attribute definiert, die für die anhängenden Objekte zugänglich gemacht werden sollen. Für den Typ des anhängenden Objekts muss eine von ` QObject` abgeleitete Klasse bereitgestellt werden, die:
- eine statische Funktion `qmlAttachedProperties()` mit der folgenden Signatur implementiert:
static <AttachedPropertiesType> *qmlAttachedProperties(QObject *object);Diese Methode sollte eine Instanz des Typs des angehängten Objekts zurückgeben.
Die QML-Engine ruft diese Methode auf, um eine Instanz des angeschlossenen Objekttyps an das durch den Parameter „
object“ angegebene Zielobjekt anzuhängen. Es ist üblich, wenn auch nicht zwingend erforderlich, dass die Implementierung dieser Methode die zurückgegebene Instanz unter „object“ anordnet, um Speicherlecks zu vermeiden.Diese Methode wird von der Engine für jede Instanz des „attachee“-Objekts höchstens einmal aufgerufen, da die Engine den Zeiger auf die zurückgegebene Instanz für nachfolgende Zugriffe auf die angehängte Eigenschaft zwischenspeichert. Folglich darf das angehängte Objekt erst gelöscht werden, wenn das „
object“ des „attachee“ zerstört wurde. - wird als „attaching type“ deklariert, indem das Makro „ QML_ATTACHED(attached)“ zur Klassendeklaration hinzugefügt wird. Das Argument ist der Name des „attached object“-Typs
Implementierung von angehängten Objekten: Ein Beispiel
Nehmen wir zum Beispiel den in einem früheren Beispiel beschriebenen Typ ` Message `:
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 erforderlich, bei der Veröffentlichung eines „ Message “ in einem Message Board ein Signal auszulösen und zudem zu verfolgen, wann die Nachricht im Message Board abgelaufen ist. Da es keinen Sinn ergibt, diese Attribute direkt einem „ Message “ hinzuzufügen, da die Attribute eher für den Kontext des Message Boards relevant sind, könnten sie als angehängte Attribute eines „ Message “-Objekts implementiert werden, die über einen „MessageBoard“-Qualifizierer bereitgestellt werden. Im Sinne der zuvor beschriebenen Konzepte sind die hier beteiligten Parteien:
- Eine Instanz eines anonymen, angehängten Objekttyps, der ein Signal „
published“ und eine Eigenschaft „expired“ bereitstellt. Dieser Typ wird im Folgenden durch „MessageBoardAttachedType“ implementiert - Ein „
Message“-Objekt, das das angehängte Objekt darstellt - Der Typ „
MessageBoard“, der als „attaching type“ dient und von „Message“-Objekten verwendet wird, um auf die angehängten Attribute zuzugreifen
Es folgt ein Beispiel für eine Implementierung. Zunächst muss ein angehängter Objekttyp mit den erforderlichen Eigenschaften und Signalen vorhanden sein, auf die das „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();
};Anschließend muss der Anbindungstyp „ MessageBoard “ eine „ qmlAttachedProperties() “-Methode deklarieren, die eine Instanz des angehängten Objekttyps zurückgibt, wie er von „MessageBoardAttachedType“ implementiert wird. Darüber hinaus muss „ MessageBoard “ über das Makro „ QML_ATTACHED()“ als Anbindungstyp 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 Typ vom Typ „ Message “ 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!")
}Darüber hinaus kann die C++-Implementierung durch Aufruf der Funktion ` qmlAttachedPropertiesObject()` auf die Instanz des angehängten Objekts zugreifen, die an ein beliebiges Objekt angehängt wurde.
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 unterklassifiziert werden, um angehängte Eigenschaften von einem übergeordneten Objekt an dessen untergeordnete Objekte weiterzugeben, ähnlich wie bei der Weitergabe von „ font “ und „ palette “. Es unterstützt die Weitergabe über „ items “, „ popups “ und „ windows “.
Eigenschaftsmodifikator-Typen
Ein Eigenschaftsmodifikatortyp ist eine spezielle Art von QML-Objekttyp. Eine Instanz eines Eigenschaftsmodifikatortyps wirkt sich auf eine Eigenschaft (einer QML-Objektinstanz) aus, auf die sie angewendet wird. Es gibt zwei verschiedene Arten von Eigenschaftsmodifikatortypen:
- Interceptoren für das Schreiben von Eigenschaftswerten
- Eigenschaftswertquellen
Ein Schreib-Interceptor für Eigenschaftswerte kann verwendet werden, um Werte zu filtern oder zu ändern, während sie in Eigenschaften geschrieben werden. Derzeit ist der einzige unterstützte Schreib-Interceptor für Eigenschaftswerte der Typ „ Behavior “, der durch den Import „ QtQuick “ 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 Eigenschaftsanimationstypen, die vom Import „ QtQuick “ bereitgestellt werden, sind Beispiele für Eigenschaftswertquellen.
Instanzen des Eigenschaftsmodifikatortyps können erstellt und über die Syntax „<ModifierType> on <propertyName>“ auf eine Eigenschaft eines QML-Objekts 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.
Kunden können ihre eigenen Typen von Eigenschaftswertquellen registrieren, derzeit jedoch keine Interceptoren für das Schreiben von Eigenschaftswerten.
Eigenschaftswertquellen
Eigenschaftswertquellen sind QML-Typen, die den Wert einer Eigenschaft im Laufe der Zeit mithilfe der Syntax „ <PropertyValueSource> on <property> “ automatisch aktualisieren können. Beispielsweise sind die verschiedenen Eigenschaftsanimationstypen, die vom Modul „ QtQuick “ bereitgestellt werden, Beispiele für Eigenschaftswertquellen.
Eine Eigenschaftswertquelle kann in C++ implementiert werden, indem man ` QQmlPropertyValueSource ` unterklassifiziert und eine Implementierung bereitstellt, die im Laufe der Zeit unterschiedliche Werte in eine Eigenschaft schreibt. Wenn die Eigenschaftswertquelle mithilfe der Syntax ` <PropertyValueSource> on <property> ` in QML auf eine Eigenschaft angewendet wird, erhält sie von der Engine eine Referenz auf diese Eigenschaft, sodass der Eigenschaftswert aktualisiert werden kann.
Nehmen wir beispielsweise an, es gibt eine Klasse ` RandomNumberGenerator `, die als Eigenschaftswertquelle zur Verfügung gestellt werden soll, sodass sie bei Anwendung auf eine QML-Eigenschaft den Eigenschaftswert alle 500 Millisekunden auf eine andere Zufallszahl aktualisiert. Zusätzlich kann für diesen Zufallszahlengenerator ein `maxValue` angegeben werden. Diese Klasse lässt sich wie folgt implementieren:
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 dem Typ die Eigenschaft mitzuteilen, 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 in QML als Eigenschaftswertquelle verwendet werden. Im folgenden Beispiel wird sie verwendet, um die Breite eines ` 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 reguläre QML-Typen, die Eigenschaften, Signale, Methoden usw. haben können, jedoch mit der zusätzlichen Fähigkeit, Eigenschaftswerte mithilfe der „ <PropertyValueSource> on <property> “-Syntax zu ändern.
Wird ein Objekt vom Typ „Eigenschaftswertquelle“ einer Eigenschaft zugewiesen, versucht QML zunächst, die Zuweisung wie bei einem regulären QML-Typ durchzuführen. Erst 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 übergeordneten Eigenschaften für QML-Objekttypen
Jeder von ` QObject` abgeleitete Typ, der als instanziierbarer QML-Objekttyp registriert ist, kann optional eine Standard-Eigenschaft für den Typ festlegen. Eine Standard-Eigenschaft ist die Eigenschaft, der die untergeordneten Elemente eines Objekts automatisch zugewiesen werden, wenn sie keiner bestimmten Eigenschaft zugewiesen sind.
Die Standard-Eigenschaft kann festgelegt werden, indem das Makro ` Q_CLASSINFO()` für eine Klasse mit einem bestimmten „DefaultProperty“-Wert aufgerufen wird. Beispielsweise legt die unten stehende Klasse ` MessageBoard ` ihre Eigenschaft ` messages ` als Standard-Eigenschaft 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;
};Dadurch können untergeordnete Elemente eines „ MessageBoard “-Objekts automatisch der Eigenschaft „ messages “ zugewiesen werden, wenn sie keiner bestimmten Eigenschaft zugewiesen sind. Beispiel:
MessageBoard {
Message { author: "Naomi" }
Message { author: "Clancy" }
}Wäre „ messages “ nicht als Standard-Eigenschaft festgelegt, müssten alle „ Message “-Objekte stattdessen explizit der Eigenschaft „ messages “ zugewiesen werden, wie im folgenden Beispiel gezeigt:
MessageBoard {
messages: [
Message { author: "Naomi" },
Message { author: "Clancy" }
]
}(Übrigens 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, sodass durch die Verwendung der Standardeigenschaft visuelle Kinder für ein Element deklariert werden können, ohne sie explizit der Eigenschaft „ children “ zuzuweisen.)
Darüber hinaus können Sie eine „ParentProperty“ Q_CLASSINFO() deklarieren, um der QML-Engine mitzuteilen, welche Eigenschaft das übergeordnete Objekt in der QML-Hierarchie kennzeichnen soll. Der Typ „Message“ könnte beispielsweise 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“-Eigenschaft ermöglicht „qmllint“ und anderen Tools einen besseren Einblick in die Absicht Ihres Codes und vermeidet Fehlalarme bei bestimmten Eigenschaftszugriffen.
Definition visueller Elemente mit dem Modul „ Qt Quick “
Beim Erstellen von Benutzeroberflächen mit dem Qt Quick Moduls müssen alle QML-Objekte, die visuell dargestellt werden sollen, vom Typ ` Item ` abgeleitet sein, da dies der Basistyp für alle visuellen Objekte in Qt Quick. Dieser Typ ` Item ` wird durch die C++-Klasse ` QQuickItem ` implementiert, die vom Qt Quick Modul bereitgestellt wird. Daher sollte diese Klasse als Basisklasse verwendet werden, wenn es erforderlich ist, einen visuellen Typ in C++ zu implementieren, der in eine QML-basierte Benutzeroberfläche integriert werden kann.
Weitere Informationen finden Sie in der Dokumentation zu „ QQuickItem “. Darüber hinaus zeigt das Tutorial „QML-Erweiterungen mit C++ schreiben“, wie ein auf „ QQuickItem “ basierendes visuelles Element in C++ implementiert und in eine auf „ Qt Quick “ basierende Benutzeroberfläche integriert werden kann.
Benachrichtigungen bei der Objektinitialisierung
Bei einigen benutzerdefinierten QML-Objekttypen kann es vorteilhaft sein, die Initialisierung bestimmter Daten zu verzögern, bis das Objekt erstellt und alle seine Eigenschaften festgelegt wurden. Dies kann beispielsweise der Fall sein, wenn die Initialisierung ressourcenintensiv ist oder wenn die Initialisierung erst durchgeführt werden soll, nachdem alle Eigenschaftswerte initialisiert wurden.
Das Qt Qml Modul stellt für diese Zwecke die Klasse „ QQmlParserStatus “ zur Verfügung, die als Basisklasse verwendet werden kann. Sie definiert eine Reihe virtueller Methoden, die in verschiedenen Phasen während der Instanziierung der Komponente aufgerufen werden. Um diese Benachrichtigungen zu erhalten, sollte eine C++-Klasse von „ QQmlParserStatus “ erben und das Qt-Metasystem mithilfe des Makros „ Q_INTERFACES()“ benachrichtigen.
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
}
};Siehe auch „ QML Type Registration Macros “ und „Übersicht – QML- und C++-Integration“.
© 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.