Qt with Cascades UI Examples Documentation

Contents

Factorial States Example

Files:

Description

The Factorial States example shows how to use The State Machine Framework to calculate the factorial of an integer.

The statechart for calculating the factorial looks as follows:

In other words, the state machine calculates the factorial of 6 and prints the result.

    /**
     * The Factorial class stores the values that are used to calculate the factorial.
     * It makes the current x value (x), the original x value (xorig) and the calculated
     * factorial (fac) available as properties, so that they can be accessed by the
     * states and transitions and also by the UI.
     */
    class Factorial : public QObject
    {
        Q_OBJECT

        // The value properties
        Q_PROPERTY(int x READ x WRITE setX NOTIFY xChanged)
        Q_PROPERTY(int xorig READ xorig WRITE setXorig NOTIFY xorigChanged)
        Q_PROPERTY(int fac READ fac WRITE setFac NOTIFY facChanged)

    public:
        Factorial(QObject *parent = 0);

        // The accessor methods for the value properties
        int x() const;
        void setX(int x);

        int xorig() const;
        void setXorig(int xorig);

        int fac() const;
        void setFac(int fac);

    Q_SIGNALS:
        // The change notification signals of the value properties
        void xChanged(int value);
        void xorigChanged(int value);
        void facChanged(int value);

    private:
        // The current x value
        int m_x;

        // The initial x value
        int m_xorig;

        // The calculated factorial
        int m_fac;
    };

The Factorial class is used to hold the data of the computation, x and fac. It also provides a signal that's emitted whenever the value of x changes.

    /**
     * This custom transition has the 'compute' state as source and
     * target state, so it just loops until eventTest() returns 'false'.
     * Inside the onTransition() method the actual work (calculating the factorial)
     * is done.
     */
    class FactorialLoopTransition : public QSignalTransition
    {
    public:
        // Creates a new factorial loop transition that works on the values of the given Factorial object
        FactorialLoopTransition(Factorial *fact);

        // This method is called by the state machine to check whether this transition can be executed
        virtual bool eventTest(QEvent *e);

        // This method is called by the state machine if this transition is executed
        virtual void onTransition(QEvent *e);

    private:
        // The Factorial object to work on
        Factorial *m_fact;
    };

The FactorialLoopTransition class implements the guard (x > 1) and calculations (fac = x * fac; x = x - 1) of the factorial loop.

    /**
     * This custom transition has the 'compute' state as source and
     * the 'done' state as target state.
     * It tests inside eventTest() whether the calculation of the factorial is finished
     * and returns 'true' in this case.
     */
    class FactorialDoneTransition : public QSignalTransition
    {
    public:
        // Creates a new factorial loop transition that works on the values of the given Factorial object
        FactorialDoneTransition(Factorial *fact);

        // This method is called by the state machine to check whether this transition can be executed
        virtual bool eventTest(QEvent *e);

    private:
        // The Factorial object to work on
        Factorial *m_fact;
    };

The FactorialDoneTransition class implements the guard (x <= 1) that terminates the factorial computation. It also prints the final result to standard output.

    Q_DECL_EXPORT int main(int argc, char **argv)
    {
        Application app(argc, argv);

        Factorial factorial;

        // Create the state machine
        QStateMachine machine;

The application's main() function first creates the application object, a Factorial object and a state machine.

        // Create the 'compute' state as child of the state machine
        QState *compute = new QState(&machine);

        // Initialize the 'fac', 'x' and 'xorig' properties of the Factorial object whenever the compute state is entered
        compute->assignProperty(&factorial, "fac", 1);
        compute->assignProperty(&factorial, "x", 6);
        compute->assignProperty(&factorial, "xorig", 6);

        /**
         * Add the custom transition to the compute state.
         * Note: This transition has the compute state as source and target state.
         */
        compute->addTransition(new FactorialLoopTransition(&factorial));

The compute state is created, and the initial values of x and fac are defined. A FactorialLoopTransition object is created and added to the state.

        // Create a final state
        QFinalState *done = new QFinalState(&machine);

        // Add a custom transition with the 'compute' state as source state and the 'done' state as target state
        FactorialDoneTransition *doneTransition = new FactorialDoneTransition(&factorial);
        doneTransition->setTargetState(done);
        compute->addTransition(doneTransition);

A final state, done, is created, and a FactorialDoneTransition object is created with done as its target state. The transition is then added to the compute state.

        // Set the 'compute' state as initial state of the state machine
        machine.setInitialState(compute);

        // Load the UI description from main.qml
        QmlDocument *qml = QmlDocument::create("asset:///main.qml");

        // Make the Factorial and StateMachine object available to the UI as context properties
        qml->setContextProperty("_factorial", &factorial);
        qml->setContextProperty("_machine", &machine);

        // Create the application scene
        AbstractPane *appPage = qml->createRootObject<AbstractPane>();
        Application::instance()->setScene(appPage);

        return Application::exec();
    }

The machine's initial state is set to be the compute state. We export both the factorial and the state machine objects to QML, so that we are able to start the state machine from the UI and display the factorial values. Finally, the application's event loop is entered.