プロパティシステム

Qt は、いくつかのコンパイラ・ベンダーが提供するものと同様の、洗練されたプロパ ティ・システムを提供します。しかし、コンパイラやプラットフォームに依存しないライブラリであるため、Qt は__property[property] のような非標準のコンパイラ機能には依存しません。 Qt のソリューションは、Qt がサポートするすべてのプラットフォームの標準 C++コンパイラで動作します。Qt のソリューションは、Qt がサポートするすべてのプラットフォーム上で、どの標準 C++ コンパイラでも動作します。これは、シグナルやスロットを介したオブジェクト間通信も提供するMeta-Object Systemに基づいています。

プロパティを宣言するための条件

プロパティを宣言するには、QObject を継承するクラスでQ_PROPERTY() マクロを使用します。

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])

以下は、クラス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)

MEMBER キーワードを使用して、メンバ変数を Qt プロパティとしてエクスポートする例を示します。QML のプロパティをバインドするためには、NOTIFY シグナルを指定しなけれ ばならないことに注意してください。

    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;

プロパティはクラスのデータメンバと同じように振る舞いますが、メタオブジェクトシステムからアクセスできる追加機能を持っています。

  • MEMBER 変数が指定されていない場合は、READ アクセッサ関数が必要です。これはプロパティの値を読み取るためのものです。理想的には、この目的のためにconst関数が使用され、プロパティの型またはその型へのconst参照のいずれかを返す必要があります。例えば、QWidget::focus は、READ 関数、QWidget::hasFocus ()を持つ読み取り専用プロパティです。BINDABLE が指定されている場合、READ default と書くと、BINDABLE からREAD アクセッサが生成されます。
  • WRITE アクセッサ関数はオプションです。これはプロパティ値を設定するためのものです。これはvoidを返し、ちょうど1つの引数(プロパティの型、またはその型へのポインタか参照)を取らなければなりません。例えば、QWidget::enabledWRITE 関数QWidget::setEnabled() を持っています。WRITE 例えば、QWidget::focus にはWRITE 関数はありません。BINDABLE BINDABLE Q_OBJECT_BINDABLE_PROPERTY と の両方を指定すると、 から アクセサが生成されます。生成された アクセサは、 で宣言されたシグナルを明示的に発行WRITE default BINDABLE WRITE WRITE NOTIFYしません
  • READ アクセッサ関数が指定されていない場合、MEMBER 変数の関連付けが必要です。これにより、READ およびWRITE アクセッサ関数を作成することなく、指定されたメンバ変数を読み書きできるようになります。変数アクセスを制御する必要がある場合は、MEMBER 変数アソシエーションに加えて、READ またはWRITE アクセッサ関数を使用することも可能です(両方は不可)。
  • RESET 関数はオプションです。例えば、QWidget::cursor は、典型的なREADWRITE 関数、QWidget::cursor() とQWidget::setCursor() を持っています。また、RESET 関数、QWidget::unsetCursor() も持っています。これは、QWidget::setCursor() を呼び出さないと、コンテキスト固有のカーソルにリセットされるからです。RESET 関数はvoidを返し、パラメータを取らない。
  • NOTIFY シグナルはオプションである。MEMBER 変数に対するNOTIFY シグナルは、0 個または 1 個のパラメー タを取る必要があります。パラメータはプロパティの新しい値を取ります。NOTIFY シグナルはプロパティが本当に変更されたときにのみ発行されるべきで、例えばQMLでバインディングが不必要に再評価されるのを避けるためです。このシグナルは、Qt API (QObject::setProperty,QMetaProperty など) を介してプロパティが変更されたときに自動的に発せられますが、MEMBER が直接変更されたときには発せられません。
  • REVISION 番号またはREVISION() マクロはオプションです。もし含まれていれば、APIの特定のリビジョンで使用されるプロパティとそのノーティファイアシグナルを定義します(通常はQMLに公開されます)。含まれていない場合、デフォルトは0です。
  • DESIGNABLE 属性は、GUI デザインツール(Qt Widgets Designer など)のプロパティエディタでプロパティを表示するかどうかを示します。ほとんどのプロパティはDESIGNABLE (デフォルトはtrue)です。有効な値はtrueとfalseです。
  • SCRIPTABLE 属性は、このプロパティがスクリプト・エンジンからアクセス可能かどうかを示します(デフォルトはtrue)。有効な値は true と false です。
  • STORED 属性は、このプロパティが単独で存在すると考えるべきか、他の値に依存すると考えるべきかを示します。また、オブジェクトの状態を保存する際に、プロパティの値を保存しなければならないかどうかを示します。ほとんどのプロパティはSTORED (デフォルトtrue)ですが、例えばQWidget::minimumWidth ()はSTORED falseとなっています。これは、その値がプロパティQWidget::minimumSize ()のwidthコンポーネントから取られているだけだからです。QSize
  • USER 属性は、そのプロパティがクラスのユーザー向けプロパティとして指定されているか、ユーザー編集可能プロパティとして指定されているかを示します。通常、USER プロパティはクラスごとに一つしかありません(デフォルトfalse)。例えば、QAbstractButton::checked は(チェック可能な)ボタン用のユーザ編集可能プロパティです。QItemDelegate 、ウィジェットのUSER プロパティを取得・設定することに注意してください。
  • BINDABLE bindableProperty 属性は、プロパティがバインディングをサポートしていることを示し、メタ・オブジェクト・システム(QMetaProperty)を介して、このプロパティへのバインディングを設定したり検査したりすることが可能であることを示します。bindableProperty は、QBindable<T>型のクラス・メンバに名前を付けます。ここで、Tはプロパティ型です。この属性は Qt 6.0 で導入されました。
  • CONSTANT 属性の存在は、プロパティ値が一定であることを示します。与えられたオブジェクト・インスタンスに対して、定数プロパティのREADメソッドは、呼び出されるたびに同じ値を返さなければなりません。この定数値は、オブジェクトのインスタンスによって異なる可能性があります。定数プロパティは、WRITEメソッドやNOTIFYシグナルを持つことはできない。
  • FINAL 属性の存在は、そのプロパティが派生クラスによってオーバーライドされないことを示します。これは場合によってはパフォーマンスの最適化に使用できますが、mocでは強制されません。FINAL プロパティをオーバーライドしないように注意しなければなりません。
  • REQUIRED 属性がある場合、そのプロパティはそのクラスのユーザが設定する必要があります。これはmocによって強制されるものではなく、主にQMLに公開されるクラスで有用です。QMLでは、すべてのREQUIREDプロパティが設定されない限り、REQUIREDプロパティを持つクラスはインスタンス化できません。

READ,WRITE,RESET 関数は継承することができます。また、仮想関数にすることもできます。多重継承が使用されているクラスで継承される場合、それらは最初に継承されたクラスから来なければなりません。

プロパティの型は、QVariant でサポートされている任意の型でも、ユーザー定義型でもかまいません。この例では、クラスQDate はユーザ定義型と見なされます。

Q_PROPERTY(QDate date READ getDate WRITE setDate)

QDate はユーザー定義型であるため、<QDate> ヘッダー・ ファイルをプロパティ宣言に含める必要があります。

歴史的な理由から、プロパティ型としてのQMapQListQVariantMapQVariantList の同義語です。

メタ・オブジェクト・システムによるプロパティの読み書き

プロパティの読み書きを行うには、ジェネリック関数QObject::property() とQObject::setProperty() を使用します。以下のコード・スニペットでは、QAbstractButton::setDown ()の呼び出しとQObject::setProperty ()の呼び出しの両方がプロパティ "down "を設定しています。

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

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

WRITE しかし、この方法でプロパティを設定するには、コンパイル時にそのクラスについて知っている必要があります。名前でプロパティにアクセスすると、コンパイル時に知らないクラスにアクセスできます。実行時にクラスのプロパティを見つけるにはQObjectQMetaObjectQMetaProperties をクエリします。

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);
    ...
}

上記のスニペットでは、QMetaObject::property() を使用して、未知のクラスで定義されている各プロパティについてmetadata を取得しています。プロパティ名はメタデータから取得され、QObject::property() に渡され、現在のobject にあるプロパティのvalue を取得します。

簡単な例

QObject から派生し、Q_OBJECT マクロを使用するクラスMyClass があるとします。このクラスはMyClass から派生し、 マクロを使用します。 でプロパティを宣言し、優先順位の値を記録したいとします。プロパティの名前はpriority とし、その型はMyClass で定義されている列挙型Priority とします。

クラスのprivateセクションで、Q_PROPERTY ()マクロを使用してプロパティを宣言します。必要なREAD 関数の名前はpriority で、WRITE 関数の名前はsetPriority です。列挙型は、Q_ENUM() マクロを使用してメタ・オブジェクト・システムに登録する必要があります。列挙型を登録すると、QObject::setProperty() の呼び出しで列挙子名を使用できるようになります。また、READWRITE 関数の宣言も必要です。MyClass の宣言は次のようになる:

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;
};

READ 関数は const で、プロパティ・タイプを返します。WRITE 関数はvoidを返し、プロパティ型のパラメータを1つだけ持ちます。Meta-Objectコンパイラは、これらの要件を強制します。WRITE 関数での等号チェックは必須ではありませんが、何も変更がない場合、他の場所に通知して再評価を強制する可能性があるのは無意味なので、良い習慣です。

MyClass のインスタンスへのポインタ、またはMyClass のインスタンスであるQObject へのポインタが与えられた場合、その優先度プロパティを設定する2つの方法があります:

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

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

この例では、プロパティ型である列挙型をMyClass で宣言し、Q_ENUM() マクロを使用してメタ・オブジェクト・システムに登録します。これにより、setProperty ()の呼び出しのように、列挙値を文字列として使用できるようになります。列挙型が別のクラスで宣言されていた場合、その完全修飾名(つまり OtherClass::Priority )が必要になり、その別のクラスもQObject を継承して、Q_ENUM() マクロを使用して列挙型を登録する必要があります。

同様のマクロとして、Q_FLAG()もあります。Q_ENUM() と同様、列挙型を登録しますが、その型はフラグの集合、つまりOR演算できる値であることを示します。I/Oクラスは列挙値ReadWrite を持ち、QObject::setProperty()はRead | Write を受け入れることができます。 この列挙型を登録するには、Q_FLAG()を使用します。

動的プロパティ

QObject::setProperty() を使用して、実行時にクラスのインスタンスに新しいプロパティを追加することもできます。名前と値を指定してこの関数を呼び出すと、指定された名前のプロパティがQObject に存在し、指定された値がプロパティの型と互換性がある場合、その値がプロパティに格納され、true が返されます。値がプロパティの型と互換性がない場合、プロパティは変更されず、falseが返されます。しかし、与えられた名前のプロパティがQObject に存在しない場合(つまり、Q_PROPERTY() で宣言されていない場合)、与えられた名前と値を持つ新しいプロパティが自動的にQObject に追加されますが、それでも false が返されます。つまり、QObject にそのプロパティがすでに存在することを事前に知っていない限り、false を返しても、特定のプロパティが実際に設定されたかどうかを判断することはできません。

ダイナミック・プロパティはインスタンスごとに追加されること、つまり、QMetaObject ではなくQObject に追加されることに注意してください。プロパティをインスタンスから削除するには、プロパティ名と無効なQVariant 値をQObject::setProperty() に渡します。QVariant のデフォルトのコンストラクタは、無効なQVariant を構築します。

動的プロパティは、コンパイル時にQ_PROPERTY() で宣言されたプロパティと同様に、QObject::property() で照会できます。

プロパティとカスタム型

プロパティで使用されるカスタム型は、その値をQVariant オブジェクトに格納できるように、Q_DECLARE_METATYPE() マクロを使用して登録する必要があります。これは、クラス定義でQ_PROPERTY() マクロを使用して宣言された静的プロパティと、実行時に作成された動的プロパティの両方で使用するのに適しています。

クラスへの追加情報の追加

プロパティ・システムに関連して、Q_CLASSINFO() というマクロが追加されています。このマクロを使用すると、クラスのメタ・オブジェクトに名前と 値のペアを追加することができます。このマクロは、例えばQML オブジェクトタイプのコンテキストで、あるプロパティをデフォルトのものとしてマークするために使用します:

Q_CLASSINFO("DefaultProperty", "content")

他のメタデータと同様に、クラスの情報はメタオブジェクトを通して実行時にアクセスすることができます。詳細はQMetaObject::classInfo() を参照してください。

バインダブル・プロパティの使用

バインド可能なプロパティを実装するために、3つの異なる型を使用することができます:

最初のものは、バインド可能なプロパティのための一般的なクラスです。後者の2つは、QObject の内部でのみ使用できます。

例を含む詳細については、上記のクラスと、バインド可能プロパティの実装と使用に関する一般的なヒントを参照してください。

Meta-Object System,Signals and Slots,Q_DECLARE_METATYPE(),QMetaType,QVariant,Qt Bindable Properties, andDefining QML Types from C++も参照して ください。

本ドキュメントに含まれる文書の著作権は、それぞれの所有者に帰属します 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。