Auf dieser Seite

Erstellen benutzerdefinierter Qt-Typen

Übersicht

Beim Erstellen von Benutzeroberflächen mit Qt, insbesondere solcher mit speziellen Steuerelementen und Funktionen, müssen Entwickler manchmal neue Datentypen erstellen, die neben den vorhandenen Werttypen von Qt oder an deren Stelle verwendet werden können.

Standardtypen wie „ QSize “, „ QColor “ und „ QString “ können alle in „ QVariant “-Objekten gespeichert, als Typen von Eigenschaften in auf „ QObject “ basierenden Klassen verwendet und bei der Signal-Slot-Kommunikation ausgegeben werden.

In diesem Dokument betrachten wir einen benutzerdefinierten Typ und beschreiben, wie man ihn in das Objektmodell von Qt integriert, sodass er auf dieselbe Weise wie Standardtypen von Qt gespeichert werden kann. Anschließend zeigen wir, wie man den benutzerdefinierten Typ registriert, damit er in Signal-Slot-Verbindungen verwendet werden kann.

Erstellen eines benutzerdefinierten Typs

Bevor wir beginnen, müssen wir sicherstellen, dass der von uns erstellte benutzerdefinierte Typ alle Anforderungen erfüllt, die unter QMetaType festgelegt sind. Mit anderen Worten, er muss Folgendes bereitstellen:

  • einen öffentlichen Standardkonstruktor,
  • einen öffentlichen Kopierkonstruktor und
  • einen öffentlichen Destruktor.

Die folgende Klassendefinition von ` Message ` 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 stellt außerdem einen Konstruktor für den normalen Gebrauch sowie zwei öffentliche Mitgliedsfunktionen bereit, die zum Abrufen der privaten Daten dienen.

Deklaration des Typs mit QMetaType

Die Klasse „ Message “ benötigt lediglich eine geeignete Implementierung, um einsatzfähig zu sein. Das Typsystem von Qt ist jedoch ohne gewisse Unterstützung nicht in der Lage zu erkennen, wie Instanzen dieser Klasse gespeichert, abgerufen und serialisiert werden müssen. So können wir beispielsweise keine Werte vom Typ „ Message “ in „ QVariant “ speichern.

Die für benutzerdefinierte Typen zuständige Klasse in Qt XML ist „ QMetaType “. Um diesen Typ der Klasse bekannt zu machen, 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);

Dadurch ist es nun möglich, Werte vom Typ ` Message ` in ` QVariant `-Objekten zu speichern und später wieder abzurufen:

QVariant stored;
stored.setValue(message);
    ...
Message retrieved = qvariant_cast<Message>(stored);
qDebug() << "Retrieved:" << retrieved;
retrieved = qvariant_cast<Message>(stored);
qDebug() << "Retrieved:" << retrieved;

Das Makro ` Q_DECLARE_METATYPE()` ermöglicht es zudem, diese Werte als Argumente für Signale zu verwenden, allerdings nur bei direkten Signal-Slot-Verbindungen. Um den benutzerdefinierten Typ allgemein mit dem Signal-Slot-Mechanismus nutzen zu können, müssen wir einige zusätzliche Schritte durchführen.

Erstellen und Löschen benutzerdefinierter Objekte

Obwohl die Deklaration im vorherigen 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 sie beispielsweise zwischen Objekten in verschiedenen Threads hergestellt werden. Dies liegt daran, dass das Metaobjektsystem nicht weiß, wie es die Erstellung und Löschung von Objekten des benutzerdefinierten Typs zur Laufzeit handhaben soll.

Um die Erstellung von Objekten zur Laufzeit zu ermöglichen, rufen Sie die Template-Funktion ` qRegisterMetaType()` auf, um den Typ beim Metaobjektsystem zu registrieren. Dadurch wird der Typ auch für die Queued-Signal-Slot-Kommunikation verfügbar, sofern Sie diese Funktion aufrufen, bevor Sie die erste Verbindung herstellen, die den Typ verwendet.

Das Beispiel „Queued Custom Type“ 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 der Datei „ window.cpp “ in einer Signal-Slot-Verbindung verwendet:

Window::Window(QWidget *parent)
    : QWidget(parent), thread(new RenderThread(this))
{
    ...
    connect(thread, &RenderThread::sendBlock,
            this, &Window::addBlock);
    ...
    setWindowTitle(tr("Queued Custom Type"));
}

Wird ein Typ in einer Queued-Verbindung verwendet, ohne dass er registriert wurde, wird eine Warnung auf der Konsole ausgegeben; zum Beispiel:

QObject::connect: Cannot queue arguments of type 'Block'
(Make sure 'Block' is registered using qRegisterMetaType().)

Den Typ druckbar machen

Oft ist es für Debugging-Zwecke sehr nützlich, einen benutzerdefinierten Typ ausgabefähig zu machen, wie im folgenden Code gezeigt:

Message message(body, headers);
qDebug() << "Original:" << message;

Dies wird durch die Erstellung eines Streaming-Operators für den Typ erreicht, der häufig in der Header-Datei für diesen Typ definiert ist:

QDebug operator<<(QDebug dbg, const Message &message);

Die Implementierung für den Typ ` Message ` ist hier darauf ausgelegt, die druckbare Darstellung so lesbar wie möglich zu gestalten:

QDebug operator<<(QDebug dbg, const Message &message)
{
    QDebugStateSaver saver(dbg);
    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 komplex gestaltet werden, wie Sie möchten. Beachten Sie, dass der von dieser Funktion zurückgegebene Wert das ` QDebug `-Objekt selbst ist, obwohl dieses häufig durch den Aufruf der Memberfunktion ` maybeSpace()` von ` QDebug ` abgerufen wird, die den Stream mit Leerzeichen auffüllt, um ihn lesbarer zu machen.

Weiterführende Literatur

Die Dokumentation zum Makro ` Q_DECLARE_METATYPE()` und zur Funktion ` qRegisterMetaType()` enthält detailliertere Informationen zu 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 Techniques“ bietet einen Überblick über die oben beschriebenen Debugging-Mechanismen.

© 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.