简体   繁体   English

如果QStateMachine中的转换成功,则从类发出信号

[英]Emitting signals from class, if transition in QStateMachine was successful

My problem is the following: I need to create class, which contains QStateMachine instance. 我的问题如下:我需要创建包含QStateMachine实例的类。 This class should have slots through which you could "ask" state machine to make transition to another state. 此类应具有插槽,您可以通过这些插槽“询问”状态机以转换到另一个状态。 And if transition was successful, my class should emit signal about it. 如果转换成功,我的班级应该发出有关它的信号。 How would I implement this? 我将如何实施呢? Class should have ability to emit certain signals according to certain slot invoke. 类应具有根据特定插槽调用发出特定信号的能力。 Here is a small example of class: 这是一个小类的例子:

class MyClass : public QObject
{
    Q_OBJECT
public:
    explicit MyClass(QObject *parent = 0)
    {
        mStateMachine = new QStateMachine(this);
        QState *s1 = new QState(mStateMachine);
        QState *s2 = new QState(mStateMachine);
        QState *s3 = new QState(mStateMachine);

        s1->addTransition(); // Transition to s2
        s2->addTransition(); // Transition to s3
        s3->addTransition(); // Transition to s1

        mStateMachine->setInitialState(s1);
        mStateMachine->start();
    }

signals:
    toS1();
    toS2();
    toS3();

public slots:
    slotToS1()
    {
        /* post event to state machine about
        transition to state s1,
        if transition was successful,
        then emit toS1() signal. */
    };
    slotToS2(){ /* Similar to slotToS1 */};
    slotToS3(){ /* Similar to slotToS1 */};
private:
    QStateMachine *mStateMachine;
}

I would be very grateful for your help! 我将非常感谢您的帮助!

UPD: UPD:
The slots are representing defferent kinds of transitions, so that outer class (that will be using MyClass ) could 'ask' for some transition. 插槽代表不同类型的转换,因此外部类(将使用MyClass )可以“询问”某些转换。 So, the slot send event or signal to state machine, it looks on event or signal and (if in right state) makes this transition. 因此,插槽将事件或信号发送到状态机,它查看事件或信号并(如果处于正确状态)进行此转换。 And I want to notify outer class with certain signal, that asked before slot (transition) was made successfuly. 我想用某些信号通知外部类,该信号在插槽(转换)成功完成之前被询问。

  1. To transition on a slot call, you need to somehow bind the slot to a QAbstractTransition . 要转换插槽调用,您需要以某种方式将插槽绑定到QAbstractTransition There are two ways of doing it: 有两种方法:

    • Use a QEventTransition and send a relevant event to trigger it. 使用QEventTransition并发送相关事件以触发它。

    • Use a QSignalTransition and use an internal signal to trigger it. 使用QSignalTransition并使用内部信号来触发它。

  2. To emit signals on state transitions, you can connect the QAbstractTransition::triggered or QState::entered or QState::exited signals to other signals . 要在状态转换时发出信号,可以将QAbstractTransition::triggeredQState::enteredQState::exited信号连接到其他信号 Remember, in Qt a connection target can be either a slot or a signal. 请记住,在Qt中,连接目标可以是插槽或信号。

Thus, using signal transitions: 因此,使用信号转换:

class MyClass : public QObject
{
  Q_OBJECT
  QStateMachine machine;
  QState s1, s2;
  Q_SIGNAL void s_go_s1_s2();
  Q_SIGNAL void s_go_s2_s1();
public:
  Q_SIGNAL void transitioned_s1_s2();
  Q_SIGNAL void transitioned_s2_s1();
  Q_SLOT void go_s2_s1() { emit s_go_s2_s1(); }
  Q_SLOT void go_s1_s2() { emit s_go_s1_s2(); }
  explicit MyClass(QObject *parent = 0) : QObject(parent),
    s1(&machine), s2(&machine) {
    auto s1_s2 = s1.addTransition(this, SIGNAL(s_go_s1_s2()), &s2);
    auto s2_s1 = s2.addTransition(this, SIGNAL(s_go_s2_s1()), &s1);
    machine.setInitialState(&s1);
    machine.start();
    connect(s1_s2, &QAbstractTransition::triggered, this, &MyClass:: transitioned_s1_s2);
    connect(s2_s1, &QAbstractTransition::triggered, this, &MyClass:: transitioned_s2_s1);
  }
}

Using event transitions is a bit harder, since the events you're using must be cloneable by the state machine. 使用事件转换要困难一些,因为您正在使用的事件必须可由状态机克隆。 The core module's state machine only knows how to clone the None and Timer events - see its cloneEvent implementation. 核心模块的状态机仅知道如何克隆NoneTimer事件-请参见其cloneEvent实现。

The widgets module adds support for various GUI/Widgets events - see the cloneEvent implementation there. 小部件模块增加了对各种GUI /小部件事件的支持-请参阅cloneEvent实现。 You could, in a pinch, use such GUI events for your own purposes - after all, they are sent to a plain QObject that doesn't interpret them in a special way. 您可能会出于某种目的而临时使用此类GUI事件-毕竟,它们会被发送到一个普通的QObject ,该对象不会以特殊方式对其进行解释。

You can provide your own cloneEvent implementation that links with the others. 您可以提供与其他链接的自己的cloneEvent实现。

#include <private/qstatemachine_p.h>

class MyClass : public QObject
{
  Q_OBJECT
  QStateMachine machine;
  QState s1, s2;
  QEvent e_s1_s2, e_s2_s1;
  QEventTransition s1_s2, s2_s1;
public:
  Q_SIGNAL void transitioned_s1_s2();
  Q_SIGNAL void transitioned_s2_s1();
  Q_SLOT void go_s2_s1() { QCoreApplication::sendEvent(this, &e_s2_s1); }
  Q_SLOT void go_s1_s2() { QCoreApplication::sendEvent(this, &e_s1_s2); }
  explicit MyClass(QObject *parent = 0) : QObject(parent),
    s1(&machine), s2(&machine),
    e_s1_s2((QEvent::Type)(QEvent::User + 1)),
    e_s2_s1((QEvent::Type)(QEvent::User + 2)),
    s1_s2(this, e_s1_s2.type()),
    s2_s1(this, e_s2_s1.type()) {
    s1_s2.setTargetState(&s2);
    s2_s1.setTargetState(&s1);
    s1.addTransition(&s1_s2);
    s2.addTransition(&s2_s1);
    machine.setInitialState(&s1);
    machine.start();
    connect(&s1_s2, &QAbstractTransition::triggered, this, &MyClass::transitioned_s1_s2);
    connect(&s2_s1, &QAbstractTransition::triggered, this, &MyClass::transitioned_s2_s1);
  }
}

static const QStateMachinePrivate::Handler * last_handler = 0;

static QEvent * cloneEvent(QEvent * e) {
  if (e->type() >= QEvent::User && e->type() < QEvent::User+100) {
    return new QEvent(e->type());
  return last_handler->cloneEvent(e);
}

const QStateMachinePrivate::Handler our_handler = {
    cloneEvent
};

void registerHandler() {
  last_handler = QStateMachinePrivate::handler;
  QStateMachinePrivate::handler = &our_handler;
}
Q_CONSTRUCTOR_FUNCTION(registerHandler())

void unregisterHandler() {
  QStateMachinePrivate::handler = last_handler;

}
Q_DESTRUCTOR_FUNCTION(unregisterHandler())

I have had the same problem in the past and I have found the easiest way was to inherit fom QState with your own QState class and implement 2 methods called QState::onEntry(QEvent * event) QState::onExit(QEvent * event) 过去我遇到过同样的问题,我发现最简单的方法是用自己的QState类继承fom QState并实现2个称为QState :: onEntry(QEvent * event)的方法QState :: onExit(QEvent * event)

This way you are able to emit any signal you like when you exit and when you enter a new state. 这样,当您退出或进入新状态时,您就可以发出任何喜欢的信号。

Here is and example: 这是示例:

file mystate.h 文件mystate.h

#include <QState>

    class MyState : public QState
    {
        Q_OBJECT
    public:
        explicit MyState(qint32 stateId, QState * parent = 0);

    protected:
        void onEntry(QEvent * event);
        void onExit(QEvent * event);

    signals:
        void exit(qint32 stateId);
        void enter(qint32 stateId);

    private:

        qint32 stateId;

    };

And file mystate.cpp 并文件mystate.cpp

#include "mystate.h"


MyState::MyState(qint32 stateId, QState *parent)
{
    this->stateId = stateId;
}

void MyState::onEntry(QEvent *event)
{
    emit enter(stateId);
}

void MyState::onExit(QEvent *event)
{
    emit (exit(stateId));
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM