Erstellen fortgeschrittener QML-Erweiterungen mit C++
Basisprojekt „BirthdayParty“
extending-qml-advanced/advanced1-Base-project
Dieses Tutorial nutzt das Beispiel einer Geburtstagsfeier, um einige der Funktionen von QML zu veranschaulichen. Der Code für die verschiedenen unten erläuterten Funktionen basiert auf diesem „BirthdayParty“-Projekt und stützt sich auf einige Inhalte aus dem ersten Tutorial zu QML-Erweiterungen. Dieses einfache Beispiel wird anschließend erweitert, um die verschiedenen unten erläuterten QML-Erweiterungen zu veranschaulichen. Den vollständigen Code für jede neue Erweiterung finden Sie in den Tutorials an der unter dem Titel des jeweiligen Abschnitts angegebenen Stelle oder über den Link zum Code ganz am Ende dieser Seite.
Das Basisprojekt definiert die Klassen „ Person “ und „ BirthdayParty “, die jeweils die Gäste und die Party selbst modellieren.
class Person : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL)
Q_PROPERTY(int shoeSize READ shoeSize WRITE setShoeSize NOTIFY shoeSizeChanged FINAL)
QML_ELEMENT
...
QString m_name;
int m_shoeSize = 0;
};
class BirthdayParty : public QObject
{
Q_OBJECT
Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL)
Q_PROPERTY(QQmlListProperty<Person> guests READ guests NOTIFY guestsChanged FINAL)
QML_ELEMENT
...
Person *m_host = nullptr;
QList<Person *> m_guests;
};Alle Informationen zur Party können dann in der entsprechenden QML-Datei gespeichert werden.
BirthdayParty {
host: Person {
name: "Bob Jones"
shoeSize: 12
}
guests: [
Person { name: "Leo Hodges" },
Person { name: "Jack Smith" },
Person { name: "Anne Brown" }
]
}Die Datei „ main.cpp “ erstellt eine einfache Shell-Anwendung, die anzeigt, wer Geburtstag hat und wer zu seiner Feier eingeladen ist.
QQmlEngine engine;
QQmlComponent component(&engine);
component.loadFromModule("People", "Main");
std::unique_ptr<BirthdayParty> party{ qobject_cast<BirthdayParty *>(component.create()) };Die App gibt die folgende Zusammenfassung der Feier aus.
"Bob Jones" is having a birthday!
They are inviting:
"Leo Hodges"
"Jack Smith"
"Anne Brown"In den folgenden Abschnitten wird erläutert, wie man mithilfe von Vererbung und Koerzition Unterstützung für „ Boy “ und „ Girl -Teilnehmer“ anstelle von nur „ Person “ hinzufügt, wie man Standard-Eigenschaften nutzt, um Teilnehmer der Feier implizit als Gäste zuzuweisen, wie man Eigenschaften gruppenweise statt einzeln zuweist, wie man angehängte Objekte nutzt, um die Antworten der eingeladenen Gäste zu verfolgen, wie man eine Eigenschaftswertquelle verwendet, um den Text des „Happy Birthday“-Liedes im Zeitverlauf anzuzeigen, und wie man Objekte von Drittanbietern für QML verfügbar macht.
Vererbung und Typumwandlung
extending-qml-advanced/advanced2-Inheritance-and-coercion
Derzeit wird jeder Anwesende als „Person“ modelliert. Das ist etwas zu allgemein, und es wäre schön, mehr über die Anwesenden zu erfahren. Indem wir sie als Jungen und Mädchen spezialisieren, können wir uns bereits ein besseres Bild davon machen, wer kommt.
Zu diesem Zweck werden die Klassen „ Boy “ und „ Girl “ eingeführt, die beide von „ Person “ erben.
class Boy : public Person
{
Q_OBJECT
QML_ELEMENT
public:
using Person::Person;
};
class Girl : public Person
{
Q_OBJECT
QML_ELEMENT
public:
using Person::Person;
};Die Klasse „ Person “ bleibt unverändert, und die C++-Klassen „ Boy “ und „ Girl “ sind einfache Erweiterungen davon. Die Typen und ihre QML-Namen werden mit „ QML_ELEMENT “ bei der QML-Engine registriert.
Beachten Sie, dass die Eigenschaften „ host “ und „ guests “ in „ BirthdayParty “ weiterhin Instanzen von „ Person “ entgegennehmen.
class BirthdayParty : public QObject
{
Q_OBJECT
Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL)
Q_PROPERTY(QQmlListProperty<Person> guests READ guests NOTIFY guestsChanged FINAL)
QML_ELEMENT
...
};Die Implementierung der Klasse ` Person ` selbst wurde nicht geändert. Da die Klasse ` Person ` jedoch als gemeinsame Basis für ` Boy ` und ` Girl` umfunktioniert wurde, sollte ` Person ` nicht mehr direkt aus QML instanziiert werden können. Stattdessen sollte explizit ` Boy ` oder ` Girl ` instanziiert werden.
class Person : public QObject
{
...
QML_ELEMENT
QML_UNCREATABLE("Person is an abstract base class.")
...
};Obwohl wir die Instanziierung von ` Person ` innerhalb von QML unterbinden wollen, muss es dennoch bei der QML-Engine registriert werden, damit es als Eigenschaftstyp verwendet werden kann und andere Typen in diesen Typ umgewandelt werden können. Genau das bewirkt das Makro ` QML_UNCREATABLE `. Da alle drei Typen – Person, Boy und Girl – im QML-System registriert sind, konvertiert QML bei der Zuweisung automatisch (und typsicher) die Objekte vom Typ „ Boy “ und „ Girl “ in einen „ Person “.
Mit diesen Änderungen können wir nun die Geburtstagsfeier mit den zusätzlichen Informationen zu den Teilnehmern wie folgt festlegen.
BirthdayParty {
host: Boy {
name: "Bob Jones"
shoeSize: 12
}
guests: [
Boy { name: "Leo Hodges" },
Boy { name: "Jack Smith" },
Girl { name: "Anne Brown" }
]
}Standard-Eigenschaften
extending-qml-advanced/advanced3-Default-properties
Derzeit wird in der QML-Datei jede Eigenschaft explizit zugewiesen. Beispielsweise wird der Eigenschaft „ host “ ein „ Boy “ zugewiesen und der Eigenschaft „ guests “ eine Liste von „ Boy “ oder „ Girl “. Das ist zwar einfach, lässt sich für diesen speziellen Anwendungsfall jedoch noch etwas vereinfachen. Anstatt die Eigenschaft „ guests “ explizit zuzuweisen, können wir Objekte vom Typ „ Boy “ und „ Girl “ direkt innerhalb der „Party“ hinzufügen und sie implizit der Eigenschaft „ guests “ zuweisen. Es ist sinnvoll, dass alle von uns angegebenen Teilnehmer, die nicht der Gastgeber sind, Gäste sind. Diese Änderung ist rein syntaktischer Natur, kann aber in vielen Situationen zu einem natürlicheren Erscheinungsbild beitragen.
Die Eigenschaft „ guests “ kann als Standardeigenschaft von „ BirthdayParty “ festgelegt werden. Das bedeutet, dass jedes innerhalb einer „ BirthdayParty “ erstellte Objekt implizit an die Standardeigenschaft „ guests “ angehängt wird. Der resultierende QML-Code sieht wie folgt aus.
BirthdayParty {
host: Boy {
name: "Bob Jones"
shoeSize: 12
}
Boy { name: "Leo Hodges" }
Boy { name: "Jack Smith" }
Girl { name: "Anne Brown" }
}Die einzige Änderung, die erforderlich ist, um dieses Verhalten zu aktivieren, besteht darin, die Klasseninfo-Annotation „ DefaultProperty “ zu „ BirthdayParty “ hinzuzufügen, um „ guests “ als dessen Standard-Eigenschaft festzulegen.
class BirthdayParty : public QObject
{
Q_OBJECT
Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL)
Q_PROPERTY(QQmlListProperty<Person> guests READ guests NOTIFY guestsChanged FINAL)
Q_CLASSINFO("DefaultProperty", "guests")
QML_ELEMENT
...
};Möglicherweise ist Ihnen dieser Mechanismus bereits bekannt. Die Standard-Eigenschaft für alle Nachkommen von ` Item ` in QML ist die Eigenschaft ` data `. Alle Elemente, die nicht explizit einer Eigenschaft eines ` Item ` hinzugefügt wurden, werden zu ` data` hinzugefügt. Dies sorgt für eine übersichtliche Struktur und reduziert unnötigen Code-Ballast.
Gruppierte Eigenschaften
extending-qml-advanced/advanced4-Grouped-properties
Es werden weitere Informationen zu den Schuhen der Gäste benötigt. Neben der Größe möchten wir auch die Farbe, die Marke und den Preis der Schuhe speichern. Diese Informationen werden in der Klasse „ ShoeDescription “ gespeichert.
class ShoeDescription : public QObject
{
Q_OBJECT
Q_PROPERTY(int size READ size WRITE setSize NOTIFY shoeChanged FINAL)
Q_PROPERTY(QColor color READ color WRITE setColor NOTIFY shoeChanged FINAL)
Q_PROPERTY(QString brand READ brand WRITE setBrand NOTIFY shoeChanged FINAL)
Q_PROPERTY(qreal price READ price WRITE setPrice NOTIFY shoeChanged FINAL)
...
};Jede Person verfügt nun über zwei Eigenschaften: eine „ name “ und eine „ShoeDescription“ shoe.
class Person : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL)
Q_PROPERTY(ShoeDescription *shoe READ shoe WRITE setShoe NOTIFY shoeChanged FINAL)
...
};Die Angabe der Werte für jedes Element der Schuhbeschreibung funktioniert zwar, ist aber etwas repetitiv.
Girl {
name: "Anne Brown"
shoe.size: 7
shoe.color: "red"
shoe.brand: "Job Macobs"
shoe.price: 99.99
}Gruppierte Eigenschaften bieten eine elegantere Möglichkeit, diese Eigenschaften zuzuweisen. Anstatt die Werte nacheinander jeder einzelnen Eigenschaft zuzuweisen, können die einzelnen Werte als Gruppe an die Eigenschaft „ shoe “ übergeben werden, was den Code lesbarer macht. Es sind keine Änderungen erforderlich, um diese Funktion zu aktivieren, da sie standardmäßig für das gesamte QML verfügbar ist.
host: Boy {
name: "Bob Jones"
shoe { size: 12; color: "white"; brand: "Bikey"; price: 90.0 }
}Angehängte Eigenschaften
extending-qml-advanced/advanced5-Attached-properties
Es ist an der Zeit, dass der Gastgeber Einladungen verschickt. Um den Überblick darüber zu behalten, welche Gäste wann auf die Einladung geantwortet haben, benötigen wir einen Ort, an dem wir diese Informationen speichern können. Eine Speicherung im „ BirthdayParty “-Objekt selbst wäre nicht wirklich sinnvoll. Besser wäre es, die Antworten als angehängte Objekte am „party“-Objekt zu speichern.
Zunächst deklarieren wir die Klasse „ BirthdayPartyAttached “, die die Antworten der Gäste enthält.
class BirthdayPartyAttached : public QObject
{
Q_OBJECT
Q_PROPERTY(QDate rsvp READ rsvp WRITE setRsvp NOTIFY rsvpChanged FINAL)
QML_ANONYMOUS
...
};Anschließend fügen wir sie der Klasse „ BirthdayParty “ hinzu und definieren die Methode „ qmlAttachedProperties() “, die das angehängte Objekt zurückgibt.
class BirthdayParty : public QObject
{
...
QML_ATTACHED(BirthdayPartyAttached)
...
static BirthdayPartyAttached *qmlAttachedProperties(QObject *);
};Nun können die angehängten Objekte im QML verwendet werden, um die RSVP-Informationen der eingeladenen Gäste zu speichern.
BirthdayParty {
Boy {
name: "Robert Campbell"
BirthdayParty.rsvp: Date.fromLocaleString(Qt.locale(), "2023-03-01", "yyyy-MM-dd")
}
Boy {
name: "Leo Hodges"
shoe { size: 10; color: "black"; brand: "Reebok"; price: 59.95 }
BirthdayParty.rsvp: Date.fromLocaleString(Qt.locale(), "2023-03-03", "yyyy-MM-dd")
}
host: Boy {
name: "Jack Smith"
shoe { size: 8; color: "blue"; brand: "Puma"; price: 19.95 }
}
}Schließlich kann auf die Informationen wie folgt zugegriffen werden.
QDate rsvpDate;
QObject *attached = qmlAttachedPropertiesObject<BirthdayParty>(guest, false);
if (attached)
rsvpDate = attached->property("rsvp").toDate();Das Programm gibt die folgende Zusammenfassung der bevorstehenden Party aus.
"Jack Smith" is having a birthday!
He is inviting:
"Robert Campbell" RSVP date: "Wed Mar 1 2023"
"Leo Hodges" RSVP date: "Mon Mar 6 2023"Eigenschaftswertquelle
extending-qml-advanced/advanced6-Property-value-source
Während der Party müssen die Gäste für den Gastgeber singen. Es wäre praktisch, wenn das Programm den Gästen den für diesen Anlass angepassten Liedtext anzeigen könnte. Zu diesem Zweck wird eine Eigenschaftswertquelle verwendet, um die Strophen des Liedes im Laufe der Zeit zu generieren.
class HappyBirthdaySong : public QObject, public QQmlPropertyValueSource
{
Q_OBJECT
Q_INTERFACES(QQmlPropertyValueSource)
...
void setTarget(const QQmlProperty &) override;
};Die Klasse „ HappyBirthdaySong “ wird als Wertquelle hinzugefügt. Sie muss von „ QQmlPropertyValueSource “ erben und die Schnittstelle „ QQmlPropertyValueSource “ mit dem Makro „ Q_INTERFACES “ implementieren. Mit der Funktion „ setTarget() “ wird definiert, auf welche Eigenschaft diese Quelle einwirkt. In diesem Fall schreibt die Wertquelle in die Eigenschaft „ announcement “ des „ BirthdayParty “, um die Liedtexte im Zeitverlauf anzuzeigen. Sie verfügt über einen internen Timer, der bewirkt, dass die Eigenschaft „ announcement “ des „ “ wiederholt auf die nächste Zeile des Liedtextes gesetzt wird.
In QML wird ein „ HappyBirthdaySong “ innerhalb des „ BirthdayParty “ instanziiert. Das Schlüsselwort „ on “ in seiner Signatur wird verwendet, um die Eigenschaft anzugeben, auf die die Wertquelle abzielt – in diesem Fall „ announcement “. Die Eigenschaft „ name “ des „ HappyBirthdaySong “-Objekts ist zudem an den Namen des Gastgebers der Party gebunden.
BirthdayParty {
id: party
HappyBirthdaySong on announcement {
name: party.host.name
}
...
}Das Programm zeigt mithilfe des Signals „ partyStarted “ den Zeitpunkt an, zu dem die Party begonnen hat, und gibt anschließend immer wieder die folgenden Geburtstagsverse aus.
Happy birthday to you,
Happy birthday to you,
Happy birthday dear Bob Jones,
Happy birthday to you!Integration fremder Objekte
extending-qml-advanced/advanced7-Foreign-objects-integration
Anstatt die Liedtexte einfach nur auf der Konsole auszugeben, möchten die Teilnehmer eine ausgefallenere Anzeige mit Farbunterstützung verwenden. Sie möchten diese in das Projekt integrieren, doch derzeit ist es nicht möglich, den Bildschirm über QML zu konfigurieren, da er aus einer Bibliothek eines Drittanbieters stammt. Um dies zu lösen, müssen die erforderlichen Typen der QML-Engine zugänglich gemacht werden, damit ihre Eigenschaften direkt in QML geändert werden können.
Die Anzeige kann über die Klasse „ ThirdPartyDisplay “ gesteuert werden. Sie verfügt über Eigenschaften zur Definition des Inhalts sowie der Vorder- und Hintergrundfarben des anzuzeigenden Textes.
class Q_DECL_EXPORT ThirdPartyDisplay : public QObject
{
Q_OBJECT
Q_PROPERTY(QString content READ content WRITE setContent NOTIFY contentChanged FINAL)
Q_PROPERTY(QColor foregroundColor READ foregroundColor WRITE setForegroundColor NOTIFY colorsChanged FINAL)
Q_PROPERTY(QColor backgroundColor READ backgroundColor WRITE setBackgroundColor NOTIFY colorsChanged FINAL)
...
};Um diesen Typ für QML verfügbar zu machen, können wir ihn mit ` QML_ELEMENT` bei der Engine registrieren. Da die Klasse jedoch nicht zur Bearbeitung zugänglich ist, kann ` QML_ELEMENT ` nicht einfach hinzugefügt werden. Um den Typ bei der Engine zu registrieren, muss die Registrierung von außen erfolgen. Hierfür ist „ QML_FOREIGN “ vorgesehen. Wenn dieses Makro in einem Typ in Verbindung mit anderen QML-Makros verwendet wird, wirken sich die anderen Makros nicht auf den Typ aus, in dem sie sich befinden, sondern auf den durch „ QML_FOREIGN “ angegebenen externen Typ.
class ForeignDisplay : public QObject
{
Q_OBJECT
QML_NAMED_ELEMENT(ThirdPartyDisplay)
QML_FOREIGN(ThirdPartyDisplay)
};Auf diese Weise verfügt „BirthdayParty“ nun über eine neue Eigenschaft mit der Anzeige.
class BirthdayParty : public QObject
{
Q_OBJECT
Q_PROPERTY(Person *host READ host WRITE setHost NOTIFY hostChanged FINAL)
Q_PROPERTY(QQmlListProperty<Person> guests READ guests NOTIFY guestsChanged FINAL)
Q_PROPERTY(QString announcement READ announcement WRITE setAnnouncement NOTIFY announcementChanged FINAL)
Q_PROPERTY(ThirdPartyDisplay *display READ display WRITE setDisplay NOTIFY displayChanged FINAL)
...
};Und in QML lassen sich die Farben des Textes auf dem ausgefallenen dritten Display explizit festlegen.
BirthdayParty {
display: ThirdPartyDisplay {
foregroundColor: "black"
backgroundColor: "white"
}
...
}Durch das Setzen der Eigenschaft „ announcement “ der „BirthdayParty“ wird die Nachricht nun an das ausgefallene Display gesendet, anstatt sie selbst auszugeben.
void BirthdayParty::setAnnouncement(const QString &announcement)
{
if (m_announcement != announcement) {
m_announcement = announcement;
emit announcementChanged();
}
m_display->setContent(announcement);
}Die Ausgabe sieht dann immer wieder ähnlich wie im vorherigen Abschnitt aus.
[Fancy ThirdPartyDisplay] Happy birthday to you,
[Fancy ThirdPartyDisplay] Happy birthday to you,
[Fancy ThirdPartyDisplay] Happy birthday dear Bob Jones,
[Fancy ThirdPartyDisplay] Happy birthday to you!Siehe auch „Festlegen von Standard- und übergeordneten Eigenschaften für QML-Objekttypen“, „Gruppierte Eigenschaften“, „Bereitstellen von angehängten Eigenschaften“, „Eigenschaftswertquellen“ und „Registrieren fremder Typen“.
© 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.