Qt 状态机框架学习

=版权声明:本文为CSDN博主「dbzhang800」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/dbzhang800/article/details/6408008

 

Qt状态机框架是基于状态图XML(SCXML) 实现的。从Qt4.6开始,它已经是QtCore模块的一部分。尽管它本身是蛮复杂的一套东西,但经过和Qt的事件系统(event system)信号槽(signals and slots)属性系统(property system)深度整合,它使用门槛并不高。

一些概念

Qt的手册中The State Machine Framework一文对Qt状态机框架及使用进行了介绍,可是还是发现看看基本的概念(详见  SCXML   的  第三部分 )更有帮助一点。

基本概念

最基本的概念是:

  • 状态(state)
  • 转换?过渡?(transition)
  • 事件(events)

每一个state包含一个transition的集合,这些transition定义了如何对events进行响应。events可以由状态机本身或外部实体产生。

在一个经典状态机中,状态机总是处于一个单一的state中。这个状态被称为active state。当event发生时,状态机将检查active state的包含的所有transition。如果它发现有一个和该event匹配,状态机将从当前的active state移动到该transition指定的state(称之为transtion的目标)。这样一来,目标state将成为新的active state。

在transition的过程中,状态机可以执行一些动作(action)。每一个状态都可以包含onentry onexit动作,transition本身也可以包含动作。当状态机通过一个名为T的transition从状态S1转换到S2,它先执行S1的 onexit 的动作,然后执行T本身包含的动作,最后执行S2 onentry的动作。

 

Compound States(复合状态)

一个state还可以嵌套其他的state。这样的state称为compound states(复合状态),我们称其为parent state,而被嵌套的则称为child state子state又可以嵌套其他的state,直到任意深度。不包含任何子state的state,称为atomic state(原子状态)

当一个child state处于 acitve时,它的parent active 也必须处于active状态。这样一来,在任何一点我们都将拥有一个包含原子state和它所有祖先的active state的集合。(在后面我们将看到多个原子state可以同时处于active状态)。

由于复合state的存在,transition将不再是从一个原子state到另一个原子state的转换,而是从一个active state集合到另一个集合的转换。如果transition的目标是一个原子state,那么状态机将不仅进入到该原子state,而且还将进入到它所有的为处于active的祖先state中。与此对应的是,transition的目标是一个组合state。在这种情况下,复合state的子state必须也被激活,由于transition并没有指定哪一个,这是需要active该复合state的initial state(初始状态)。如果该state依然是复合状态,将递归下去,直到原子 state。

一个复合state还可以包含final 和 history state作为其child state。

复合状态还会影响到transition的选择。当事件发生时,状态机从最深层嵌套的state(原子state)开始查找,如果未找到匹配的transition,则查找其parent state的transition,依次递归。如果状态机中所有transition均不匹配,事件被丢弃。

 

Parallel States

注:在SCXML中 Parallel States使用的是paralled 标签,前面提到的复合state和原子state都是用的state 标签。在Qt中,Paralled State和普通 state 是靠构造函数一个的参数进行区分的。

Parallel States(平行状态) 与前面介绍的复合 state 有很大的不同:当一个复合state处于active时,有且只有一个child state处于acitve;而parallel state处于active时,所有的child state都必须处于active状态。

当状态机进入平行state时,它也进入各个child state。各个child state可以采取不同的transition对event进行响应。

Executable Content

SCXML通过Executable Content(可执行内容?)提供了对数据(data model)进行修改和与外界实体进行交互的功能。

回到 Qt

这部分内容Qt Manual中给的太详细了,以至于都不知道该怎么向下写了。

基本概念

3个基本概念对照:

  • state-QAbstractState及其派生类
  • transition-QAbstractTransition及其派生类
  • event-Qt的信号和事件
  • data-Qt属性

注意:QStateMachine本身是QState的派生类。这使得状态机可以嵌套,见qstatemachines-a-state-too 。

-QObject
  -QAbstractState
    -QState
      -QStateMachine
    -QFinialState
    -QHistoryState
  -QAbstractTransition
    -QSignalTransition
    -QEventTransition
      -QKeyEventTransition
      -QMouseEventTransition
  -QEvent
    -QStateMachine::SignalEvent
    -QStateMachine::WrappedEvent

    

transition的触发

Qt的信号和事件都可以触发transition, 那么是如何实现的呢?

打开QAbstractTransition的Manual,可以看到两个protected的函数:

virtual bool eventTest ( QEvent * event ) = 0
virtual void onTransition ( QEvent * event ) = 0

前者用来判断事件是否匹配,后者用来执行一些动作。

两个具体类和对应的QEvent的派生类关系如下:

  • QSignalTransition-QStateMachine::SignalEvent
  • QEventTransition-QStateMachine::WrappedEvent

进入状态后,状态机将active state的对应的transition进行注册:

  • 对于signal类型的,将该信号连接到一个私有槽函数,槽函数中生成一个内部的QStateMachine::SignalEvent事件。
  • 对于event类型的,将直接在事件的对象安装事件过滤器,将事件拷贝并封装到一个内部的QStateMachine::WrappedEvent事件中
  • 区别:

    •   Qt的状态机运行在自己的事件循环中。对一个信号装换(QSignalTransition objects),当状态机接受一个signal信号时【即用户自定义的signal信号】,状态机自动会产生一个相应的信号事件即QStateMachine::SignalEvent给他自己;

    •   对于QObject的事件【mouseEvent(click/press/double click/release等)、keyEvent(键盘事件)等】过渡,则产生一个QStateMachine::WrappedEvent事件

对于自定义事件,可以使用QStateMachine::postEvent()进行派发。

posted on 2022-10-20 14:18  斗战胜佛美猴王  阅读(453)  评论(0编辑  收藏  举报