今回はシグナル遷移がどのようなものかを詳しく説明し、
シグナル遷移
シグナル遷移とは図1のように表すことができます。図で

シグナル遷移を表すためにはQSignalTransitionのインスタンスが用いられ、
プロパティ | データ型 | 説明 |
---|---|---|
senderObject | QObject* | シグナル発生源となるオブジェクト。 |
signal | QByteArray | 遷移を引き起すシグナル。 |
sourceState | QState* const | 遷移前の状態。読込み専用プロパティ。内部的には親オブジェクトとなる。 |
targetState | QAbstractState* | 遷移後の状態。 |
targetStates | QList<QAbstractState*> | 並列状態のための遷移後の状態リスト。 |
最初の4つのプロパティを図1と見比べてみると、
シグナル遷移の内部動作
ステートマシンフレームワーク内でのシグナル遷移の処理概要は以下のようになっています。
- シグナル遷移の対象となっているオブジェクトでシグナルが発生する。
- QStateMachineはイベント型がQEvent::StateMachineSignalのイベントQStateMachine::SignalEventを発生させ、
QSignalTransitionインスタンスに送る。 - QSignalTransition::eventTest()でイベントを受け取り、
遷移対象のシグナルかを判定する。戻り値がtrueならば状態が遷移し、 falseならば遷移しない。
前回の説明でも触れたように、
オブジェクトツリー構成
オブジェクトのメモリ管理のために、
シグナル遷移の設定パターン
前回の
1: #include <QApplication>
2: #include <QPushButton>
3: #include <QStateMachine>
4: #include <QSignalTransition>
5:
6: int main( int argc, char** argv )
7: {
8: QApplication app( argc, argv );
9:
10: QPushButton button;
11:
12: QStateMachine machine;
13: QState* beforeState = new QState( &machine );
14: beforeState->assignProperty( &button, "text", "Before" );
15: QState* afterState = new QState( &machine );
16: afterState->assignProperty( &button, "text", "After" );
17:
18: beforeState->addTransition( &button, SIGNAL(clicked()), afterState );
19:
20: button.show();
21:
22: machine.setInitialState( beforeState );
23: machine.start();
24:
25: return app.exec();
26: }
図2のようにボタンをクリックすると、

18: beforeState->addTransition( &button, SIGNAL(clicked()), afterState );
前回にも出て来たように、
QState* afterState2 = new QState( &machine );
afterState2->assignProperty( &button, "text", "After 2" );
QSignalTransition* transition = beforeState->addTransition( &button, SIGNAL(clicked()), afterState );
transition = beforeState->addTransition( &button, SIGNAL(pressed()), afterState2 );
もし上記のようなコードを書いたとすると、
次に、
QSignalTransition* signalTransition = new QSignalTransition( &button, SIGNAL(clicked()), beforeState );
signalTransition->setTargetState( afterState );
この設定パターンも前回示しました。このようにQSignalTransition のインスタンスを生成すると、
QSignalTransition* signalTransition = new QSignalTransition( beforeState );
signalTransition->setSenderObject( &button );
signalTransition->setSignal( SIGNAL(clicked()) );
signalTransition->setTargetState( afterState );
別の設定パターンです。遷移前の状態のみを指定してQSignalTransitionのインスタンスを生成した場合には、
QSignalTransition* signalTransition = new QSignalTransition;
signalTransition->setSenderObject( &button );
signalTransition->setSignal( SIGNAL(clicked()) );
signalTransition->setTargetState( afterState );
beforeState->addTransition( signalTransition );
最後の設定パターンです。パラメータを指定せずにQSignalTransitionのインスタンスを生成すると、
無条件遷移
シグナル遷移の設定パターンではありませんが、
beforeState->addTransition( afterState );
初期状態にしているbeforeStateの状態になると、
無条件遷移でも内部的に以下のクラスの遷移オブジェクトが生成されます。
class UnconditionalTransition : public QAbstractTransition
{
public:
UnconditionalTransition(QAbstractState *target)
: QAbstractTransition()
{ setTargetState(target); }
protected:
void onTransition(QEvent *) {}
bool eventTest(QEvent *) { return true; }
};
そして、
ここ迄のいろいろな設定パターンからわかるように、
シグナル遷移の独自拡張
無条件遷移でUnconditionalTransitionクラスと同じようにして、
QPushButtonがチェックされ、

1: #include <QApplication>
2: #include <QPushButton>
3: #include <QStateMachine>
4: #include <QSignalTransition>
5:
6: class ClickedTransition : public QSignalTransition
7: {
独自のシグナル遷移を作成するには、
8: public:
9: explicit ClickedTransition( QState* sourceState = 0 )
10: : QSignalTransition( 0, SIGNAL(clicked()), sourceState ) {}
11:
12: explicit ClickedTransition( QPushButton* button, QState* sourceState = 0 )
13: : QSignalTransition( button, SIGNAL(clicked()), sourceState ) {}
14:
clicked(bool) シグナルに限定して、
15: protected:
16: bool eventTest( QEvent* event ) {
17: if ( !QSignalTransition::eventTest( event ) )
18: return false;
19: QStateMachine::SignalEvent *signalEvent = static_cast<:signalevent>( event );
20: return signalEvent->arguments().at(0).toBool()
21: && signalEvent->sender()->property( "value" ).toInt() > 99;
22: }
メソッドeventTest()には、
シグナル情報 | 説明 |
---|---|
QList<QVariant> arguments() const | シグナルの引数。arguments().at(0)で最初の引数、 |
QObject* sender () const | シグナルを送信したオブジェクト。 |
int signalIndex () const | シグナルのインデックス。 |
シグナルclicked(bool)を用いているので、
signalEvent->arguments().at(0).toBool()
さらに詳しくシグナルの送信元オブジェクトを参照するには、
QPushButton* pushButton = qobject_cast<QPushButton*>( signalEvent->sender() );
さらにメタオブジェクト情報を辿れば、
QMetaMethod metaMethod = pushButton->metaObject()->method( signalEvent->signalIndex() );
たとえばmetaMethod.
23: };
24:
25: int main( int argc, char** argv )
26: {
27: QApplication app( argc, argv );
28:
29: QPushButton button;
30: button.setCheckable( true );
31: button.setChecked( true );
QPushButtonのチェックを有効にして、
32: button.setProperty( "value", 100 );
ダイナミックプロパティvalueに整数値100を設定して、
33:
34: QStateMachine machine;
35: QState* beforeState = new QState( &machine );
36: beforeState->assignProperty( &button, "text", "Before" );
37: QState* afterState = new QState( &machine );
38: afterState->assignProperty( &button, "text", "After" );
39:
40: ClickedTransition* clickedTransition = new ClickedTransition( &button, beforeState );
41: clickedTransition->setTargetState( afterState );
42:
状態を2つ用意して、
43: button.show();
44:
45: machine.setInitialState( beforeState );
46: machine.start();
47:
48: return app.exec();
49: }
再実装したQSignalTransition::eventTest()でいろいろな判定をして、
おわりに
次回以降では、