4

Question

I am currently working on a application using Qt state machine framework, my objective is to associate a single transition with multiple source states. The rationale for doing it is the signal causing the transition is independent of the source state, hence instead of multiple transitions (one for each source state) I thought it would be better to have one. I am facing issues in acheiving the above mentioned through Qt.Details are demonstrated below with a test state machine. (Below mentioned is an hypothetical state machine but I can aslo give some real world examples for such a use case). Please suggest some effective ways for acheiving the mentioned objective.

Code snippet where the problem lies

m_s1.addTransition(&m_Trans); // Adding transition to state S1
m_s2.addTransition(&m_Trans); // Adding the same transition to state S2
                              // As per Qt docs, it seems the ownership of thr transition will be transferred to s2 which is what is causing the problem.

enter image description here

CState.hpp

class CState: public QState
{

public:

    /** Constructor */
    CState(std::string a_pStateName)
       :QState(nullptr),
        m_pStateName(a_pStateName)
    {

    }

    /** Overriding on entry */
    virtual void onEntry(QEvent *a_pEvent) Q_DECL_OVERRIDE
    {
        (void) a_pEvent;
        printf("State entry %s\n",m_pStateName.c_str());
    }

    ~CState() {}
     CState() {}
private:
    std::string  m_pStateName;
};

CTestMachine.hpp

class CTestStateMachine: public QObject
{
    Q_OBJECT

public:
    CTestStateMachine();
    ~CTestStateMachine() {};

private:
    QSignalTransition       m_Trans;
    CState                  m_s1;
    CState                  m_s2;
    CState                  m_s3;
    QStateMachine           m_TestMachine;
    QTimer                  m_Timer;

signals:
    void SourceIndependentSignal();

public slots:
    void TimetoInvokeTrans();


};

CTestMachine.cpp

#include <stdio.h>
#include <iostream>
#include <string>
using namespace std;

#include <QObject>
#include <QCoreApplication>
#include <QStateMachine>
#include <QState>
#include <QSignalTransition>
#include <QTimer>

#include "CState.hpp"
#include "CTestStateMachine.hpp"

void CTestStateMachine::TimetoInvokeTrans()
{
    printf("Emitting source independent signal\n");
    emit SourceIndependentSignal();
}

CTestStateMachine::CTestStateMachine():
    m_Trans(this, SIGNAL(SourceIndependentSignal())),
    m_s1("s1"),
    m_s2("s2"),
    m_s3("s3")
{
   /* Setup state machine */
   m_Trans.setTargetState(&m_s3);
   m_s1.addTransition(&m_Trans);
   m_s2.addTransition(&m_Trans);
   m_TestMachine.addState(&m_s1);
   m_TestMachine.addState(&m_s2);
   m_TestMachine.addState(&m_s3);
   m_TestMachine.setInitialState(&m_s1);
   m_TestMachine.start();
   printf("Started state machine\n");

   /* Trigger timer to make transitions */
   connect(&m_Timer, SIGNAL(timeout()), this, SLOT(TimetoInvokeTrans()));
   m_Timer.start(1000);
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    CTestStateMachine TestMachine;
    return a.exec();
}
Vivek Maran
  • 2,623
  • 5
  • 38
  • 52
  • Why don't use the other [overloads](http://doc.qt.io/qt-5/qstate.html#addTransition-1) of `addTransition`? – Rostislav Oct 18 '15 at 19:34
  • Because I would like to override `OnTransition` by subclassing `QSignalTransition`, – Vivek Maran Oct 18 '15 at 19:39
  • Well, given that `QAbstractTransition` seems to always have only one source state, I'd say you would have to find a different approach, such as passing some shared state to several instances of your `QSignalTransition` subclass. – Rostislav Oct 18 '15 at 19:45

1 Answers1

3

You can move the transition on state entry.

connect(m_state, &QState::entered, [m_state, m_tr]() -> void { m_state->addTransition(m_tr); });

Or just have a parent state keep the transition and set transition type to internal.

QState *s = new QState(m_stateMachine);
QState *s1 = new QState(s);
QState *s2 = new QState(s);
QState *s3 = new QState(s);
QSignalTransition *sTr = new QSignalTransition(sender, SIGNAL(foobar), s);
sTr->setTargetState(s3);
sTr->setTransitionType(QAbstractTransition::InternalTransition);