開発環境の準備
Qtの状態マシン機能を利用するには、プロジェクトファイル(.pro)に以下のモジュールを追加する。
QT += statemachineヘッダファイルでは、必要に応じて以下をインクルードする。
#include <QStateMachine>
#include <QState>
#include <QFinalState>
#include <QSignalTransition>状態マシンの構成要素
QStateの階層構造
Qtの状態マシンは階層型有限状態機として実装されている。QAbstractStateを基底とし、QStateが基本クラスとなる。子状態を持つ親状態を構成することで、複雑な状態遷移を整理できる。
状態の主な特性:
addTransition()で遷移条件を設定onEntry/onExitでライフサイクルイベントを捕捉- 親状態に所属する状態マシンを
machine()で取得
遷移の実装パターン
遷移設定には2通りの方法がある。
// パターンA: シグナル駆動での簡易設定
sourceState->addTransition(emitter, SIGNAL(signalName()), targetState);
// パターンB: 遷移オブジェクトの明示的構築
auto *tx = new QSignalTransition(someButton, &QPushButton::clicked);
tx->setTargetState(anotherState);
currentState->addTransition(tx);カスタム遷移クラス
複雑な判定ロジックが必要な場合、QAbstractTransitionを継承して実装する。
class ConditionalTransition : public QAbstractTransition
{
Q_OBJECT
public:
using GuardFunc = std::function<bool(QEvent*)>;
using ActionFunc = std::function<void(QEvent*)>;
explicit ConditionalTransition(GuardFunc guard, ActionFunc action,
QState *target = nullptr)
: guard_(std::move(guard)), action_(std::move(action))
{
if (target) setTargetState(target);
}
protected:
bool eventTest(QEvent *ev) override
{
return guard_ ? guard_(ev) : true;
}
void onTransition(QEvent *ev) override
{
if (action_) action_(ev);
}
private:
GuardFunc guard_;
ActionFunc action_;
};ログインシステムの実装
状態設計
以下の4状態を定義する:
| 状態名 | 役割 |
|---|---|
| Idle | 初期状態、入力待ち |
| Authenticating | サーバ通信中 |
| Authenticated | 認証成功(終了状態) |
| Failed | 認証失敗、再入力へ |
実装コード
class LoginController : public QObject
{
Q_OBJECT
public:
explicit LoginController(QObject *parent = nullptr);
void initiate();
// 外部からトリガーされる操作
void submitCredentials(const QString &user, const QString &pass);
void cancelOperation();
signals:
void credentialSubmitted();
void authSuccess();
void authRejected();
void resetRequested();
private:
void setupStateGraph();
void performRemoteAuth();
QStateMachine *engine_;
QString pendingUser_;
QString pendingPass_;
};
LoginController::LoginController(QObject *parent)
: QObject(parent)
, engine_(new QStateMachine(this))
{
setupStateGraph();
}
void LoginController::setupStateGraph()
{
// 状態定義
auto *idle = new QState(engine_);
auto *verifying = new QState(engine_);
auto *success = new QFinalState(engine_);
auto *rejected = new QState(engine_);
// プロパティ設定によるUI連動
idle->assignProperty(this, "status", QString("入力待ち"));
verifying->assignProperty(this, "status", QString("認証中..."));
rejected->assignProperty(this, "status", QString("認証失敗"));
// 遷移定義
idle->addTransition(this, &LoginController::credentialSubmitted, verifying);
verifying->addTransition(this, &LoginController::authSuccess, success);
verifying->addTransition(this, &LoginController::authRejected, rejected);
rejected->addTransition(this, &LoginController::resetRequested, idle);
// エントリ/エグジット処理
connect(verifying, &QState::entered, this, &LoginController::performRemoteAuth);
// 初期状態設定
engine_->setInitialState(idle);
}
void LoginController::submitCredentials(const QString &user, const QString &pass)
{
pendingUser_ = user;
pendingPass_ = pass;
emit credentialSubmitted();
}
void LoginController::performRemoteAuth()
{
// 非同期処理を模擬
QTimer::singleShot(1500, this, [this]() {
bool valid = (pendingUser_ == "admin" && pendingPass_ == "secret");
valid ? emit authSuccess() : emit authRejected();
});
}エラーハンドリングの拡張
ネットワーク異常時のリトライ対応を追加する例:
class RetryableTransition : public QAbstractTransition
{
public:
explicit RetryableTransition(int maxAttempts, QState *fallback)
: maxRetry_(maxAttempts), fallbackState_(fallback), attemptCount_(0) {}
protected:
bool eventTest(QEvent *) override
{
return attemptCount_ < maxRetry_;
}
void onTransition(QEvent *) override
{
++attemptCount_;
}
private:
int maxRetry_;
QState *fallbackState_;
int attemptCount_;
};
// 使用例:verifying状態からの分岐
auto *retryTx = new RetryableTransition(3, idle);
retryTx->setTargetState(verifying); // 再試行
verifying->addTransition(retryTx);状態マシンの起動と制御
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
LoginController controller;
// UI構築(省略)
QWidget window;
QPushButton *btnLogin = new QPushButton("ログイン", &window);
QObject::connect(btnLogin, &QPushButton::clicked, [&]() {
controller.submitCredentials("admin", "secret");
});
controller.initiate(); // 状態マシン開始
return app.exec();
}QStateMachine::start() を呼び出すと、initialState から評価が開始される。停止時は stop() を呼び出し、必要に応じて deleteLater() で資源を解放する。