Files:
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.