Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Help with State Machine



  • I am trying to make console application which prints the state it is in. If you guys could have a look at my code tell me if I am going in the right direction and if not why ?

    ps: I have just started learning Qt . I have a very few knowledge and skills , also I have almost no skill in C++

    main.cpp

    #include <QCoreApplication>
    //#include <QTimer>
    #include "test.h"
    
    int main(int argc, char *argv[])
    {
        QCoreApplication a(argc, argv);
    
        Test test;
    
        QObject::connect(&test, &Test::stop, &a, &QCoreApplication::quit);
    //    QTimer::singleShot(0, &test, &Test::Run);
    
        return a.exec();
    }
    

    test.h

    #ifndef TEST_H
    #define TEST_H
    
    #include <QObject>
    #include <QStateMachine>
    
    class Test : public QState
    {
        Q_OBJECT
    public:
        explicit Test(QObject *parent = 0);
    
    signals:
        void start();
    //    void finished();
        void stop();
    private:
        void buildStateMachine();
        QStateMachine machine;
    
    public slots:
        void Run();
    //    void runI1();
    //    void runS1();
    //    void runS2();
    //    void runS3();
    //    void debugSlot();
    //};
    
    #endif // TEST_H
    

    statetransition.h

    #ifndef STRINGTRANSITION_H
    #define STRINGTRANSITION_H
    #include<QStateMachine>
    #include<QString>
    
    class StringTransition : public QAbstractTransition
    {
    public:
        StringTransition(const QString &value)
            : m_value(value) {}
    
    protected:
        virtual bool eventTest(QEvent *e) const
        {
            if (e->type() != QEvent::Type(QEvent::User+1)) // StringEvent
                return false;
            StringEvent *se = static_cast<StringEvent*>(e);
            return (m_value == se->value);
        }
    
        virtual void onTransition(QEvent *) {}
    
    private:
        QString m_value;
    };
    
    #endif // STRINGTRANSITION_H
    
    

    state.cpp

    #include "test.h"
    #include <iostream>
    #include <QTextStream>
    #include <stringtransition.h>
    #include<QStateMachine>
    
    Test::Test(QObject * parent) : QObject(parent)
    {
        buildStateMachine();
    }
    
    
    
    void Test::Run()
    {
        QTextStream qin(stdin);
    
        std::cout << "Press Keyboard buttons to Start: " << std::endl;
        std::cout << "S for Start, State Name to go to next State and Q to quit " << std::endl;
        QString line = qin.readLine();
    
        if (line=="S")
        {
        std::cout << "Machine Started" << std::endl;
        std::cout << "1ist of Machine States" << std::endl;
        std::cout << "State 1 named s1" << std::endl;
        std::cout << "State 2 named s2" << std::endl;
        std::cout << "State 3 named s3" << std::endl;
    
    }
    
        if (line == "State1" )
        {
            std::cout << "Entering S1" << std::endl;
            std::cout << "State Changed signal" << std::endl;
            emit start();
       }
    
              else if (line == "State2")
        {
    //          Test::runS2();
            emit start();
        }
    
        else if (line == "State3")
        {
    //           Test::runS3();
            emit start();
    }
    
        else if (line == "q")
           {
            emit stop();
           }
    
        else
        {
            Run();
        }
    }
    
    
    void Test::buildStateMachine()
    {
        struct StringEvent : public QEvent
        {
            StringEvent(const QString &val)
            : QEvent(QEvent::Type(QEvent::User+1)),
              value(val) {}
    
            QString value;
    
            QStateMachine machine;
            QState *s1 = new QState();
            QState *s2 = new QState();
            QState *s3 = new QState();
    };
    
    
            StringTransition *t1 = new StringTransition("State1");
            t1->setTargetState(s2);
            s1->addTransition(this,SIGNAL(start()),t1);
    
            StringTransition *t2 = new StringTransition("State2");
            t2->setTargetState(done);
            s2->addTransition(this,SIGNAL(start()),t2);
    
            StringTransition *t3 = new StringTransition("State3");
            t3->setTargetState(done);
            s3->addTransition(this,SIGNAL(start()),t3);
    
            machine.addState(s1);
            machine.addState(s2);
            machine.addState(s3);
            machine.addState(done);
            machine.setInitialState(s1);
    
            machine.postEvent(new StringEvent("State1"));
            machine.postEvent(new StringEvent("State2"));
            machine.postEvent(new StringEvent("State3"));
    
    };
    
    
    
    
    
    
    
    
    
        //    QState *I1 = new QState(&machine);
        //    QState *s1 = new QState(&machine);
        //    QState *s2 = new QState(&machine);
        //    QState *s3 = new QState(&machine);
    
    
        //    I1->addTransition(this, SIGNAL (next_state()), s1);
        //    I1->addTransition(this, SIGNAL (next_state()), s2);
        //    I1->addTransition(this, SIGNAL (next_state()), s3);
    
        //    s1->addTransition(this, SIGNAL(next_state()), s2);
        //    s1->addTransition(this, SIGNAL(next_state()), s3);
    
        //    s2->addTransition(this, SIGNAL(next_state()), s3);
        //    s2->addTransition(this, SIGNAL(next_state()), s1);
    
        //    s3->addTransition(this, SIGNAL(next_state()), s1);
        //    s3->addTransition(this, SIGNAL(next_state()), s2);
    
        //    connect(I1, &QState::entered, this, &Test::runI1);
        //    connect(s1, &QState::entered, this, &Test::runS1);
        //    connect(s2, &QState::entered, this, &Test::runS2);
        //    connect(s3, &QState::entered, this, &Test::runS3);
    
        //    machine.setInitialState(I1);
    
        //    machine.start();
    
    
    
    //void Test::runI1()
    //{
    
    
    //    Run();
    //}
    
    //void Test::runS1()
    //{
    //    std::cout << "entered state S1" << std::endl;
    
    //    Run();
    //}
    
    //void Test::runS2()
    //{
    //    std::cout << "entered state S2" << std::endl;
    //    Run();
    //}
    
    //void Test::runS3()
    //{
    //    std::cout << "entered state S3" << std::endl;
    //    Run();
    //}
    
    //void Test::debugSlot()
    //{
    //    std::cout << "debug slot was triggered!" << std::endl;
    //    Run();
    //}
    
    

    Thankyou in Advance


  • Lifetime Qt Champion

    Hi and welcome to devnet,

    Since you're a full beginner, you should first take the time to learn C++.

    You should rather name your files following the class you implement with them.

    Since you are using the code from the State Machine API documentation. You should start simple. Everything can fit in main.cpp. Once you have that running, you can separate them in their own files.



  • Hello, Thankyou for your quick reply.
    Actually I am learning C++ and qt at the same time.
    Also, I am a newly hired in a company which makes application for embedded systems based on Qt's State Machine.
    As I have already gone through the Qt's documentation for State Machine. I find it difficult as the project I am working is non graphical and all the examples and things in the internet is for graphical interfaces and I have hard time understanding all the concept of Qt.


  • Lifetime Qt Champion

    The example are using GUI just because it allows to better visualise the interaction you can have with a GUI. However, QStateMachine itself is completely independent of any GUI. You can model the behaviour you want without any graphical element.

    What concept are you struggling with ?



  • Thankyou for your reply.
    I agree that examples for GUI are really good and it clears alot of doubts but when I try to implement it for a non graphical simple console application for example just changing the state from a string input like in example I am trying above.
    I have a little hard time understanding that if I make a class of State 1 , class of State 2 and Class of State 3.
    and in each state I perform just a normal print in the console that I have entered this state.
    I have hard time understanding how do I do that.
    instead of that I implemented the above code in the post but I have a problem understanding, How do I print with StringTransition
    or to use onEntry , OnExit .
    Although, I understand I may have some conceptual problem in understanding but as I said earlier I think I will only understand when I try to code hands on and make mistakes.
    I really appreciate that you make a time to reply back because I feel like if someone explains it to me I would really excell in it.



  • From what I see you have two options to trigger code when entering a state. Either create a slot that will be connected to the state's "entered" signal or inherit QState and override onEntry.


Log in to reply