このページでは

Qtのバインド可能なプロパティ

Qt はバインド可能なプロパティを提供しています。バインド可能なプロパティとは、値を持つプロパティ、あるいは C++ 関数(通常は C++ ラムダ式)を使用して指定されるプロパティのことです。C++ 関数を使用して指定されている場合、その依存関係が変化するたびに自動的に更新されます。

バインド可能なプロパティは、データオブジェクトと管理データ構造体へのポインタで構成されるクラス `QProperty`、およびデータオブジェクトのみで構成され、カプセル化を行う `QObject ` を使用して管理データ構造体へのポインタを格納するクラス `QObjectBindableProperty` で実装されています。

バインド可能なプロパティを使用する理由

プロパティのバインディングは、QMLの中核機能の一つです。これにより、異なるオブジェクトのプロパティ間の関係を指定し、依存関係が変化した際にプロパティの値を自動的に更新することができます。 バインド可能なプロパティを使用すれば、QMLコードだけでなくC++でも同様の機能を実現できます。バインド可能なプロパティを使用することで、異なるオブジェクト間の依存関係の更新を追跡し、それに対応するための多くの定型コードを排除し、プログラムを簡素化することができます。

以下の入門例では、C++コードにおけるバインド可能なプロパティの使用方法を示しています。「バインド可能なプロパティ」のサンプルも参照して、バインド可能なプロパティがコードの改善にどのように役立つかを確認してください。

入門例

バインディング式は、他のQProperty の値を読み取って値を計算します。この依存関係は内部で追跡されています。いずれかのプロパティの依存関係に変化が検出されるたびに、バインディング式が再評価され、新しい結果がプロパティに適用されます。例:

QProperty<QString>firstname("John");
QProperty<QString>lastname("Smith");
QProperty<int>age(41);

QProperty<QString>fullname;
fullname.setBinding([&]() {returnfirstname.value()+ " " +lastname.value()+ " age: " +QString::number(age.value()); });

qDebug() << fullname.value(); // Prints "John Smith age: 41"

firstname= "Emma";// バインディングの再評価がトリガーされる

qDebug() << fullname.value(); // Prints the new value "Emma Smith age: 41"

// 誕生日が近づいている
age.setValue(age.value()+ 1);// 再評価をトリガーする

qDebug() << fullname.value(); // Prints "Emma Smith age: 42"

firstname プロパティに新しい値が割り当てられると、fullname のバインディング式が再評価されます。そのため、最後のqDebug() ステートメントがfullname プロパティのname値を読み取ろうとした際、新しい値が返されます。

バインディングは C++ 関数であるため、C++ で可能なことはすべて実行できます。これには、他の関数の呼び出しも含まれます。それらの関数が `QProperty` が保持する値にアクセスする場合、それらは自動的にそのバインディングの依存関係となります。

バインディング式は任意の型のプロパティを使用できるため、上記の例では age は整数であり、整数への変換を経て文字列値に折りたたまれますが、依存関係は完全に追跡されます。

バインディング可能なプロパティのゲッターとセッター

クラスに、QProperty またはQObjectBindableProperty を使用してバインド可能なプロパティがある場合、そのプロパティのゲッターおよびセッターを定義する際には特別な注意が必要です。

バインド可能なプロパティのゲッター

自動依存関係追跡システムの正常な動作を保証するためには、ゲッター内のあらゆる可能なコードパスにおいて、基になるプロパティオブジェクトから読み取りを行う必要があります。さらに、ゲッター内部でプロパティへの書き込みを行ってはなりません。ゲッター内で何かを再計算したり更新したりする設計パターンは、バインド可能なプロパティとは互換性がありません。

したがって、バインド可能なプロパティでは、単純なゲッターのみを使用することを推奨します。

バインド可能なプロパティのセッター

自動依存関係追跡システムの正常な動作を保証するためには、値が変更されなかった場合でも、セッター内のあらゆるコードパスで、基になるプロパティオブジェクトへの書き込みを行う必要があります。

セッター内のその他のコードは、不正である可能性が高くなります。新しい値に基づいて更新を行うコードは、バインディングを通じてプロパティが変更された際に実行されないため、バグである可能性が極めて高いです。

したがって、バインド可能なプロパティには、単純なセッターのみを使用することを推奨します。

バインダブルプロパティへの書き込み

バインド可能なプロパティは、変更のたびに依存するプロパティにその変更を通知します。これにより変更ハンドラがトリガーされ、その結果、任意のコードが呼び出される可能性があります。したがって、バインド可能なプロパティへの書き込みはすべて慎重に検証する必要があります。以下のような問題が発生する可能性があります。

バインド可能なプロパティへの中間値の書き込み

バインド可能なプロパティをアルゴリズム内の変数として使用してはなりません。書き込まれた各値は、依存するプロパティに通知されてしまいます。たとえば、次のコードでは、myPropertyに依存する他のプロパティに対して、まず42への変更が通知され、次にmaxValueへの変更が通知されてしまいます。

myProperty = somecomputation(); // returning, say, 42
if (myProperty.value() > maxValue)
    myProperty = maxValue;

その代わりに、別の変数で計算を実行してください。正しい使用方法は、次の例に示されています。

int newValue = someComputation();
if (newValue > maxValue)
    newValue = maxValue;
myProperty = newValue; // only write to the property once

遷移状態におけるバインド可能プロパティへの書き込み

バインド可能なプロパティがクラスのメンバである場合、そのプロパティへの書き込みのたびに、現在の状態が外部に漏れる可能性があります。したがって、クラスの不変条件が満たされていない一過性の状態では、バインド可能なプロパティへの書き込みを行ってはなりません。

たとえば、半径(radius) と面積(area)の 2 つのメンバを一貫して保持する円を表すクラスでは、セッターは次のようになります(ここで、radius はバインド可能なプロパティです)。

void setRadius(double newValue)
{
    radius = newValue; // this might trigger change handlers
    area = M_PI * radius * radius;
    emit radiusChanged();
}

この場合、変更ハンドラで実行されるコードは、円が新しい半径を持つ一方で、面積は依然として古い値のままの状態にある可能性があり、その円を使用してしまう恐れがあります。

仮想セッターおよびゲッターを持つバインド可能なプロパティ

プロパティのセッターおよびゲッターは、通常、最小限に抑え、プロパティの設定以外の処理を行わないようにすべきです。したがって、通常、そのようなセッターやゲッターを仮想とするのは適切ではありません。派生クラスがそこで行うべき意味のある処理は存在しないからです。

ただし、一部の Qt クラスには仮想セッターを持つプロパティが存在する場合があります。そのような Qt クラスをサブクラス化する際、これらのセッターをオーバーライドするには特別な注意が必要です。

いずれの場合も、バインディングが正しく機能するためには、基底クラスの実装を呼び出す必要があります。

以下の例はこのアプローチを示しています。

void DerivedClass::setValue(int val)
{
    // do something
    BaseClass::setValue(val);
    // probably do something else
}

バインド可能なプロパティへの書き込みに関する一般的なルールや推奨事項は、ここでもすべて適用されます。基底クラスの実装が呼び出されるとすぐに、すべてのオブザーバーに対してプロパティの変更が通知されます。つまり、基底クラスの実装を呼び出す前に、クラスの不変条件を満たしておく必要があります。

そのような仮想ゲッターやセッターが必要となる稀なケースでは、基底クラスはオーバーライドに課す要件を文書化しておくべきです。

プロパティバインディングの策定

正しい型に評価される C++ の式であれば、どれでもバインディング式として使用でき、setBinding() メソッドに渡すことができます。ただし、正しいバインディングを構成するには、いくつかのルールに従う必要があります。

依存関係追跡は、バインド可能なプロパティに対してのみ機能します。バインド式で使用されるすべてのプロパティがバインド可能なプロパティであることを確認するのは、開発者の責任です。バインド式でバインド不可能なプロパティが使用された場合、それらのプロパティの変更は、バインドされたプロパティの更新をトリガーしません。 コンパイル時にも実行時にも、警告やエラーは発生しません。バインドされたプロパティは、バインディング式で使用されているバインド可能なプロパティが変更された場合にのみ更新されます。 バインド対象のプロパティが、バインドされていない依存関係が変更されるたびに `markDirty` が呼び出されることが保証できる場合、バインド式内でバインド不可能なプロパティを使用することができます。

バインドされたプロパティは、その存続期間中にバインドを数回評価する場合があります。開発者は、バインド式で使用されるすべてのオブジェクトが、バインドよりも長く存続することを確認する必要があります。

バインド可能なプロパティシステムはスレッドセーフではありません。あるスレッドでのバインディング式で使用されるプロパティは、他のいかなるスレッドからも読み取られたり変更されたりしてはなりません。バインディングを持つプロパティを持つ `QObject` 派生クラスのオブジェクトは、別のスレッドに移動させてはなりません。 また、バインディングで使用されるプロパティを持つ `QObject` 派生クラスのオブジェクトは、別のスレッドに移動してはなりません。この文脈において、それが同じオブジェクト内のプロパティのバインディングで使用されているか、別のオブジェクト内のプロパティのバインディングで使用されているかは関係ありません。

バインディング式は、そのバインディングの対象となっているプロパティから読み取ってはなりません。そうしないと、評価ループが発生します。

バインディング式は、そのバインディングの対象となっているプロパティに書き込みを行ってはなりません。

バインディングとして使用される関数、およびバインディング内で呼び出されるすべてのコードは、co_await を行ってはなりません。そうすると、プロパティシステムによる依存関係の追跡が混乱する可能性があります。

バインド可能なプロパティとマルチスレッド

特に断りがない限り、バインド可能なプロパティはスレッドセーフではありません。バインド可能なプロパティは、それが作成されたスレッド以外のいかなるスレッドによっても読み取られたり変更されたりしてはなりません。

バインダブルプロパティの追跡

プロパティ間の関係をバインディングだけで表現できない場合があります。その代わりに、プロパティの値が変更されるたびにカスタムコードを実行し、その値を別のプロパティに代入するのではなく、アプリケーションの他の部分に渡す必要があるかもしれません。例えば、ネットワークソケットへのデータ書き込みや、デバッグ出力の表示などです。QProperty は、追跡のための2つのメカニズムを提供しています。

onValueChanged() を使用すると、プロパティの値が変更されるたびに呼び出されるコールバック関数を登録できます。プロパティの現在の値についてもコールバックを呼び出したい場合は、代わりに subscribe() を使用してコールバックを登録してください。

Q_PROPERTY との連携

BINDABLE を定義するQ_PROPERTYは、バインド可能であり、バインド式で使用できます。このようなプロパティは、QProperty 、QObjectBindableProperty 、またはQObjectComputedProperty を使用して実装できます。

BINDABLE を持たないQ_PROPERTYであっても、NOTIFY シグナルを定義している限り、バインドしたりバインド式で使用したりすることができます。 プロパティを、QBindable(QObject* obj, const char* property) コンストラクタを使用してQBindable でラップする必要があります。そうすることで、QBindable::setBinding() を使用してプロパティをバインドしたり、QBindable::value() を通じてバインディング式で使用したりできるようになります。プロパティがBINDABLE でない場合、依存関係の追跡を有効にするには、バインディング式で通常のプロパティのREAD 関数(またはMEMBER )の代わりにQBindable::value() を使用する必要があります。

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