Das Eigenschaftssystem

Qt bietet ein ausgeklügeltes Eigenschaftssystem, das dem von einigen Compilerherstellern angebotenen ähnelt. Als compiler- und plattformunabhängige Bibliothek ist Qt jedoch nicht auf nicht standardisierte Compilerfunktionen wie __property oder [property] angewiesen. Die Qt-Lösung funktioniert mit jedem Standard-C++-Compiler auf jeder von Qt unterstützten Plattform. Sie basiert auf dem Meta-Objektsystem, das auch die Kommunikation zwischen Objekten über Signale und Slots ermöglicht.

Voraussetzungen für die Deklaration von Eigenschaften

Um eine Eigenschaft zu deklarieren, verwenden Sie das Makro Q_PROPERTY() in einer Klasse, die QObject erbt.

Q_PROPERTY(type name
           (READ getFunction [WRITE setFunction] |
            MEMBER memberName [(READ getFunction | WRITE setFunction)])
           [RESET resetFunction]
           [NOTIFY notifySignal]
           [REVISION int | REVISION(int[, int])]
           [DESIGNABLE bool]
           [SCRIPTABLE bool]
           [STORED bool]
           [USER bool]
           [BINDABLE bindableProperty]
           [CONSTANT]
           [FINAL]
           [REQUIRED])

Hier sind einige typische Beispiele für Eigenschaftsdeklarationen aus der Klasse QWidget.

Q_PROPERTY(bool focus READ hasFocus)
Q_PROPERTY(bool enabled READ isEnabled WRITE setEnabled)
Q_PROPERTY(QCursor cursor READ cursor WRITE setCursor RESET unsetCursor)

Das folgende Beispiel zeigt, wie man mit dem Schlüsselwort MEMBER Member-Variablen als Qt-Eigenschaften exportieren kann. Beachten Sie, dass ein NOTIFY Signal angegeben werden muss, um QML-Eigenschaftsbindungen zu ermöglichen.

    Q_PROPERTY(QColor color MEMBER m_color NOTIFY colorChanged)
    Q_PROPERTY(qreal spacing MEMBER m_spacing NOTIFY spacingChanged)
    Q_PROPERTY(QString text MEMBER m_text NOTIFY textChanged)
    ...
signals:
    void colorChanged();
    void spacingChanged();
    void textChanged(const QString &newText);

private:
    QColor  m_color;
    qreal   m_spacing;
    QString m_text;

Eine Eigenschaft verhält sich wie ein Datenelement der Klasse, hat aber zusätzliche Funktionen, die über das Meta-Objektsystem zugänglich sind.

  • Eine READ Accessor-Funktion ist erforderlich, wenn keine MEMBER Variable angegeben wurde. Sie dient zum Lesen des Eigenschaftswertes. Idealerweise wird für diesen Zweck eine const-Funktion verwendet, die entweder den Typ der Eigenschaft oder eine const-Referenz auf diesen Typ zurückgeben muss. QWidget::focus ist z. B. eine schreibgeschützte Eigenschaft mit der Funktion READ, QWidget::hasFocus(). Wenn ein BINDABLE angegeben ist, können Sie READ default schreiben, um den READ Accessor aus dem BINDABLE zu generieren.
  • Eine WRITE -Accessor-Funktion ist optional. Sie dient zum Setzen des Eigenschaftswertes. Sie muss void zurückgeben und genau ein Argument annehmen, entweder vom Typ der Eigenschaft oder einen Zeiger oder Verweis auf diesen Typ. z.B. hat QWidget::enabled die Funktion WRITE QWidget::setEnabled (). Nur-Lese-Eigenschaften benötigen keine WRITE Funktionen. z.B. QWidget::focus hat keine WRITE Funktion. Wenn Sie sowohl BINDABLE als auch WRITE default angeben, wird ein WRITE Accessor aus BINDABLE generiert. Der generierte WRITE Accessor wird kein explizit mit NOTIFY deklariertes Signal ausgeben. Sie sollten das Signal als Änderungshandler für BINDABLE registrieren, z. B. mit Q_OBJECT_BINDABLE_PROPERTY.
  • Eine MEMBER Variablenverknüpfung ist erforderlich, wenn keine READ Accessorfunktion angegeben ist. Dadurch wird die angegebene Mitgliedsvariable lesbar und beschreibbar, ohne dass READ und WRITE Accessorfunktionen erstellt werden müssen. Es ist immer noch möglich, READ oder WRITE Accessor-Funktionen zusätzlich zur MEMBER Variablen-Zuordnung zu verwenden (aber nicht beides), wenn Sie den Zugriff auf die Variable kontrollieren müssen.
  • Eine RESET Funktion ist optional. Sie dient dazu, die Eigenschaft auf ihren kontextspezifischen Standardwert zurückzusetzen. QWidget::cursor hat z. B. die typischen Funktionen READ und WRITE, QWidget::cursor() und QWidget::setCursor(), und es gibt auch eine Funktion RESET, QWidget::unsetCursor(), da kein Aufruf von QWidget::setCursor() das Zurücksetzen auf den kontextspezifischen Cursor bedeuten kann. Die Funktion RESET muss void zurückgeben und keine Parameter annehmen.
  • Ein NOTIFY Signal ist optional. Wenn es definiert ist, sollte es ein bestehendes Signal in dieser Klasse angeben, das immer dann ausgegeben wird, wenn sich der Wert der Eigenschaft ändert. NOTIFY Signale für MEMBER Variablen müssen null oder einen Parameter annehmen, der vom gleichen Typ wie die Eigenschaft sein muss. Der Parameter nimmt den neuen Wert der Eigenschaft an. Das Signal NOTIFY sollte nur dann ausgegeben werden, wenn die Eigenschaft tatsächlich geändert wurde, um zu vermeiden, dass Bindungen z.B. in QML unnötig neu ausgewertet werden. Das Signal wird automatisch ausgegeben, wenn die Eigenschaft über die Qt-API (QObject::setProperty, QMetaProperty, etc.) geändert wird, aber nicht, wenn das MEMBER direkt geändert wird.
  • Eine REVISION Nummer oder ein REVISION() Makro ist optional. Wenn es enthalten ist, definiert es die Eigenschaft und das Signal für den Notifier, das in einer bestimmten Revision der API verwendet werden soll (normalerweise für den Kontakt mit QML). Wenn es nicht enthalten ist, ist es standardmäßig auf 0 gesetzt.
  • Das Attribut DESIGNABLE gibt an, ob die Eigenschaft im Eigenschaftseditor des GUI-Design-Tools (z. B. Qt Widgets Designer) sichtbar sein soll. Die meisten Eigenschaften sind DESIGNABLE (Standardwert true). Gültige Werte sind true und false.
  • Das Attribut SCRIPTABLE gibt an, ob diese Eigenschaft von einer Skripting-Engine zugänglich sein soll (Standardwert true). Gültige Werte sind true und false.
  • Das Attribut STORED gibt an, ob die Eigenschaft als eigenständig oder abhängig von anderen Werten betrachtet werden soll. Es gibt auch an, ob der Eigenschaftswert gespeichert werden muss, wenn der Zustand des Objekts gespeichert wird. Die meisten Eigenschaften sind STORED (Standardwert true), aber z. B. QWidget::minimumWidth() hat STORED false, weil ihr Wert nur von der Breitenkomponente der Eigenschaft QWidget::minimumSize() übernommen wird, die eine QSize ist.
  • Das Attribut USER gibt an, ob die Eigenschaft als benutzerorientierte oder vom Benutzer editierbare Eigenschaft für die Klasse bestimmt ist. Normalerweise gibt es nur eine USER Eigenschaft pro Klasse (Standardwert false). QAbstractButton::checked ist z. B. die vom Benutzer editierbare Eigenschaft für (ankreuzbare) Schaltflächen. Beachten Sie, dass QItemDelegate die Eigenschaft USER eines Widgets erhält und festlegt.
  • Das BINDABLE bindableProperty Attribut zeigt an, dass die Eigenschaft Bindungen unterstützt und dass es möglich ist, Bindungen zu dieser Eigenschaft über das Meta-Objektsystem zu setzen und zu überprüfen (QMetaProperty). bindableProperty benennt ein Klassenmitglied vom Typ QBindable<T>, wobei T der Typ der Eigenschaft ist. Dieses Attribut wurde in Qt 6.0 eingeführt.
  • Das Vorhandensein des Attributs CONSTANT zeigt an, dass der Eigenschaftswert konstant ist. Für eine gegebene Objektinstanz muss die READ-Methode einer konstanten Eigenschaft jedes Mal, wenn sie aufgerufen wird, denselben Wert zurückgeben. Dieser konstante Wert kann für verschiedene Instanzen des Objekts unterschiedlich sein. Eine konstante Eigenschaft kann weder eine WRITE-Methode noch ein NOTIFY-Signal haben.
  • Das Vorhandensein des Attributs FINAL zeigt an, dass die Eigenschaft nicht von einer abgeleiteten Klasse überschrieben werden kann. Dies kann in einigen Fällen für Leistungsoptimierungen genutzt werden, wird aber von moc nicht erzwungen. Es muss darauf geachtet werden, dass eine FINAL Eigenschaft niemals überschrieben wird.
  • Das Vorhandensein des REQUIRED Attributs zeigt an, dass die Eigenschaft von einem Benutzer der Klasse gesetzt werden sollte. Dies wird von moc nicht erzwungen und ist hauptsächlich für Klassen nützlich, die QML ausgesetzt sind. In QML können Klassen mit REQUIRED-Eigenschaften erst dann instanziiert werden, wenn alle REQUIRED-Eigenschaften gesetzt wurden.

Die Funktionen READ, WRITE, und RESET können vererbt werden. Sie können auch virtuell sein. Wenn sie in Klassen vererbt werden, in denen Mehrfachvererbung verwendet wird, müssen sie von der ersten vererbten Klasse stammen.

Der Eigenschaftstyp kann ein beliebiger Typ sein, der von QVariant unterstützt wird, oder er kann ein benutzerdefinierter Typ sein. In diesem Beispiel wird die Klasse QDate als benutzerdefinierter Typ betrachtet.

Q_PROPERTY(QDate date READ getDate WRITE setDate)

Da QDate benutzerdefiniert ist, müssen Sie die Header-Datei <QDate> in die Eigenschaftsdeklaration aufnehmen.

Aus historischen Gründen sind QMap und QList als Eigenschaftstypen Synonyme für QVariantMap und QVariantList.

Lesen und Schreiben von Eigenschaften mit dem Meta-Objektsystem

Eine Eigenschaft kann mit den generischen Funktionen QObject::property() und QObject::setProperty() gelesen und geschrieben werden, ohne dass man außer dem Namen der Eigenschaft etwas über die besitzende Klasse weiß. Im folgenden Codeschnipsel setzen sowohl der Aufruf von QAbstractButton::setDown() als auch der Aufruf von QObject::setProperty() die Eigenschaft "down".

QPushButton *button = new QPushButton;
QObject *object = button;

button->setDown(true);
object->setProperty("down", true);

Der Zugriff auf eine Eigenschaft über den Accessor WRITE ist der bessere der beiden Wege, da er schneller ist und bessere Diagnosen zur Kompilierzeit liefert. Durch den Zugriff auf Eigenschaften über den Namen können Sie auf Klassen zugreifen, die Ihnen zur Kompilierzeit nicht bekannt sind. Sie können die Eigenschaften einer Klasse zur Laufzeit ermitteln, indem Sie ihre QObject, QMetaObject und QMetaProperties abfragen.

QObject *object = ...
const QMetaObject *metaobject = object->metaObject();
int count = metaobject->propertyCount();
for (int i=0; i<count; ++i) {
    QMetaProperty metaproperty = metaobject->property(i);
    const char *name = metaproperty.name();
    QVariant value = object->property(name);
    ...
}

In dem obigen Ausschnitt wird QMetaObject::property() verwendet, um metadata über jede in einer unbekannten Klasse definierte Eigenschaft abzurufen. Der Eigenschaftsname wird aus den Metadaten geholt und an QObject::property() übergeben, um die value der Eigenschaft in der aktuellen object zu erhalten.

Ein einfaches Beispiel

Angenommen, wir haben eine Klasse MyClass, die von QObject abgeleitet ist und das Makro Q_OBJECT verwendet. Wir wollen eine Eigenschaft in MyClass deklarieren, um einen Prioritätswert zu verfolgen. Der Name der Eigenschaft wird priority sein, und ihr Typ wird ein Aufzählungstyp namens Priority sein, der in MyClass definiert ist.

Wir deklarieren die Eigenschaft mit dem Makro Q_PROPERTY() im privaten Abschnitt der Klasse. Die erforderliche Funktion READ heißt priority, und wir schließen eine Funktion WRITE mit dem Namen setPriority ein. Der Aufzählungstyp muss im Meta-Objektsystem mit dem Makro Q_ENUM() registriert werden. Durch die Registrierung eines Aufzählungstyps werden die Aufzählungsnamen für die Verwendung in Aufrufen von QObject::setProperty() verfügbar. Wir müssen auch unsere eigenen Deklarationen für die Funktionen READ und WRITE bereitstellen. Die Deklaration von MyClass könnte dann wie folgt aussehen:

class MyClass : public QObject
{
    Q_OBJECT
    Q_PROPERTY(Priority priority READ priority WRITE setPriority NOTIFY priorityChanged)

public:
    MyClass(QObject *parent = nullptr);
    ~MyClass();

    enum Priority { High, Low, VeryHigh, VeryLow };
    Q_ENUM(Priority)

    void setPriority(Priority priority)
    {
        if (m_priority == priority)
            return;

        m_priority = priority;
        emit priorityChanged(priority);
    }
    Priority priority() const
    { return m_priority; }

signals:
    void priorityChanged(Priority);

private:
    Priority m_priority;
};

Die Funktion READ ist const und gibt den Eigenschaftstyp zurück. Die Funktion WRITE gibt void zurück und hat genau einen Parameter des Eigenschaftstyps. Der Meta-Object Compiler setzt diese Anforderungen durch. Die Gleichheitsprüfung in der Funktion WRITE ist zwar nicht obligatorisch, aber eine gute Praxis, da es keinen Sinn macht, dies zu melden und möglicherweise eine erneute Evaluierung an anderen Stellen zu erzwingen, wenn sich nichts geändert hat.

Mit einem Zeiger auf eine Instanz von MyClass oder einem Zeiger auf eine QObject, die eine Instanz von MyClass ist, haben wir zwei Möglichkeiten, die Prioritätseigenschaft zu setzen:

MyClass *myinstance = new MyClass;
QObject *object = myinstance;

myinstance->setPriority(MyClass::VeryHigh);
object->setProperty("priority", "VeryHigh");

Im Beispiel wird der Aufzählungstyp, der der Eigenschaftstyp ist, in MyClass deklariert und mit dem Makro Q_ENUM() im Meta-Objektsystem registriert. Dadurch stehen die Aufzählungswerte als Zeichenketten zur Verfügung, die wie im Aufruf von setProperty() verwendet werden können. Wäre der Aufzählungstyp in einer anderen Klasse deklariert worden, wäre sein voll qualifizierter Name (d. h. OtherClass::Priority) erforderlich, und diese andere Klasse müsste ebenfalls QObject erben und den Aufzählungstyp dort mit dem Makro Q_ENUM() registrieren.

Ein ähnliches Makro, Q_FLAG(), ist ebenfalls verfügbar. Wie Q_ENUM() registriert es einen Aufzählungstyp, aber es kennzeichnet den Typ als einen Satz von Flags, d. h. von Werten, die miteinander ODER-verknüpft werden können. Eine E/A-Klasse könnte die Aufzählungswerte Read und Write haben und QObject::setProperty() könnte dann Read | Write akzeptieren. Q_FLAG() sollte verwendet werden, um diesen Aufzählungstyp zu registrieren.

Dynamische Eigenschaften

QObject::setProperty() kann auch verwendet werden, um einer Instanz einer Klasse zur Laufzeit neue Eigenschaften hinzuzufügen. Wenn sie mit einem Namen und einem Wert aufgerufen wird, wird der Wert in der Eigenschaft gespeichert und true zurückgegeben, wenn eine Eigenschaft mit dem angegebenen Namen in QObject existiert und der angegebene Wert mit dem Typ der Eigenschaft kompatibel ist. Wenn der Wert nicht mit dem Typ der Eigenschaft kompatibel ist, wird die Eigenschaft nicht geändert und false wird zurückgegeben. Wenn jedoch die Eigenschaft mit dem angegebenen Namen in QObject nicht existiert (d.h. wenn sie nicht mit Q_PROPERTY() deklariert wurde), wird automatisch eine neue Eigenschaft mit dem angegebenen Namen und Wert zu QObject hinzugefügt, aber false wird trotzdem zurückgegeben. Das bedeutet, dass ein Rückgabewert von false nicht verwendet werden kann, um festzustellen, ob eine bestimmte Eigenschaft tatsächlich gesetzt wurde, es sei denn, Sie wissen im Voraus, dass die Eigenschaft bereits in QObject existiert.

Beachten Sie, dass dynamische Eigenschaften pro Instanz hinzugefügt werden, d.h. sie werden zu QObject hinzugefügt, nicht zu QMetaObject. Eine Eigenschaft kann aus einer Instanz entfernt werden, indem der Eigenschaftsname und ein ungültiger QVariant Wert an QObject::setProperty() übergeben wird. Der Standardkonstruktor für QVariant konstruiert eine ungültige QVariant.

Dynamische Eigenschaften können mit QObject::property() abgefragt werden, genau wie Eigenschaften, die zur Kompilierzeit mit Q_PROPERTY() deklariert wurden.

Eigenschaften und benutzerdefinierte Typen

Benutzerdefinierte Typen, die von Eigenschaften verwendet werden, müssen mit dem Makro Q_DECLARE_METATYPE() registriert werden, damit ihre Werte in QVariant Objekten gespeichert werden können. Dadurch eignen sie sich sowohl für statische Eigenschaften, die mit dem Makro Q_PROPERTY() in Klassendefinitionen deklariert werden, als auch für dynamische Eigenschaften, die zur Laufzeit erstellt werden.

Hinzufügen zusätzlicher Informationen zu einer Klasse

Mit dem Eigenschaftssystem verbunden ist ein zusätzliches Makro, Q_CLASSINFO(), mit dem zusätzliche Name-Wert-Paare an das Meta-Objekt einer Klasse angehängt werden können. Dies wird z.B. verwendet, um eine Eigenschaft als Standard-Eigenschaft im Kontext von QML-Objekttypen zu markieren:

Q_CLASSINFO("DefaultProperty", "content")

Wie andere Metadaten sind Klasseninformationen zur Laufzeit über das Meta-Objekt zugänglich; siehe QMetaObject::classInfo() für Details.

Verwendung von bindbaren Eigenschaften

Drei verschiedene Typen können verwendet werden, um bindbare Eigenschaften zu implementieren:

Der erste Typ ist eine allgemeine Klasse für bindbare Eigenschaften. Die beiden letzteren können nur innerhalb einer QObject verwendet werden.

Weitere Informationen, einschließlich Beispiele, finden Sie in den oben genannten Klassen und in den allgemeinen Tipps zur Implementierung und Verwendung von bindbaren Eigenschaften.

Siehe auch Meta-Objektsystem, Signale und Slots, Q_DECLARE_METATYPE(), QMetaType, QVariant, Qt Bindable Properties und Defining QML Types from C++.

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