概念
状态机框架提供了一些类来创建和执行(状态图state graphs) ,状态图为一个系统如何对外界激励进行反应提供了一个图形化模型,该模型是通过定义一些系统可能进入的状态以及系统怎样从一个状态切换到另一个状态来实现的。
事件驱动的系统(比如Qt应用程序)的一个关键特性就是**它的行为不仅仅依赖于最后一个或者当前的事件,而且也依赖于将要执行的事件**。
通过使用状态图,这些信息会非常容易地表达。
状态机框架提供了一个API和一个执行模型来有效地将状态图的元素和语义嵌人到Qt应用程序中。
该框架与Qt的元对象系统是紧密结合的,例如,状态间的切换可以由信号来触发。
Qt的事件系统用来驱动状态机。
状态机框架中的状态图是分层的,状态可以嵌套在其他状态中,状态机一个有效配置中的所有状态都拥有一个共同的祖先。
状态机框架在Qt 4.6中被引入。本节内容可以参考The State Machine Frame-work关键字。
创建状态机
下面先来看一个最简单的应用:
假定状态机由一个QPushButton控制,
包含3个状态:即s1、s2和 s3,其中,s1是初始状态。当单击按钮时,状态机切换到另一个状态。
图11-7是该状态机的状态图:

下面通过代码实现:
新建空的Qt项目,名称为mystatemachine,完成后先在项目文件中添加“QT+= widgets”一行代码并保存该文件,然后添加新的 main.cpp文件,并更改其中内容为:
#include <QApplication>
#include <QPushButton>
#include <QState>
#include <QStateMachine>
int main(int argc, char* argv[ ])
{
QApplication app(argc, argv);
//创建一个开始按钮
QPushButton button("State Machine");
// 创建状态机和三个状态,并将三个状态添加到状态机中
QStateMachine machine;
QState *s1 = new QState(&machine);
QState *s2 = new QState(&machine);
QState *s3 = new QState(&machine);
// 为按钮部件的geometry属性分配一个值,当进入该状态时会设置该值
s1->assignProperty(&button, "geometry", QRect(100, 100, 120, 50));
s2->assignProperty(&button, "geometry", QRect(300, 100, 120, 50));
s3->assignProperty(&button, "geometry", QRect(200, 200, 120, 50));
// 使用按钮部件的单击信号来完成三个状态的切换
//按下是
//按下s1到s2,
//按下s2到s3
//按下s3到s1
QSignalTransition *transition1 = s1->addTransition(&button,SIGNAL()),s2);
QSignalTransition *transition1 = s1->addTransition(&button,SIGNAL()),s3);
QSignalTransition *transition1 = s1->addTransition(&button,SIGNAL()),s1);
// 设置状态机的初始状态并启动状态机
machine.setInitialState(s1);
machine.start();
button.show();
return app.exec();
}
要使用一个状态机,则需要先创建该状态机和使用到的状态,可以像这里在创建状态时直接将其添加到状态机中,也可以使用QStateMachine::addState()来添加状态。
创建完状态后要使用assignProperty()函数为QObject对象的属性分配值。
该状态时就可以为QObject对象的这个属性设置该值。然后要使用addTransition()函数来完成一个状态到另一个状态的切换,可以关联QObject对象的一个信号来触发切换。
最后要为状态机设置初始状态并启动状态机,这样当状态机启动时就会自动进入初始状态。
状态机是 异步执行 (各语句执行结束的顺序与语句执行开始的顺序并不一定相同)
它会成为应用程序事件循环的一部分
现在可以运行程序,然后单击按钮,查看状态机的运行效果。

当状态机进人一个状态时会发射QState:: entered()信号,而退出一个状态时会发射QState::exited()信号。
可以关联这两个信号来完成一些操作:
例如,在进入s3状态时将按钮最小化,那么可以在程序中调用setInitialState()函数,并在代码前添加如下代码:
QObject::connect(s3, &QState::entered, &button, &QPushButton::showMinimized);//按下最小化
在状态机中使用动画
如果将状态机API和Qt中的动画API相关联,那么就可以使分配到状态上的属性自动实现动画效果。
在前面的程序中先添加头文件:
#include <QSignalTransition>
#include <QPropertyAnimation>
然后进行状态切换的代码更改如下:
// 使用按钮部件的单击信号来完成三个状态的切换
QSignalTransition *transition1 = s1->addTransition(&button,&QPushButton::clicked, s2);
QSignalTransition *transition1 = s1->addTransition(&button,&QPushButton::clicked, s3);
QSignalTransition *transition1 = s1->addTransition(&button,&QPushButton::clicked, s1);
//设置按钮动画效果
QPropertyAnimation *animation = new QPropertyAnimation(&button, "geometry");
transition1->addAnimation(animation);
transition2->addAnimation(animation);
transition3->addAnimation(animation);
这样就可以在状态切换时使用动画效果了。

在属性上添加动画,就意味着进入一个状态时分配的属性将无法立即生效,而是在进入时开始播放动画,然后以平滑的动画来达到属性分配的值。
这里无须为动画设置开始和结束的值,它们会被隐含地进行设置,开始值就是开始播放动画时属性的当前值,结束值就是状态分配的属性的值。
1.默认动画
如果想对一个属性指定一个动画,从而使所有的切换都默认使用这个动画,那么可以在状态机中使用默认动画。
例如,可以将前面程序中3个调用addAnimation()函数的代码使用下面一行代码来代替:
machine.addDefaultAnimation(animation);
注意,如果一个属性明确指定了动画,那么它就会优先于该属性的任何默认动画。
2. 检测状态中的所有属性都已经被设置
首先来看代码:
QMessageBox * messageBox =new QMessageBox(mainWindow);
messageBox->addButton(QMessageBox::Ok);
messageBox->setText(

最低0.47元/天 解锁文章
661

被折叠的 条评论
为什么被折叠?



