Qt ステートマシン C++ ガイド
ステートマシン・フレームワークは、ステートグラフの作成と実行のためのクラスを提供します。このページでは、フレームワークの主な機能を C++ で説明します。
ステートマシン フレームワークの C++ クラス
ステートマシン(State Machine)フレームワークの C++ クラスの全リストは、以下を参照してください。Qt State Machine C++ Classes
単純なステート・マシン
ステートマシンAPIのコア機能を示すために、小さな例を見てみましょう:s1
、s2
、s3
の3つの状態を持つステート・マシン。QPushButtonボタンがクリックされると、マシンは別の状態に遷移する。最初は、ステートマシンは状態s1
にある。このマシンのステートチャートは以下のとおりである:
次のスニペットは、このようなステートマシンを作成するのに必要なコードを示している。まず、ステートマシンと状態を作成する:
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() シグナルが発せられます。次のスニペットでは、ボタンのshowMaximized ()スロットは、s3
(状態)が入力されたときに呼び出され、ボタンのshowMinimized ()スロットは、s3
(状態)が終了したときに呼び出されます:
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 つ以上のトランジションのターゲットとして使用することだけです。
ステートをグループ化してトランジションを共有する
ユーザーがいつでもQuitボタンをクリックしてアプリケーションを終了できるようにしたいとします。これを実現するには、最終ステートを作成し、それをQuitボタンのclicked ()シグナルに関連するトランジションのターゲットにする必要があります。s1
、s2
、s3
のそれぞれから遷移を追加することもできるが、これは冗長に思えるし、将来追加されるすべての新しい状態からこのような遷移を追加することを忘れてはならない。
状態s1
、s2
、s3
をグループ化することで、同じ動作(ステートマシンがどの状態にあるかにかかわらず、Quitボタンをクリックするとステートマシンが終了する)を実現できる。これは、新しいトップレベル状態を作成し、元の3つの状態を新しい状態の子にすることで実現します。次の図は、新しいステートマシンを示しています。
元の3つの状態は、s11
、s12
、s13
という名前に変更され、新しいトップレベル状態であるs1
の子状態になったことを反映している。子状態は親状態の遷移を暗黙的に継承する。つまり、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
のいずれかである)。
このような動作は、ヒストリー・ステートを使って簡単にモデル化できる。ヒストリー・ステート(QHistoryState オブジェクト)とは、親ステートが最後に終了したときの子ステートを表す擬似状態である。
ヒストリー状態は、現在の子状態を記録したい状態の子として作成される。ステートマシンが実行時にこのような状態の存在を検出すると、親状態が終了したときに、現在の(実際の)子状態が自動的に記録される。履歴状態への遷移は、実際には、ステートマシンが以前に保存していた子状態への遷移である。ステートマシンは、実子状態への遷移を自動的に「転送」する。
次の図は、割り込みメカニズムを追加した後のステートマシンを示しています。
この例では、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);
ステートの組み合わせによる爆発を避けるための並列ステートの使用
車の互いに排他的なプロパティのセットを、1つのステートマシンでモデル化したいとします。興味のある特性は、「きれい」対「汚い」、「動いている」対「動いていない」だとしよう。すべての可能な組み合わせを表現して自由に行き来するには、4つの互いに排他的な状態と8つの遷移が必要になる。
もし3つ目のプロパティ(例えば、赤対青)を追加すれば、状態の総数は2倍の8つになり、4つ目のプロパティ(例えば、密閉対転換可能)を追加すれば、状態の総数はまた2倍の16になる。
並列状態を使用すると、プロパティを追加しても、状態と遷移の総数は指数関数的ではなく、直線的に増加する。さらに、どの兄弟ステートにも影響を与えることなく、ステートをパラレル・ステートに追加したり、パラレル・ステートから削除したりすることができます。
並列状態グループを作成するには、QState コンストラクタにQState::ParallelStates を渡す。
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
の最終状態に入ると、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
にキャストし、文字列の比較を実行します。
以下は、カスタム・イベントとトランジションを使用したステートチャートです:
ステートチャートの実装はこんな感じだ:
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"));
関連するトランジションで処理されないイベントは、ステートマシンに黙って消費される。ステートをグループ化し、そのようなイベントのデフォルト処理を提供すると便利である:
深くネストされたステートチャートでは、最も適切な粒度レベルで、このような "フォールバック "遷移を追加できます。
復元ポリシーの使用によるプロパティの自動復元
ステートマシンの中には、ステートがアクティブでなくなったときにプロパティをリストアするのではなく、ステートにプロパティを割り当てることに注意を向けると便利なものがあります。マシンが、プロパティに明示的に値を与えていない状態に入ったときに、プロパティを常に初期値にリストアする必要があることがわかっている場合、グローバル・リストア・ポリシーを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になる。ステートは明示的にこの値を割り当てるからである。マシンが状態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
は、s2
とs3
の2つの子を持つ。s2
が入力されると、fooBar
プロパティは値 2.0 を持つ。これは、状態に対して明示的に定義されているからである。マシンが状態s3
にあるとき、状態に対する値は定義されていないが、s1
はプロパティを 1.0 と定義しているので、これがfooBar
に割り当てられる値である。
アニメーションとステート・マシン
ステート・マシンAPIは、アニメーション・フレームワークと接続し、プロパティがステートに割り当てられると自動的にアニメーション化する。
ステートマシンは、アニメーションを再生できる特別なステートを提供する。また、QState 、ステートに入ったり出たりするときにプロパティを設定することができます。この特別なアニメーション・ステートは、QPropertyAnimation 、これらの値の間を補間します。
1つ以上のアニメーションを、QSignalTransition またはQEventTransition クラスを使って、状態間の遷移に関連付けることができる。これらのクラスはどちらもQAbstractTransition から派生したもので、addAnimation() という便利な関数が定義されており、遷移が発生したときにトリガーされる1つ以上のアニメーションを追加することができます。
また、開始と終了の値を自分で設定するのではなく、プロパティとステートを関連付けることもできます。
例えば、次のようなコードがあるとしよう:
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
にあるとき、このプロパティはs2
によって割り当てられているので、マシンはプロパティfooBar
のデフォルトアニメーションを再生する。
遷移時に明示的に設定されたアニメーションは、指定されたプロパティのデフォルトアニメーションよりも優先されることに注意すること。
ステートマシンの入れ子
QStateMachine は、 のサブクラスです。これにより、ステートマシンを別のマシンの子ステートにすることができます。 は、 ()を再実装し、 ()を呼び出すことで、子ステートマシンが入力されると、自動的に実行を開始します。QState QStateMachine QState::onEntry QStateMachine::start
親ステートマシンは、ステートマシン・アルゴリズムにおいて、子マシンをアトミック状態として扱う。子ステートマシンは自己完結型であり、独自のイベントキューとコンフィギュレーションを保持する。特に、子マシンのconfiguration() は、親マシンの構成には含まれないことに注意すること(含まれるのは子マシン自身のみ)。
子ステートマシンの状態を、親ステートマシンの遷移のターゲットとして指定することはできません。逆に、親ステートマシンの状態を子ステートマシンの遷移のターゲットとして指定することはできません。子ステートマシンのfinished() シグナルを使用して、親マシンの遷移をトリガーすることができます。
Qt ステートマシンの概要と Qt ステートマシン QML ガイドも参照してください 。
本ドキュメントに含まれる文書の著作権は、それぞれの所有者に帰属します。 本書で提供されるドキュメントは、Free Software Foundation が発行したGNU Free Documentation License version 1.3に基づいてライセンスされています。 Qtおよびそれぞれのロゴは、フィンランドおよびその他の国におけるThe Qt Company Ltd.の 商標です。その他すべての商標は、それぞれの所有者に帰属します。