首先是简单的状态机使用。状态机里面包含不同的状态,状态之间可以切换。状态机的类是QStateMachine。 状态的类是QState。 在使用之前,需要在QT的pro配置文件中添加QT
先声明状态机和状态。
QStateMachine machine;
QState *stop;
QState *running;
QState *pause;
然后再实现:
stop = new QState;
running = new QState;
pause = new Qstate;
将状态添加到状态机:
machine.addState(stop);
machine.addState(running);
machine.addState(pause);
状态机里有多个状态,但不知道从哪里开始执行。我们需要指定一个初始的状态。
machine.setInitialState(stop);
状态之前需要切换,从一个状态变成另一个状态。我们需要告诉程序状态之间的切换关系,还有在什么状态下切换。
在按钮按下时,从stop切换到running状态
stop->addTransition(ui->changeStateButton, &QPushButton::clicked, running);
在按钮按下时,从running切换到pause状态
running->addTransition(ui->changeStateButton, &QPushButton::clicked, pause);
在按钮按下时,从pause切换到running状态
pause->addTransition(ui->changeStateButton, &QPushButton::clicked, running);
然后,我们告诉状态机开始执行
machine.start();
好了,简单状态机完成,在UI上添加标签来显示状态值,开始运行程序,点击按钮。
什么也没发生!因为状态切换时,没有伴随的效果,我们看不出状态发生了变化。下面我们为各个状态添加一些肉眼可见的行为。
这里将状态和标签的text属性绑定起来,标签会根据状态来变更属性的值(属性有多种,可以在对应控件的帮助文档里找到)。
stop->assignProperty(ui->stateLabel, "text", QString(tr("停止状态")));
running->assignProperty(ui->stateLabel, "text", QString(tr("运行状态")));
pause->assignProperty(ui->stateLabel, "text", QString(tr("暂停状态")));
运行如下:
状态切换关系如下图:
一个简单的状态机完成了。
代码如下:
头文件
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QStateMachine>
#include <QState>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
QStateMachine machine;
QState *stop;
QState *running;
QState *pause;
};
#endif // WIDGET_H
源文件
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
stop = new QState;
running = new QState;
pause = new QState;
machine.addState(stop);
machine.addState(running);
machine.addState(pause);
machine.setInitialState(stop);
stop->addTransition(ui->changeStateButton, &QPushButton::clicked, running);
running->addTransition(ui->changeStateButton, &QPushButton::clicked, pause);
pause->addTransition(ui->changeStateButton, &QPushButton::clicked, running);
machine.start();
stop->assignProperty(ui->stateLabel, "text", QString(tr("停止状态")));
running->assignProperty(ui->stateLabel, "text", QString(tr("运行状态")));
pause->assignProperty(ui->stateLabel, "text", QString(tr("暂停状态")));
}
Widget::~Widget()
{
delete ui;
}
继续完善,状态切换可以有不同的操作出发,我们再增加一个状态复位按钮,让状态回到停止状态。
增加如下代码,注意第二个参数也可以采用这种格式。将运行或暂停状态切换回停止状态。
running->addTransition(ui->resumeButton, SIGNAL(clicked()), stop);
pause->addTransition(ui->resumeButton, SIGNAL(clicked()), stop);
假如这个状态切换伴随着其他操作比如歌曲的播放和暂停,那在按钮按下时,需要对应的操作。对于按钮来说,都是click状态,怎么知道是谁切换到谁呢?可以通过判断标签的属性来确定,但更好的办法是不同的状态切换时直接触发对应的处理。
将stop的addTransition改造一下。 addTransition添加的参数可以是QSignalTransition类。QSignalTransition在初始化时,设置上关联的控件和控件动作。然后通过 setTargetState设置成将要变成的状态,通过triggered信号,来绑定触发时对应的行为(这里添加了一个槽函数)。再用stop状态添加新创建的QSignalTransition,完成状态切换的设置。
这样在状态切换的同时,还会调用对应的处理函数。
QSignalTransition *stopTorunning = new QSignalTransition(ui->changeStateButton, &QPushButton::clicked);
stopTorunning->setTargetState(running);
connect(stopTorunning, SIGNAL(triggered()), this, SLOT(stopToRunningSlot()));
stop->addTransition(stopTorunning);
为了直观的显示调用,将UI修改如下:
槽函数的实现如下:
void Widget::stopToRunningSlot()
{
ui->textBrowser->append(QString("停止到运行"));
}
运行一下程序
将其他转换设置也作类似更改,得到如下程序:
代码如下:
头文件
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <QStateMachine>
#include <QState>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private slots:
void stopToRunningSlot();
void runningToPauseSlot();
void pauseToRunningSlot();
void runningToStopSlot();
void pauseToStopSlot();
private:
Ui::Widget *ui;
QStateMachine machine;
QState *stop;
QState *running;
QState *pause;
};
#endif // WIDGET_H
源文件
#include "widget.h"
#include "ui_widget.h"
#include <QSignalTransition>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
stop = new QState;
running = new QState;
pause = new QState;
machine.addState(stop);
machine.addState(running);
machine.addState(pause);
machine.setInitialState(stop);
//stop->addTransition(ui->changeStateButton, &QPushButton::clicked, running);
QSignalTransition *stopTorunning = new QSignalTransition(ui->changeStateButton, &QPushButton::clicked);
stopTorunning->setTargetState(running);
connect(stopTorunning, SIGNAL(triggered()), this, SLOT(stopToRunningSlot()));
stop->addTransition(stopTorunning);
//running->addTransition(ui->changeStateButton, &QPushButton::clicked, pause);
QSignalTransition *runningTopause = new QSignalTransition(ui->changeStateButton, &QPushButton::clicked);
runningTopause->setTargetState(pause);
connect(runningTopause, SIGNAL(triggered()), this, SLOT(runningToPauseSlot()));
running->addTransition(runningTopause);
//pause->addTransition(ui->changeStateButton, &QPushButton::clicked, running);
QSignalTransition *pauseTorunning = new QSignalTransition(ui->changeStateButton, &QPushButton::clicked);
pauseTorunning->setTargetState(running);
connect(pauseTorunning, SIGNAL(triggered()), this, SLOT(pauseToRunningSlot()));
pause->addTransition(pauseTorunning);
//running->addTransition(ui->resumeButton, SIGNAL(clicked()), stop);
QSignalTransition *runningTostop = new QSignalTransition(ui->resumeButton, &QPushButton::clicked);
runningTostop->setTargetState(stop);
connect(runningTostop, SIGNAL(triggered()), this, SLOT(runningToStopSlot()));
running->addTransition(runningTostop);
//pause->addTransition(ui->resumeButton, SIGNAL(clicked()), stop);
QSignalTransition *pauseTostop = new QSignalTransition(ui->resumeButton, &QPushButton::clicked);
pauseTostop->setTargetState(stop);
connect(pauseTostop, SIGNAL(triggered()), this, SLOT(pauseToStopSlot()));
pause->addTransition(pauseTostop);
machine.start();
stop->assignProperty(ui->stateLabel, "text", QString(tr("停止状态")));
running->assignProperty(ui->stateLabel, "text", QString(tr("运行状态")));
pause->assignProperty(ui->stateLabel, "text", QString(tr("暂停状态")));
}
Widget::~Widget()
{
delete ui;
}
void Widget::stopToRunningSlot()
{
ui->textBrowser->append(QString("停止到运行"));
}
void Widget::runningToPauseSlot()
{
ui->textBrowser->append(QString("运行到暂停"));
}
void Widget::pauseToRunningSlot()
{
ui->textBrowser->append(QString("暂停到运行"));
}
void Widget::runningToStopSlot()
{
ui->textBrowser->append(QString("运行到停止"));
}
void Widget::pauseToStopSlot()
{
ui->textBrowser->append(QString("暂停到停止"));
}