【问题标题】:Emitting signals from class, if transition in QStateMachine was successful如果 QStateMachine 中的转换成功,则从类中发出信号
【发布时间】:2015-07-13 14:05:20
【问题描述】:

我的问题如下:我需要创建包含 QStateMachine 实例的类。这个类应该有插槽,您可以通过这些插槽“要求”状态机转换到另一个状态。如果转换成功,我的班级应该发出关于它的信号。我将如何实现这一点?类应该有能力根据特定的插槽调用发出特定的信号。 这是一个类的小例子:

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;
}

非常感谢您的帮助!

更新:
插槽代表不同种类的转换,因此外部类(将使用MyClass)可以“要求”一些转换。因此,插槽将事件或信号发送到状态机,它会查看事件或信号并(如果处于正确状态)进行此转换。我想用某个信号通知外部类,该信号在插槽(转换)成功之前询问。

【问题讨论】:

    标签: c++ qt state-machine qstatemachine


    【解决方案1】:
    1. 要转换槽调用,您需要以某种方式将槽绑定到QAbstractTransition。有两种方法:

      • 使用QEventTransition 并发送相关事件来触发它。

      • 使用QSignalTransition 并使用内部信号来触发它。

    2. 要在状态转换时发出信号,您可以将QAbstractTransition::triggeredQState::enteredQState::exited 信号连接到其他信号。请记住,在 Qt 中,连接目标可以是插槽或信号。

    因此,使用信号转换:

    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);
      }
    }
    

    使用事件转换有点困难,因为您使用的事件必须可由状态机克隆。核心模块的状态机只知道如何克隆 NoneTimer 事件 - 请参阅它的 cloneEvent 实现。

    小部件模块增加了对各种 GUI/小部件事件的支持 - 请参阅那里的 cloneEvent 实现。在紧要关头,您可以将此类 GUI 事件用于您自己的目的 - 毕竟,它们被发送到不会以特殊方式解释它们的普通 QObject

    您可以提供自己的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())
    

    【讨论】:

    • 这不是我的情况。我想在进行转换时触发信号,但不是在进入某种状态时。我已经更新了问题。
    • @NazariiPlebanskii 如果有多个转换导致一个状态,您也可以使用转换本身的triggered 信号。
    • 真的。那对我来说太棒了!谢谢。
    • @NazariiPlebanskii 啊,是的。嗯,那是因为愚蠢的状态机坚持复制所有事件,它不知道如何处理用户事件。所以它只是忽略了转换。您可以使用任何其他 Qt 事件类型。说 Enter 和 Leave 事件。
    • @NazariiPlebanskii 我添加了代码,展示了如何教状态机处理您的事件。
    【解决方案2】:

    我过去也遇到过同样的问题,我发现最简单的方法是用你自己的 QState 类继承 QState 并实现两个名为 QState::onEntry(QEvent * event) QState::onExit(QEvent *事件)

    这样,您可以在退出和进入新状态时发出任何您喜欢的信号。

    这是一个例子:

    文件 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;
    
        };
    

    还有文件 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));
    }
    

    【讨论】:

    • 这是不必要的。您可以将enteredexited 信号连接到Qt 4 中的其他信号,并使用QObject::sender() 来确定信号源的状态。在 Qt 5 中,您可以将信号连接到 lambda,后者会发出带有您想要的数据的信号。
    猜你喜欢
    • 1970-01-01
    • 2022-01-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-01
    • 1970-01-01
    • 2018-11-21
    • 2013-02-17
    相关资源
    最近更新 更多