このページについて

Qt State Machine C++ ガイド

ステートマシンフレームワークは、ステートグラフの作成および実行を行うためのクラスを提供します。このページでは、C++ におけるこのフレームワークの主な機能について解説します。

ステートマシンフレームワークのC++クラス

ステートマシンフレームワークに含まれるC++クラスの完全な一覧については、以下を参照してください。 Qt State Machine C++ Classes

簡単なステートマシン

ステートマシンAPIの中核となる機能を実演するために、簡単な例を見てみましょう。3つの状態(s1 、s2 、s3 )を持つステートマシンです。このステートマシンは、単一のQPushButton によって制御され、ボタンがクリックされると、別の状態に移行します。初期状態では、ステートマシンはs1 の状態にあります。このマシンのステートチャートは以下の通りです:

ボタンがクリックされた際の遷移で、3つの状態 s1、s2、s3 が循環する

以下のコードスニペットは、このようなステートマシンを作成するために必要なコードを示しています。まず、ステートマシンと各状態を作成します:

    QStateMachine machine;
    QState *s1 = new QState();
    QState *s2 = new QState();
    QState *s3 = new QState();

次に、QState::addTransition() 関数を使用して遷移を作成します:

    s1->addTransition(button, &QPushButton::clicked, s2);
    s2->addTransition(button, &QPushButton::clicked, s3);
    s3->addTransition(button, &QPushButton::clicked, s1);

次に、状態をマシンに追加し、マシンの初期状態を設定します:

    machine.addState(s1);
    machine.addState(s2);
    machine.addState(s3);
    machine.setInitialState(s1);

最後に、ステートマシンを起動します:

    machine.start();

このステートマシンは非同期で実行されます。つまり、アプリケーションのイベントループの一部となります。

状態への移行および移行完了時の処理

上記の状態マシンは、単にある状態から別の状態へ遷移するだけであり、いかなる操作も行いません。QState::assignProperty() 関数を使用すると、状態に入った際に、その状態が `QObject ` のプロパティを設定するようにできます。以下のスニペットでは、各状態ごとに `QLabel` の text プロパティに割り当てるべき値が指定されています:

    s1->assignProperty(label, "text", "In state s1");
    s2->assignProperty(label, "text", "In state s2");
    s3->assignProperty(label, "text", "In state s3");

いずれかの状態に入ると、ラベルのテキストがそれに応じて変更されます。

QState::entered() シグナルは状態に入ったときに発火し、QState::exited() シグナルは状態から出たときに発火します。以下のスニペットでは、状態s3 に入ったときにボタンのshowMaximized() スロットが呼び出され、s3 から出たときにボタンのshowMinimized() スロットが呼び出されます:

    QObject::connect(s3, &QState::entered, button, &QPushButton:showMaximized);
    QObject::connect(s3, &QState::exited, button, &QPushButton::showMinimized);

カスタム状態では、QAbstractState::onEntry() および QAbstractState::onExit() を再実装できます。

終了するステートマシン

前のセクションで定義したステートマシンは、決して終了しません。ステートマシンが終了するためには、最上位の最終状態(QFinalState オブジェクト)が必要です。ステートマシンが最上位の最終状態に入ると、マシンはQStateMachine::finished()シグナルを発生させ、停止します。

グラフに最終状態を導入するには、QFinalState オブジェクトを作成し、それを1つ以上の遷移のターゲットとして指定するだけで済みます。

状態のグループ化による遷移の共有

ユーザーが「終了」ボタンをクリックすることで、いつでもアプリケーションを終了できるようにしたいとします。これを実現するには、最終状態を作成し、それを「終了」ボタンの `clicked()` シグナルに関連付けられた遷移の宛先にする必要があります。s1 、s2 、s3 の各状態から遷移を追加することも可能ですが、これは冗長であるうえ、将来追加される新しい状態からも同様の遷移を追加することを忘れてはなりません。

s1 、s2 、s3 の各状態をグループ化することで、(すなわち、ステートマシンがどの状態にあっても「Quit」ボタンをクリックするとステートマシンが終了するという)同じ動作を実現できます。これを行うには、新しいトップレベルの状態を作成し、元の3つの状態をその子状態として設定します。以下の図は、新しいステートマシンを示しています。

ボタンがクリックされた際の遷移において、3つのサブ状態 s11、s12、s13 を循環する状態 s1

元の3つの状態は、新しい最上位状態s1 の子状態となったことを反映して、s11 、s12 、s13 に名前が変更されました。子状態は、親状態の遷移を暗黙的に継承します。つまり、s1 から最終状態s2 への遷移を1つ追加するだけで済むようになりました。s1 に追加される新しい状態も、この遷移を自動的に継承します。

状態をグループ化するために必要なのは、状態の作成時に適切な親状態を指定することだけです。また、どの子状態を初期状態とするか(つまり、親状態が遷移のターゲットとなった際に、ステートマシンがどの子状態に入るべきか)を指定する必要があります。

    QState *s1 = new QState();
    QState *s11 = new QState(s1);
    QState *s12 = new QState(s1);
    QState *s13 = new QState(s1);
    s1->setInitialState(s11);
    machine.addState(s1);
    QFinalState *s2 = new QFinalState();
    s1->addTransition(quitButton, &QPushButton::clicked, s2);
    machine.addState(s2);
    machine.setInitialState(s1);

    QObject::connect(&machine, &QStateMachine::finished,
                     QCoreApplication::instance(), &QCoreApplication::quit);

この例では、ステートマシンが終了した際にアプリケーションを終了させたいので、マシンのfinished()シグナルをアプリケーションのquit()スロットに接続しています。

子状態は、継承された遷移をオーバーライドすることができます。たとえば、次のコードは、ステートマシンがs12 状態にあるときに、事実上「Quit」ボタンを無視するようにする遷移を追加しています。

    s12->addTransition(quitButton, &QPushButton::clicked, s12);

遷移のターゲットとして、どの状態でも指定できます。つまり、ターゲット状態は、ソース状態と同じ階層レベルにある必要はありません。

履歴状態を使用した現在の状態の保存と復元

前のセクションで説明した例に「割り込み」メカニズムを追加したいと仮定します。 ユーザーはボタンをクリックして、ステートマシンに何らかの無関係なタスクを実行させ、その後、ステートマシンが以前行っていた処理を再開できるようにする必要があります(つまり、以前の状態、この場合はs11 、s12 、s13 のいずれかに戻る)。

このような挙動は、履歴状態(history states)を使用することで簡単にモデル化できます。履歴状態(QHistoryState オブジェクト)とは、親状態が最後に終了した際に親状態が置かれていた子状態を表す疑似状態のことです。

履歴状態は、現在の子状態を記録したい状態の子として作成されます。実行時にステートマシンがそのような状態の存在を検出すると、親状態が終了した際に現在の(実際の)子状態が自動的に記録されます。 履歴状態への遷移は、実際には、ステートマシンが以前に保存していた子状態への遷移であり、ステートマシンは自動的にその遷移を実際の子状態へと「転送」します。

次の図は、割り込みメカニズムが追加された後のステートマシンを示しています。

サブ状態と履歴状態を持つ状態 s1 において、中断ボタンを押すと s3 へ遷移し、処理が完了すると s1 の履歴状態に戻る

以下のコードは、その実装方法を示しています。この例では、s3 に入った際にメッセージボックスを表示し、その後、履歴状態を経由して直ちに前の子状態であるs1 に戻ります。

    QHistoryState *s1h = new QHistoryState(s1);

    QState *s3 = new QState();
    s3->assignProperty(label, "text", "In s3");
    QMessageBox *mbox = new QMessageBox(mainWindow);
    mbox->addButton(QMessageBox::Ok);
    mbox->setText("Interrupted!");
    mbox->setIcon(QMessageBox::Information);
    QObject::connect(s3, &QState::entered, mbox, &QMessageBox::exec);
    s3->addTransition(s1h);
    machine.addState(s3);

    s1->addTransition(interruptButton, &QPushButton::clicked, s3);

並列状態を用いた状態の組み合わせ爆発の回避

ある自動車の相互に排他的な一連の特性を、単一の状態マシンでモデル化したいと仮定します。対象とする特性が「Clean(きれい)」対「Dirty(汚い)」、および「Moving(移動中)」対「Not moving(停止中)」であるとしましょう。すべての可能な組み合わせを表現し、それら間を自由に移動できるようにするには、4つの相互に排他的な状態と8つの遷移が必要になります。

4つの状態:「清潔/静止」、「清潔/移動」、「汚れている/静止」、「汚れている/移動」――「開始」、「停止」、「清掃」、「汚染」への遷移を含む

もし3つ目の特性(例えば「赤」対「青」)を追加すると、状態の総数は2倍の8つになります。さらに4つ目の特性(例えば「クローズド」対「コンバーチブル」)を追加すれば、状態の総数は再び2倍になり、16つになります。

並列状態を使用すれば、プロパティを追加するにつれて、状態と遷移の総数は指数関数的に増加するのではなく、線形に増加します。さらに、並列状態から状態を追加したり削除したりしても、その兄弟状態には一切影響を与えません。

領域 s11(クリーン/ダーティ)および s12(移動中/非移動中)を持つ並列状態 s1

並列状態グループを作成するには、`QState::ParallelStates ` を `QState ` コンストラクタに渡します。

    QState *s1 = new QState(QState::ParallelStates);
    // s11 and s12 will be entered in parallel
    QState *s11 = new QState(s1);
    QState *s12 = new QState(s1);

並列状態グループに入ると、そのすべての子状態が同時に進入状態になります。個々の子状態内の遷移は通常通り動作します。ただし、いずれの子状態でも、親状態を離れる遷移を実行する可能性があります。これが発生すると、親状態とそのすべての子状態から離脱します。

ステートマシンフレームワークにおける並列処理は、インターリーブ型セマンティクスに従います。すべての並列操作は、イベント処理の単一の原子的なステップ内で実行されるため、いかなるイベントも並列操作を中断することはできません。ただし、マシン自体がシングルスレッドであるため、イベントの処理自体は依然として順次行われます。 例として、同じ並列状態グループから抜け出す2つの遷移があり、それらの条件が同時に真になる状況を考えます。この場合、2つのイベントのうち最後に処理されるイベントは、最初のイベントによってすでにマシンが並列状態から抜け出しているため、何の影響も及ぼしません。

複合状態の終了の検知

子状態は最終状態(QFinalState オブジェクト)となり得ます。最終子状態に入ると、親状態はQState::finished()シグナルを発行します。次の図は、最終状態に入る前に何らかの処理を行う複合状態s1 を示しています:

処理サブ状態を持つ状態 s1 から最終状態へ遷移し、s2 への移行がトリガーされる

s1 の最終状態に入ると、s1 は自動的にfinished()を発行します。このイベントによって状態変化をトリガーさせるには、シグナル遷移を使用します:

  s1->addTransition(s1, &QState::finished, s2);

複合状態において最終状態を使用するのは、複合状態の内部の詳細を隠蔽したい場合に有用です。つまり、外部から行える操作は、その状態に入ることと、状態が処理を完了した際の通知を受け取ることのみに限定されるべきです。 これは、複雑な(深くネストされた)ステートマシンを構築する際の、非常に強力な抽象化およびカプセル化メカニズムです。(上記の例では、s1 のfinished()シグナルに依存する代わりに、s1 のdone 状態から直接遷移を作成することももちろん可能ですが、その結果、s1 の実装詳細が露呈し、それに依存することになります)。

並列状態グループの場合、すべての子状態が最終状態に入ったときに、QState::finished()シグナルが発信されます。

ターゲットのない遷移

遷移にターゲット状態が必ずしも必要というわけではありません。ターゲットのない遷移は、他の遷移と同様にトリガーできます。違いは、ターゲットのない遷移がトリガーされても、状態変化は発生しないという点です。これにより、マシンが特定の状態にあるときに、その状態を離れることなく、シグナルやイベントに応答することができます。例:

QStateMachine machine;
QState *s1 = new QState(&machine);

QPushButton button;
QSignalTransition *trans = new QSignalTransition(&button, &QPushButton::clicked);
s1->addTransition(trans);

QMessageBox msgBox;
msgBox.setText("The button was clicked; carry on.");
QObject::connect(trans, QSignalTransition::triggered, &msgBox, &QMessageBox::exec);

machine.setInitialState(s1);

ボタンがクリックされるたびにメッセージボックスが表示されますが、ステートマシンは現在の状態(s1)のまま維持されます。一方、ターゲット状態が明示的に s1 に設定されていた場合、s1 は毎回終了して再進入することになります(例えば、QAbstractState::entered() およびQAbstractState::exited() シグナルが発信されます)。

イベント、遷移、およびガード

QStateMachine は独自のイベントループを実行します。シグナル遷移(QSignalTransition オブジェクト)の場合、QStateMachine は対応するシグナルをインターセプトすると、自動的に自身に対してQStateMachine::SignalEvent を送信します。同様に、QObject イベント遷移(QEventTransition オブジェクト)の場合も、QStateMachine::WrappedEvent が送信されます。

QStateMachine::postEvent() を使用すると、ステートマシンに独自のイベントを送信できます。

ステートマシンにカスタムイベントを投稿する場合、通常、そのタイプのイベントからトリガーされる 1 つ以上のカスタム遷移も用意します。このような遷移を作成するには、QAbstractTransition をサブクラス化し、eventTest() を再実装します。その際、イベントが自分のイベントタイプ(およびオプションで、イベントオブジェクトの属性などの他の基準)に一致するかどうかを確認します。

ここでは、文字列をステートマシンに投稿するための独自のカスタムイベント型 `StringEvent` を定義します:

struct StringEvent : public QEvent
{
    StringEvent(const QString &val)
    : QEvent(QEvent::Type(QEvent::User+1)),
      value(val) {}

    QString value;
};

次に、イベントの文字列が特定の文字列と一致した場合にのみトリガーされる遷移(ガード付き遷移)を定義します:

class StringTransition : public QAbstractTransition
{
    Q_OBJECT

public:
    StringTransition(const QString &value)
        : m_value(value) {}

protected:
    bool eventTest(QEvent *e) override
    {
        if (e->type() != QEvent::Type(QEvent::User+1)) // StringEvent
            return false;
        StringEvent *se = static_cast<StringEvent*>(e);
        return (m_value == se->value);
    }

    void onTransition(QEvent *) override {}

private:
    QString m_value;
};

eventTest() の再実装では、まずイベントタイプが目的のものかどうかを確認します。目的のものだった場合、そのイベントをStringEvent にキャストし、文字列比較を行います。

以下は、カスタムイベントと遷移を使用したステートチャートです。

カスタムイベント処理のためのStringTransitionを使用したs1およびs2の状態

ステートチャートの実装は以下のようになります:

    QStateMachine machine;
    QState *s1 = new QState();
    QState *s2 = new QState();
    QFinalState *done = new QFinalState();

    StringTransition *t1 = new StringTransition("Hello");
    t1->setTargetState(s2);
    s1->addTransition(t1);
    StringTransition *t2 = new StringTransition("world");
    t2->setTargetState(done);
    s2->addTransition(t2);

    machine.addState(s1);
    machine.addState(s2);
    machine.addState(done);
    machine.setInitialState(s1);

ステートマシンが起動すると、そこにイベントを投稿できるようになります。

    machine.postEvent(new StringEvent("Hello"));
    machine.postEvent(new StringEvent("world"));

関連する遷移のいずれによっても処理されないイベントは、ステートマシンによって黙って消費されます。ステートをグループ化し、そのようなイベントに対するデフォルトの処理を用意しておくと便利です。例えば、次のステートチャートに示されているように:

s1 状態(サブ状態 s11 および s12 を含む)、カスタムイベント遷移、および完了時の状態の祝賀

深くネストされたステートチャートの場合、最も適切な粒度レベルでこのような「フォールバック」遷移を追加できます。

プロパティを自動的に復元するための復元ポリシーの使用

一部のステートマシンでは、状態が非アクティブになった際のプロパティの復元ではなく、状態におけるプロパティの割り当てに注力することが有用な場合があります。 マシンが、そのプロパティに明示的に値を指定していない状態に入った際に、そのプロパティを常に初期値に復元すべきであることが分かっている場合は、グローバルな復元ポリシーを QStateMachine::RestoreProperties に設定できます。

QStateMachine machine;
machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties);

この復元ポリシーが設定されている場合、マシンはすべてのプロパティを自動的に復元します。特定のプロパティが設定されていない状態に入った場合、まず祖先の階層を検索して、そのプロパティがそこで定義されているかどうかを確認します。 定義されている場合、そのプロパティは最も近い祖先で定義された値に復元されます。定義されていない場合は、初期値(つまり、各状態でのプロパティの代入が実行される前の値)に復元されます。

次のコードを例に挙げます。

    QStateMachine machine;
    machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties);

    QState *s1 = new QState();
    s1->assignProperty(object, "fooBar", 1.0);
    machine.addState(s1);
    machine.setInitialState(s1);

    QState *s2 = new QState();
    machine.addState(s2);

マシン起動時にプロパティ `fooBar ` の値が 0.0 であるとします。マシンが状態 `s1` にあるとき、この状態ではプロパティに明示的に 1.0 が割り当てられているため、プロパティの値は 1.0 になります。マシンが状態 `s2` にあるとき、プロパティに対して明示的な値が定義されていないため、暗黙的に 0.0 に復元されます。

ネストされた状態を使用している場合、親状態がプロパティの値を定義し、その値は、プロパティに明示的に値を割り当てていないすべての子孫状態によって継承されます。

    QStateMachine machine;
    machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties);

    QState *s1 = new QState();
    s1->assignProperty(object, "fooBar", 1.0);
    machine.addState(s1);
    machine.setInitialState(s1);

    QState *s2 = new QState(s1);
    s2->assignProperty(object, "fooBar", 2.0);
    s1->setInitialState(s2);

    QState *s3 = new QState(s1);

ここで、s1 には2つの子状態があります:s2 とs3 です。s2 状態に入ると、プロパティfooBar の値は2.0になります。これは、この状態に対して明示的に定義されているためです。マシンがs3 状態にある場合、この状態に対して値は定義されていませんが、s1 がプロパティの値を1.0と定義しているため、fooBar にはこの値が割り当てられます。

アニメーションとステートマシン

ステートマシンAPIはアニメーションフレームワークと連携し、ステート内でプロパティが割り当てられる際に、自動的にアニメーションを実行できるようにします。

ステートマシンは、アニメーションを再生できる特別な状態を提供します。QState は、状態への移行時や状態からの離脱時にプロパティを設定することもでき、この特別なアニメーション状態は、QPropertyAnimation が指定されると、それらの値の間を補間します。

QSignalTransition またはQEventTransition クラスを使用することで、状態間の遷移に1つ以上のアニメーションを関連付けることができます。これらのクラスはどちらもQAbstractTransition を継承しており、このクラスには遷移発生時にトリガーされる1つ以上のアニメーションを追加できる便利な関数addAnimation()が定義されています。

また、開始値や終了値を自分で設定するのではなく、プロパティを各状態に紐付けることも可能です。

たとえば、次のようなコードがあるとします。

    QState *s1 = new QState();
    QState *s2 = new QState();

    s1->assignProperty(button, "geometry", QRectF(0, 0, 50, 50));
    s2->assignProperty(button, "geometry", QRectF(0, 0, 100, 100));

    s1->addTransition(button, &QPushButton::clicked, s2);

ここでは、ユーザーインターフェースの2つの状態を定義しています。s1 では、button は小さく、s2 では大きくなっています。ボタンをクリックしてs1 からs2 へ遷移すると、指定された状態に入った時点でボタンのジオメトリが即座に設定されます。しかし、遷移をスムーズにしたい場合は、QPropertyAnimation を作成し、これを遷移オブジェクトに追加するだけで済みます。

    QState *s1 = new QState();
    QState *s2 = new QState();

    s1->assignProperty(button, "geometry", QRectF(0, 0, 50, 50));
    s2->assignProperty(button, "geometry", QRectF(0, 0, 100, 100));

    QSignalTransition *transition = s1->addTransition(button, &QPushButton::clicked, s2);
    transition->addAnimation(new QPropertyAnimation(button, "geometry"));

対象のプロパティにアニメーションを追加すると、状態に入った時点でプロパティの割り当てが即座に反映されることはなくなります。代わりに、状態に入った時点でアニメーションの再生が開始され、プロパティの割り当てが滑らかにアニメーション化されます。アニメーションの開始値や終了値を設定していないため、これらは暗黙的に設定されます。 アニメーションの開始値は、アニメーション開始時のプロパティの現在の値となり、終了値は、その状態に対して定義されたプロパティの代入に基づいて設定されます。

ステートマシンのグローバル復元ポリシーが QStateMachine::RestoreProperties に設定されている場合、プロパティの復元に対してもアニメーションを追加することが可能です。

状態内のすべてのプロパティが設定済みであることの検出

アニメーションを使用してプロパティを割り当てる場合、ステートは、マシンが特定のステートにあるときにプロパティが持つ正確な値を定義しなくなります。アニメーションの実行中は、アニメーションに応じて、プロパティはあらゆる値をとる可能性があります。

場合によっては、プロパティに状態によって定義された値が実際に割り当てられたタイミングを検出できると便利なことがあります。

たとえば、次のようなコードがあるとします:

    QMessageBox *messageBox = new QMessageBox(mainWindow);
    messageBox->addButton(QMessageBox::Ok);
    messageBox->setText("Button geometry has been set!");
    messageBox->setIcon(QMessageBox::Information);

    QState *s1 = new QState();

    QState *s2 = new QState();
    s2->assignProperty(button, "geometry", QRectF(0, 0, 50, 50));
    connect(s2, &QState::entered, messageBox, SLOT(exec()));

    s1->addTransition(button, &QPushButton::clicked, s2);

button がクリックされると、マシンは状態s2 に移行し、ボタンのジオメトリが設定された後、ジオメトリが変更されたことをユーザーに知らせるメッセージボックスが表示されます。

アニメーションを使用しない通常の場合、これは期待通りに動作します。しかし、s1 とs2 の遷移時に、button のgeometry に対するアニメーションが設定されている場合、s2 に入った時点でアニメーションが開始されますが、アニメーションの実行が完了する前にgeometry プロパティが定義された値に実際に到達することはありません。 この場合、ボタンのジオメトリが実際に設定される前にメッセージボックスがポップアップしてしまいます。

ジオメトリが実際に最終値に達するまでメッセージボックスが表示されないようにするには、状態の `propertiesAssigned()` シグナルを使用します。`propertiesAssigned()` シグナルは、プロパティに最終値が割り当てられたときに発火します。これは、即座に行われる場合でも、アニメーションの再生が終了した後に行われる場合でも同様です。

    QMessageBox *messageBox = new QMessageBox(mainWindow);
    messageBox->addButton(QMessageBox::Ok);
    messageBox->setText("Button geometry has been set!");
    messageBox->setIcon(QMessageBox::Information);

    QState *s1 = new QState();

    QState *s2 = new QState();
    s2->assignProperty(button, "geometry", QRectF(0, 0, 50, 50));

    QState *s3 = new QState();
    connect(s3, &QState::entered, messageBox, SLOT(exec()));

    s1->addTransition(button, &QPushButton::clicked, s2);
    s2->addTransition(s2, &QState::propertiesAssigned, s3);

この例では、button がクリックされると、マシンはs2 状態に入ります。geometry プロパティがQRect(0, 0, 50, 50) に設定されるまで、マシンはs2 状態にとどまります。その後、s3 状態へと遷移します。s3 状態に入ると、メッセージボックスが表示されます。s2 状態への遷移にgeometry プロパティのアニメーションが設定されている場合、マシンはアニメーションの再生が完了するまでs2 状態にとどまります。 そのようなアニメーションがない場合は、単にプロパティを設定し、直ちに状態s3 に入ります。

いずれの場合も、マシンが状態s3 にあるときは、プロパティgeometry に定義された値が確実に割り当てられていることが保証されます。

グローバルな復元ポリシーが QStateMachine::RestoreProperties に設定されている場合、これらも実行されるまで、状態はpropertiesAssigned() シグナルをエミットしません。

アニメーションが終了する前に状態を離脱した場合の挙動

状態にプロパティの割り当てがあり、その状態への遷移にプロパティのアニメーションが含まれている場合、プロパティが状態によって定義された値に割り当てられる前に、その状態から抜け出してしまう可能性があります。これは特に、前のセクションで説明したように、propertiesAssigned() シグナルに依存しない状態からの遷移が存在する場合に当てはまります。

ステートマシンAPIは、ステートマシンによって割り当てられたプロパティについて、以下のいずれかが満たされることを保証します。

  • プロパティに明示的に値が割り当てられている。
  • そのプロパティに明示的に割り当てられた値へと、現在アニメーションによって遷移中である。

アニメーションが完了する前に状態を離脱した場合、ステートマシンの挙動は遷移先の状態によって異なります。遷移先の状態がプロパティに明示的に値を割り当てる場合、追加の処理は行われません。プロパティには、遷移先の状態で定義された値が割り当てられます。

ターゲット状態がそのプロパティに値を割り当てない場合、2つの選択肢があります:デフォルトでは、そのプロパティには、離脱する状態によって定義された値(アニメーションの再生が完了していた場合に割り当てられていたはずの値)が割り当てられます。 ただし、グローバルな復元ポリシーが設定されている場合は、それが優先され、プロパティは通常通り復元されます。

デフォルトのアニメーション

前述のように、遷移にアニメーションを追加することで、ターゲット状態でのプロパティの割り当てがアニメーション化されるようにすることができます。 どの遷移が行われるかに関係なく、特定のプロパティに対して特定のアニメーションを使用したい場合は、そのアニメーションをステートマシンのデフォルトのアニメーションとして追加することができます。これは、マシンの構築時に、特定の状態によって割り当てられる(または復元される)プロパティが不明な場合に特に役立ちます。

QState *s1 = new QState();
QState *s2 = new QState();

s2->assignProperty(object, "fooBar", 2.0);
s1->addTransition(s2);

QStateMachine machine;
machine.setInitialState(s1);
machine.addDefaultAnimation(new QPropertyAnimation(object, "fooBar"));

マシンが状態 `s2` にある場合、プロパティ `fooBar ` は `s2` によって割り当てられるため、マシンはこのプロパティに対してデフォルトのアニメーションを再生します。

なお、遷移で明示的に設定されたアニメーションは、そのプロパティのデフォルトのアニメーションよりも優先されます。

ステートマシンのネスト

QStateMachine は、QState のサブクラスです。これにより、あるステートマシンを別のマシンの子ステートとすることができます。QStateMachine はQState::onEntry()を再実装し、QStateMachine::start()を呼び出すため、子ステートマシンに入ると、自動的に実行が開始されます。

親ステートマシンは、ステートマシンアルゴリズムにおいて子ステートマシンを原子的な状態として扱います。子ステートマシンは自己完結型であり、独自のイベントキューと構成を維持します。特に、子ステートマシンのconfiguration()は親ステートマシンの構成の一部ではない点に注意してください(構成の一部となるのは子ステートマシン自体のみです)。

子ステートマシンの状態は、親ステートマシンにおける遷移のターゲットとして指定することはできません。ターゲットとして指定できるのは、子ステートマシン自体のみです。逆に、親ステートマシンの状態も、子ステートマシンにおける遷移のターゲットとして指定することはできません。子ステートマシンのfinished()シグナルを使用することで、親ステートマシンでの遷移をトリガーすることができます。

「 Qt State Machine の概要」および「Qt State Machine QMLガイド」も参照してください 。

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