最近在学习GUI框架,发现GUI本质上就是一个大状态机。以EW为例,在每次loop的update之前,都会判断这次是否有input/signal/device/timer这四类会改变系统状态的外部变量(这些就是各种各样的condition)。如果有,再去执行对应的slot(也就是action),执行完再把各个对应的component的状态更改掉(或者是gui框架observer一个variable或者property, 当这个状态变化的时候,执行对用的function); 关于是先改变状态再执行action, 还是先执行action再改变状态,我个人认为,取决于这个具体的状态是否是一个持续性的动作。你比方说,通过GUI设置一个开关的打开,假如这个打开过程需要一段时间才能完成,这种情况就是先执行完action, 判断返回值成功后再修改状态。否则假如有另外一个线程在监控状态,它就会得到一个错误的信息。也就是有可能,monitor得到的是open, 结果实际上还没有打开,甚至过了一会,打开动作失败也是有可能的。那么有没有需要先改变状态,然后再执行action的情况呢,也是有的。
比如我前面说的对property的observer. 这个是一个什么机制呢?就是说,gui的loop在判断input/signal/device/timer, 比如有一个消息推送,machine内部的某个状态发生了变化,比如说电量,或者信号强度,这种变化需要反应到UI上绘制出来,那么就是在对应的控件上会有一个value记录当前值,一旦发现某个loop中,这个value发生变化,那么就执行对应的slot,发出提示或者重绘控件。这两种情况都是存在的,需要根据具体场景去具体分析。
说了不少,下面看一些具体的案例来学习其中的一些技巧。比如,汽车上面有一个安全带控制器,其工作场景是:如果有人坐在座椅上,但是固定时间内没有系好安全带,那么将打开蜂鸣器进行提示。该系统具有三个传感器输入,分别是座椅传感器,安全带传感器,定时器,以及一个输出蜂鸣器。
下面我们简单画一下这个场景的状态图:
状态是输入传感器组合的子集
状态图有了,下面就要开始编码了。这里面最关键的部分是对State的抽象,以及将各个状态子类化:
class CState {
public:
virtual void checkPara(CInput* input) {
if(!ptr) {return;}
}
virtual doProcessInput(CInput* input, CMachine *machine) {
checkPara(input);
processInput(input);
}
virtual void processInput(CInput* input) = 0;
virtual const char* type(void) = 0;
};
class CStateIdle : public CState {
void processInput(CInput* input, CMachine *machine) override {
switch(input->getType()) {
case SIG_SEATED: machine->startTimer(1000);
machine->setState(Seated_NoBelt_NoTimeout);
break;
default:
printf("do nothing in current state: %s\n", type());
}
}
const char* type(void) override {
return STR(StateIdle);
}
};
class CStateSeated_NoBelt_NoTimeout : public CState {
const char* type(void) override {
return STR(Seated_NoBelt_NoTimeout);
}
void processInput(CInput* input, CMachine *machine) override {
switch(input->getType()) {
case SIG_TIME_OUT: machine->openBeeper(1000);
machine->setState(Seated_NoBelt_Timeout);
break;
case SIG_SEATED_LEAVE:
machine->resetTimer();
machine->stopTimer();
default:
printf("do nothing in current state: %s\n", type());
break;
}
}
};
class CStateSeated_NoBelt_Timeout : public CState {
const char* type(void) override {
return STR(Seated_NoBelt_Timeout);
}
void processInput(CInput* input, CMachine *machine) override {
switch(input->getType()) {
case SIG_BELTED: machine->closeBeeper();
machine->resetTimer();
machine->setState(Seated_Belted);
break;
case SIG_SEATED_LEAVE:
machine->closeBeeper();
machine->resetTimer();
machine->stopTimer();
machine->setState(Idle);
default:
printf("do nothing in current state: %s\n", type());
break;
}
}
};
class CStateSeated_Belted : public CState {
const char* type(void) override {
return STR(Seated_Belted);
}
void processInput(CInput* input, CMachine *machine) override {
switch(input->getType()) {
case SIG_BELTED_LEAVE:
machine->openBeeper();
machine->setState(Seated_NoBelt_Timeout);
default:
printf("do nothing in current state: %s\n", type());
break;
}
}
};
class Machine {
public:
typedef enum {
StateIdle,
Seated_NoBelt_NoTimeout
Seated_NoBelt_Timeout,
Seated_Belted
} MachineState;
Machine() {
mIdle = new CStateIdle;
mNoBeltNoTimeout = new CStateSeated_NoBelt_NoTimeout;
mNoBeltTimeout = new CStateSeated_NoBelt_Timeout;
mSeatedBelted = new CStateSeated_Belted;
}
void openBeeper();
void closeBeeper();
void startTimer();
void stopTimer();
void resetTimer();
CInput* getInput(void);
void setState(MachineState state) {
switch(sig) {
case Idle: mState = mIdleState; break;
case Seated_NoBelt_NoTimeout: mState = mSeatedNoBeltNoTimeout ; break;
case Seated_NoBelt_Timeout: mState = mSeatedNoBeltTimeout ; break;
case Seated_Belted: mState = mSeatedBelted; break;
default: break;
}
}
private:
CState* mState;
CStateIdle *mIdle;
CStateSeated_NoBelt_NoTimeout* mNoBeltNoTimeout;
CStateSeated_NoBelt_Timeout *mNoBeltTimeout;
CStateSeated_Belted* mSeatedBelted;
};
在主循环中,不断收集事件,然后驱动这个状态机运行:
void machine_logic(Machine *machine)
{
while(1) {
CInput* input = machine->getInput();
machine_state->processInput(input);
}
}
input事件的收集一般放到另外一个单独的线程,这里简单示意一下
void machine_input(Machine *machine)
{
while(1) {
if(machine->isBelted() != machine->isBelted()) {
input->sendToQue();
}
}
}
以上