Qt中狀態(tài)機(jī)框架QState的實現(xiàn)
QState是Qt狀態(tài)機(jī)框架(Qt State Machine Framework)的核心類,用于建模離散狀態(tài)以及狀態(tài)間的轉(zhuǎn)換邏輯,廣泛應(yīng)用于UI交互流程、設(shè)備狀態(tài)管理、工作流控制等場景。它基于UML狀態(tài)圖規(guī)范設(shè)計,支持層次化狀態(tài)、并行狀態(tài)、歷史狀態(tài)等高級特性,能夠簡化復(fù)雜狀態(tài)邏輯的實現(xiàn)。
一、核心定位與繼承關(guān)系
QState繼承自QAbstractState(抽象狀態(tài)基類),而QAbstractState又繼承自QObject,因此QState具備Qt對象模型的所有特性(如信號槽、元對象系統(tǒng)、父子關(guān)系管理)。其核心作用是:
- 封裝一個離散狀態(tài)的l行為(進(jìn)入/退出時的動作);
- 管理狀態(tài)間的轉(zhuǎn)換規(guī)則(
QAbstractTransition); - 支持子狀態(tài)嵌套,構(gòu)建層次化狀態(tài)機(jī);
- 與
QStateMachine配合,實現(xiàn)狀態(tài)的自動切換。
狀態(tài)機(jī)的基本構(gòu)成包括:
- 狀態(tài)(State):如
QState、QParallelState(并行狀態(tài))、QHistoryState(歷史狀態(tài)); - 轉(zhuǎn)換(Transition):如
QSignalTransition(信號觸發(fā))、QEventTransition(事件觸發(fā)); - 事件(Event):觸發(fā)狀態(tài)轉(zhuǎn)換的信號、Qt事件或自定義事件;
- 狀態(tài)機(jī)(State Machine):
QStateMachine,負(fù)責(zé)調(diào)度狀態(tài)切換與事件處理。
二、基礎(chǔ)用法:狀態(tài)創(chuàng)建與轉(zhuǎn)換
1. 狀態(tài)的創(chuàng)建與添加
通過QStateMachine管理狀態(tài),需先創(chuàng)建狀態(tài)實例并添加到狀態(tài)機(jī)中:
#include <QStateMachine> #include <QState> #include <QPushButton> QStateMachine *machine = new QStateMachine; // 創(chuàng)建狀態(tài) QState *s1 = new QState(machine); // 直接指定父對象為狀態(tài)機(jī) QState *s2 = new QState; machine->addState(s2); // 或通過addState()添加 // 設(shè)置初始狀態(tài)(狀態(tài)機(jī)啟動時進(jìn)入的第一個狀態(tài)) machine->setInitialState(s1);
2. 狀態(tài)轉(zhuǎn)換的定義
狀態(tài)轉(zhuǎn)換(QAbstractTransition)是狀態(tài)切換的規(guī)則,需指定“觸發(fā)條件”和“目標(biāo)狀態(tài)”。QState通過addTransition()方法添加轉(zhuǎn)換,常用轉(zhuǎn)換類型包括:
(1)信號觸發(fā)轉(zhuǎn)換(QSignalTransition)
最常用的轉(zhuǎn)換類型,當(dāng)特定信號發(fā)射時觸發(fā)狀態(tài)切換:
QPushButton *btn = new QPushButton("Next");
// 當(dāng)btn發(fā)射clicked()信號時,從s1轉(zhuǎn)換到s2
s1->addTransition(btn, &QPushButton::clicked, s2);
// 進(jìn)階:轉(zhuǎn)換可攜帶動作(通過onTransition()信號)
QSignalTransition *trans = s1->addTransition(btn, &QPushButton::clicked, s2);
connect(trans, &QSignalTransition::onTransition, [](){
qDebug() << "從s1切換到s2"; // 轉(zhuǎn)換過程中執(zhí)行的動作
});
(2)事件觸發(fā)轉(zhuǎn)換(QEventTransition)
基于Qt事件(如QKeyEvent、QMouseEvent)觸發(fā)轉(zhuǎn)換:
#include <QEventTransition> // 當(dāng)s1收到QEvent::KeyPress事件時,轉(zhuǎn)換到s2 QEventTransition *keyTrans = new QEventTransition(btn, QEvent::KeyPress); keyTrans->setTargetState(s2); s1->addTransition(keyTrans);
(3)守衛(wèi)條件(Guard)
轉(zhuǎn)換可設(shè)置守衛(wèi)條件(布爾函數(shù)),僅當(dāng)條件為true時才執(zhí)行轉(zhuǎn)換:
// 定義守衛(wèi)函數(shù)(返回bool)
bool canTransition() {
return someCondition; // 例如:檢查輸入是否合法
}
// 為轉(zhuǎn)換設(shè)置守衛(wèi)
trans->setGuard(canTransition); // 僅當(dāng)canTransition()為true時,轉(zhuǎn)換才生效
三、狀態(tài)行為:進(jìn)入與退出動作
QState在進(jìn)入(entered)和退出(exited)時會發(fā)射對應(yīng)信號,可通過信號槽機(jī)制綁定狀態(tài)切換時的動作。此外,還可通過onEntry()和onExit()方法直接設(shè)置動作函數(shù)。
1. 信號綁定方式
// 進(jìn)入s1時執(zhí)行動作
connect(s1, &QState::entered, [](){
qDebug() << "進(jìn)入狀態(tài)s1";
// 例如:更新UI顯示、啟動定時器
});
// 退出s1時執(zhí)行動作
connect(s1, &QState::exited, [](){
qDebug() << "退出狀態(tài)s1";
// 例如:停止定時器、保存臨時數(shù)據(jù)
});
2. 動作函數(shù)方式
通過assignProperty()可在進(jìn)入狀態(tài)時自動為對象設(shè)置屬性,簡化UI狀態(tài)管理:
QPushButton *btn = new QPushButton("Click me");
// 進(jìn)入s1時,將btn的text屬性設(shè)為"狀態(tài)1",enabled設(shè)為true
s1->assignProperty(btn, "text", "狀態(tài)1");
s1->assignProperty(btn, "enabled", true);
// 進(jìn)入s2時,更新btn屬性
s2->assignProperty(btn, "text", "狀態(tài)2");
s2->assignProperty(btn, "enabled", false);
當(dāng)狀態(tài)激活時,assignProperty()設(shè)置的屬性會自動應(yīng)用到目標(biāo)對象,退出狀態(tài)時不會自動恢復(fù)(需手動在exited信號中處理)。
四、層次化狀態(tài):父狀態(tài)與子狀態(tài)
QState支持嵌套子狀態(tài),形成層次化結(jié)構(gòu)(父狀態(tài)包含子狀態(tài)),這是實現(xiàn)復(fù)雜狀態(tài)邏輯的核心特性。
1. 子狀態(tài)的添加與初始子狀態(tài)
// 創(chuàng)建父狀態(tài) QState *parentState = new QState; // 創(chuàng)建子狀態(tài)(指定父狀態(tài)) QState *child1 = new QState(parentState); QState *child2 = new QState(parentState); // 設(shè)置父狀態(tài)的初始子狀態(tài)(進(jìn)入父狀態(tài)時自動進(jìn)入該子狀態(tài)) parentState->setInitialState(child1);
2. 層次化狀態(tài)的行為規(guī)則
- 進(jìn)入父狀態(tài):先執(zhí)行父狀態(tài)的
entered動作,再進(jìn)入其初始子狀態(tài)(執(zhí)行子狀態(tài)的entered動作); - 退出父狀態(tài):先退出當(dāng)前活躍的子狀態(tài)(執(zhí)行子狀態(tài)的
exited動作),再執(zhí)行父狀態(tài)的exited動作; - 子狀態(tài)轉(zhuǎn)換限制:子狀態(tài)的轉(zhuǎn)換默認(rèn)只能在同一父狀態(tài)的子狀態(tài)間進(jìn)行,若需轉(zhuǎn)換到外部狀態(tài),需顯式指定目標(biāo)。
示例:播放器的“播放中”狀態(tài)(父狀態(tài))包含“正常播放”和“快進(jìn)”子狀態(tài):
QState *playing = new QState; // 父狀態(tài):播放中 QState *normalPlay = new QState(playing); // 子狀態(tài):正常播放 QState *fastForward = new QState(playing); // 子狀態(tài):快進(jìn) playing->setInitialState(normalPlay); // 子狀態(tài)間轉(zhuǎn)換:正常播放 → 快進(jìn)(按快進(jìn)鍵) normalPlay->addTransition(fastForwardBtn, &QPushButton::clicked, fastForward); // 子狀態(tài)轉(zhuǎn)換到外部狀態(tài):任何子狀態(tài)下按停止鍵 → 停止?fàn)顟B(tài) playing->addTransition(stopBtn, &QPushButton::clicked, stopped);
五、特殊狀態(tài)類型
1. 并行狀態(tài)(QParallelState)
用于建模同時活躍的多個狀態(tài)(如設(shè)備同時處于“聯(lián)網(wǎng)”和“充電”狀態(tài))。QParallelState是QState的子類,其所有子狀態(tài)會同時進(jìn)入和退出。
#include <QParallelState> QParallelState *parallel = new QParallelState; // 兩個并行子狀態(tài) QState *networkState = new QState(parallel); // 網(wǎng)絡(luò)狀態(tài) QState *powerState = new QState(parallel); // 電源狀態(tài) // 進(jìn)入parallel時,networkState和powerState同時激活 machine->setInitialState(parallel);
并行狀態(tài)的退出規(guī)則:所有子狀態(tài)退出后,并行狀態(tài)才會退出。
2. 歷史狀態(tài)(QHistoryState)
用于保存父狀態(tài)中最后活躍的子狀態(tài),當(dāng)父狀態(tài)再次進(jìn)入時,自動恢復(fù)到該子狀態(tài)(避免重復(fù)初始化)。分為兩種類型:
- 淺歷史(默認(rèn)):僅恢復(fù)直接子狀態(tài)的歷史;
- 深歷史:遞歸恢復(fù)所有嵌套子狀態(tài)的歷史(通過
setDeepHistory(true)啟用)。
#include <QHistoryState> QState *parent = new QState; QState *child1 = new QState(parent); QState *child2 = new QState(parent); parent->setInitialState(child1); // 創(chuàng)建歷史狀態(tài)(作為parent的子狀態(tài)) QHistoryState *history = new QHistoryState(parent); // 啟用深歷史(可選) history->setDeepHistory(true); // 從外部狀態(tài)轉(zhuǎn)換到history時,恢復(fù)parent的最后活躍子狀態(tài) externalState->addTransition(backBtn, &QPushButton::clicked, history);
六、狀態(tài)機(jī)的運行與生命周期
1. 狀態(tài)機(jī)的啟動與停止
// 啟動狀態(tài)機(jī)(開始處理事件并進(jìn)入初始狀態(tài)) machine->start(); // 停止?fàn)顟B(tài)機(jī)(退出當(dāng)前狀態(tài),暫停事件處理) machine->stop();
狀態(tài)機(jī)啟動后,會觸發(fā)初始狀態(tài)的entered信號,并開始監(jiān)聽事件以驅(qū)動轉(zhuǎn)換。
2. 狀態(tài)機(jī)的完成與終止
當(dāng)狀態(tài)機(jī)進(jìn)入“終止?fàn)顟B(tài)”(QFinalState)時,會發(fā)射finished()信號并停止運行:
#include <QFinalState> QFinalState *final = new QFinalState(machine); // 從s2轉(zhuǎn)換到終止?fàn)顟B(tài) s2->addTransition(quitBtn, &QPushButton::clicked, final); // 狀態(tài)機(jī)完成時退出程序 connect(machine, &QStateMachine::finished, qApp, &QApplication::quit);
七、高級特性與底層機(jī)制
1. 事件優(yōu)先級與處理順序
狀態(tài)機(jī)的事件處理遵循以下規(guī)則:
- 子狀態(tài)的事件處理器優(yōu)先于父狀態(tài);
- 同一狀態(tài)的多個轉(zhuǎn)換按添加順序檢查(守衛(wèi)條件先滿足者觸發(fā));
- 并行狀態(tài)的子狀態(tài)獨立處理事件,互不干擾。
2. 自定義轉(zhuǎn)換與事件
通過繼承QAbstractTransition可實現(xiàn)自定義轉(zhuǎn)換邏輯,通過QEvent子類可定義自定義事件:
// 自定義事件
class MyEvent : public QEvent {
public:
static const QEvent::Type Type = static_cast<QEvent::Type>(QEvent::User + 1);
MyEvent() : QEvent(Type) {}
};
// 自定義轉(zhuǎn)換(監(jiān)聽MyEvent)
class MyTransition : public QAbstractTransition {
protected:
bool eventTest(QEvent *e) override {
return e->type() == MyEvent::Type; // 僅響應(yīng)MyEvent
}
void onTransition(QEvent *) override {
// 轉(zhuǎn)換動作
}
};
// 使用自定義轉(zhuǎn)換
MyTransition *trans = new MyTransition;
trans->setTargetState(s2);
s1->addTransition(trans);
3. 調(diào)試與狀態(tài)監(jiān)控
Qt提供QStateMachine::setDebuggingEnabled(true)開啟調(diào)試日志,輸出狀態(tài)轉(zhuǎn)換過程:
machine->setDebuggingEnabled(true); // 控制臺會打印狀態(tài)切換日志
也可通過QState::active()方法實時檢查狀態(tài)是否活躍:
if (s1->active()) {
qDebug() << "當(dāng)前處于s1狀態(tài)";
}
八、常見問題
- 狀態(tài)轉(zhuǎn)換循環(huán):避免無守衛(wèi)條件的循環(huán)轉(zhuǎn)換(如
s1→s2→s1),可能導(dǎo)致狀態(tài)機(jī)無限切換; - 子狀態(tài)與父狀態(tài)的信號沖突:子狀態(tài)的
entered信號會在父狀態(tài)之后觸發(fā),需注意動作執(zhí)行順序; - 并行狀態(tài)的同步:并行子狀態(tài)的轉(zhuǎn)換獨立,若需同步退出,可統(tǒng)一轉(zhuǎn)換到同一個終止?fàn)顟B(tài);
- 歷史狀態(tài)的濫用:僅在需要恢復(fù)狀態(tài)時使用,過度使用會增加狀態(tài)機(jī)復(fù)雜度;
- 性能考量:復(fù)雜狀態(tài)機(jī)(>100個狀態(tài))需避免頻繁轉(zhuǎn)換,可通過合并狀態(tài)減少開銷。
QState作為Qt狀態(tài)機(jī)框架的核心,通過封裝狀態(tài)行為、轉(zhuǎn)換規(guī)則和層次化結(jié)構(gòu),大幅簡化了復(fù)雜狀態(tài)邏輯的實現(xiàn)。其特性包括:支持信號/事件觸發(fā)的轉(zhuǎn)換、狀態(tài)進(jìn)入/退出動作、屬性自動賦值、層次化與并行狀態(tài)、歷史狀態(tài)恢復(fù)等。
到此這篇關(guān)于Qt中狀態(tài)機(jī)框架QState的實現(xiàn)的文章就介紹到這了,更多相關(guān)Qt 狀態(tài)機(jī)框架QState內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言 fseek(f,0,SEEK_SET)函數(shù)案例詳解
這篇文章主要介紹了C語言 fseek(f,0,SEEK_SET)函數(shù)案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
C++實現(xiàn)數(shù)據(jù)保留小數(shù)點后兩位的常見方法
在計算機(jī)程序中,保留小數(shù)點后兩位通常需要使用特定的函數(shù)或方法來實現(xiàn),本文給大家介紹了C++實現(xiàn)數(shù)據(jù)保留小數(shù)點后兩位的常見方法,并通過代碼講解的非常詳細(xì),需要的朋友可以參考下2025-03-03
C語言素數(shù)(質(zhì)數(shù))判斷的3種方法舉例
這篇文章主要給大家介紹了關(guān)于C語言素數(shù)(質(zhì)數(shù))判斷的3種方法,質(zhì)數(shù)是只能被1或者自身整除的自然數(shù)(不包括1),稱為質(zhì)數(shù),文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2023-11-11

