Benutzerdefinierte Qt-Typen erstellen
Übersicht
Bei der Erstellung von Benutzeroberflächen mit Qt, insbesondere bei solchen mit speziellen Steuerelementen und Funktionen, müssen Entwickler manchmal neue Datentypen erstellen, die neben oder anstelle der vorhandenen Qt-Wertetypen verwendet werden können.
Standardtypen wie QSize, QColor und QString können alle in QVariant Objekten gespeichert, als Typen von Eigenschaften in QObject-basierten Klassen verwendet und in der Signal-Slot-Kommunikation ausgegeben werden.
In diesem Dokument nehmen wir einen benutzerdefinierten Typ und beschreiben, wie er in das Qt-Objektmodell integriert werden kann, so dass er auf die gleiche Weise gespeichert werden kann wie die Standardtypen von Qt. Anschließend zeigen wir, wie man den benutzerdefinierten Typ registriert, damit er in Signalen und Slot-Verbindungen verwendet werden kann.
Erstellen eines benutzerdefinierten Typs
Bevor wir beginnen, müssen wir sicherstellen, dass der benutzerdefinierte Typ, den wir erstellen, alle Anforderungen erfüllt, die von QMetaType gestellt werden. Mit anderen Worten, er muss Folgendes bieten:
- einen öffentlichen Standardkonstruktor,
- einen öffentlichen Kopierkonstruktor und
- einen öffentlichen Destruktor.
Die folgende Message
Klassendefinition enthält diese Mitglieder:
class Message { public: Message() = default; ~Message() = default; Message(const Message &) = default; Message &operator=(const Message &) = default; Message(const QString &body, const QStringList &headers); QStringView body() const; QStringList headers() const; private: QString m_body; QStringList m_headers; };
Die Klasse bietet auch einen Konstruktor für den normalen Gebrauch und zwei öffentliche Mitgliedsfunktionen, die verwendet werden, um die privaten Daten zu erhalten.
Deklaration des Typs mit QMetaType
Die Klasse Message
benötigt nur eine geeignete Implementierung, um nutzbar zu sein. Das Typsystem von Qt wird jedoch nicht in der Lage sein, ohne Hilfe zu verstehen, wie Instanzen dieser Klasse gespeichert, abgerufen und serialisiert werden können. Zum Beispiel werden wir nicht in der Lage sein, Message
Werte in QVariant zu speichern.
Die für benutzerdefinierte Typen zuständige Klasse in Qt ist QMetaType. Um dieser Klasse den Typ mitzuteilen, rufen wir das Makro Q_DECLARE_METATYPE() für die Klasse in der Header-Datei auf, in der sie definiert ist:
Q_DECLARE_METATYPE(Message);
Damit ist es nun möglich, Message
Werte in QVariant Objekten zu speichern und später abzurufen:
QVariant stored; stored.setValue(message); ... Message retrieved = qvariant_cast<Message>(stored);qDebug() << "Retrieved:" << retrieved; abgerufen = qvariant_cast<Nachricht>(gespeichert);qDebug() << "Retrieved:" << retrieved;
Mit dem Makro Q_DECLARE_METATYPE() können diese Werte auch als Argumente für Signale verwendet werden, allerdings nur in direkten Signal-Slot-Verbindungen. Um den benutzerdefinierten Typ allgemein mit dem Signal- und Slot-Mechanismus nutzbar zu machen, müssen wir einige zusätzliche Arbeit leisten.
Erstellen und Zerstören von benutzerdefinierten Objekten
Obwohl die Deklaration im vorigen Abschnitt den Typ für die Verwendung in direkten Signal-Slot-Verbindungen verfügbar macht, kann er nicht für Signal-Slot-Verbindungen in der Warteschlange verwendet werden, wie z. B. solche, die zwischen Objekten in verschiedenen Threads hergestellt werden. Dies liegt daran, dass das Meta-Objektsystem nicht weiß, wie es die Erstellung und Zerstörung von Objekten des benutzerdefinierten Typs zur Laufzeit handhaben soll.
Um die Erstellung von Objekten zur Laufzeit zu ermöglichen, rufen Sie die Vorlagenfunktion qRegisterMetaType() auf, um sie beim Meta-Objektsystem zu registrieren. Dadurch wird der Typ auch für die Kommunikation über Signalschlitze in Warteschlangen verfügbar, sofern Sie die Funktion aufrufen, bevor Sie die erste Verbindung herstellen, die den Typ verwendet.
Das Beispiel für einen benutzerdefinierten Typ in der Warteschlange deklariert eine Klasse Block
, die in der Datei main.cpp
registriert ist:
int main(int argc, char *argv[]) { QApplication app(argc, argv); ... qRegisterMetaType<Block>(); ... return app.exec(); }
Dieser Typ wird später in einer Signal-Slot-Verbindung in der Datei window.cpp
verwendet:
Window::Window(QWidget *parent) : QWidget(parent), thread(new RenderThread(this)) { ... connect(thread, &RenderThread::sendBlock, this, &Window::addBlock); ... setWindowTitle(tr("Queued Custom Type")); }
Wenn ein Typ in einer Warteschlangenverbindung verwendet wird, ohne registriert zu sein, wird eine Warnung auf der Konsole ausgegeben, z. B:
QObject::connect: Cannot queue arguments of type 'Block' (Make sure 'Block' is registered using qRegisterMetaType().)
Den Typ druckbar machen
Es ist oft sehr nützlich, einen benutzerdefinierten Typ zu Debugging-Zwecken druckbar zu machen, wie im folgenden Code:
Nachricht message(body, headers);qDebug() << "Original:" << message;
Dies wird erreicht, indem ein Streaming-Operator für den Typ erstellt wird, der häufig in der Header-Datei für diesen Typ definiert ist:
Die Implementierung für den Typ Message
gibt sich hier einige Mühe, die druckbare Darstellung so lesbar wie möglich zu machen:
QDebug operator<<(QDebug dbg, const Message &message) { const QList<QStringView> pieces = message.body().split(u"\r\n", Qt::SkipEmptyParts); if (pieces.isEmpty()) dbg.nospace() << "Message()"; else if (pieces.size() == 1) dbg.nospace() << "Message(" << pieces.first() << ")"; else dbg.nospace() << "Message(" << pieces.first() << " ...)"; return dbg; }
Die an den Debug-Stream gesendete Ausgabe kann natürlich so einfach oder so kompliziert gestaltet werden, wie Sie möchten. Beachten Sie, dass der von dieser Funktion zurückgegebene Wert das QDebug -Objekt selbst ist, obwohl dieses oft durch den Aufruf der maybeSpace()-Mitgliedsfunktion von QDebug erhalten wird, die den Stream mit Leerzeichen auffüllt, um ihn lesbarer zu machen.
Weitere Lektüre
Die Dokumentation des Makros Q_DECLARE_METATYPE() und der Funktion qRegisterMetaType() enthält detailliertere Informationen über deren Verwendung und Einschränkungen.
Das Beispiel Queued Custom Type zeigt, wie ein benutzerdefinierter Typ mit den in diesem Dokument beschriebenen Funktionen implementiert wird.
Das Dokument Debugging-Techniken bietet einen Überblick über die oben beschriebenen Debugging-Mechanismen.
© 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.