Detail Analyze On State Machine Framework
It’s November 6, I must work hard for my Qt Application for four days. November 10 is the limit time of our work. But I will not stop analyzing the Qt Source.
Now let’s begin today’s work. Look at the following code.
QStateMachine machine;
QState *s2 = new QState;
machine.addState (s1);
machine.addState (s2);
machine.setInitialState (s2);
for (int i = 0;i<12;i++)
{
Pixmap * pPixmap = new Pixmap(QPixmap(100,100));
s1->assignProperty(pPixmap,"pos",QPointF(cos(i*30/57.297)*130,sin(i*30/57.297)*130));
s2->assignProperty(pPixmap,"pos",QPointF(0,0));
scene.addItem(pPixmap);
}
QTimer timer;
timer.start(1000);
timer.setSingleShot(true);
s2->addTransition(&timer, SIGNAL(timeout()), s1);
machine.start();
This is a simple code to show how to use the state machine framework.
First we need an object of QStateMachine. So let’s look at the constructor.
/*!
Constructs a new state machine with the given /a parent.
*/
QStateMachine::QStateMachine(QObject *parent)
: QState(*new QStateMachinePrivate, /*parentState=*/0)
{
// Can't pass the parent to the QState constructor, as it expects a QState
// But this works as expected regardless of whether parent is a QState or not
setParent(parent);
}
This function is just to add this object to the main object tree, and create a new object from QStateMachinePrivate class. So let’s have a look at the constructor of QStateMachinePrivate class.
QStateMachinePrivate::QStateMachinePrivate()
{
state = NotRunning;
_startState = 0;
processing = false;
processingScheduled = false;
stop = false;
stopProcessingReason = EventQueueEmpty;
error = QStateMachine::NoError;
globalRestorePolicy = QStateMachine::DoNotRestoreProperties;
signalEventGenerator = 0;
#ifndef QT_NO_ANIMATION
animationsEnabled = true;
#endif
}
It’s easy to understand, the only thing this function do is to initialize the member variable. From this definition, QT_NO_ANIMATION, we can find that this class always using with the animation framework.
And then, let’s look at the next sentence.
QState *s1 = new QState;
QState *s2 = new QState;
machine.addState (s1);
machine.addState (s2);
machine.setInitialState (s2);
First, they create two object of QState. And then call the addState () function from the object named machine. At yesterday’s analysis, we mentioned that the addState () function is just to add the current object to the main object tree for auto destruction. And what did the setInitialState () function do? We can find this function is a member of QState class.
/*!
Sets this state's initial state to be the given /a state.
/a state has to be a child of this state.
*/
void QState::setInitialState(QAbstractState *state)
{
Q_D(QState);
if (d->childMode == QState::ParallelStates) {
qWarning("QState::setInitialState: ignoring attempt to set initial state "
"of parallel state group %p", this);
return;
}
if (state && (state->parentState() != this)) {
qWarning("QState::setInitialState: state %p is not a child of this state (%p)",
state, this);
return;
}
d->initialState = state;
}
You see, it just save the point of state to its member variable, and waiting for the start () function is called.
The important thing is to understand how this sentence to work.
s2->assignProperty(pPixmap,"pos",QPointF(0,0));
To make this function works, they use the meta-object system.
/*!
Instructs this state to set the property with the given /a name of the given
/a object to the given /a value when the state is entered.
/sa polished()
*/
void QState::assignProperty(QObject *object, const char *name,
const QVariant &value)
{
Q_D(QState);
if (!object) {
qWarning("QState::assignProperty: cannot assign property '%s' of null object", name);
return;
}
for (int i = 0; i < d->propertyAssignments.size(); ++i) {
QPropertyAssignment &assn = d->propertyAssignments[i];
if ((assn.object == object) && (assn.propertyName == name)) {
assn.value = value;
return;
}
}
d->propertyAssignments.append(QPropertyAssignment(object, name, value));
}
At this function, they use the QPropertyAssignment class. It’s an inner class which storage the data of state property.
struct QPropertyAssignment
{
QPropertyAssignment()
: object(0), explicitlySet(true) {}
QPropertyAssignment(QObject *o, const QByteArray &n,
const QVariant &v, bool es = true)
: object(o), propertyName(n), value(v), explicitlySet(es)
{}
QObject *object;
QByteArray propertyName;
QVariant value;
bool explicitlySet;
};
They use a list of QPropertyAssignment’s object to save the data. And save data by call the assignProperty () function.
The next step is to set the trigger signal access to call addTransition () function.
/*!
Adds the given /a transition. The transition has this state as the source.
This state takes ownership of the transition. If the transition is successfully
added, the function will return the /a transition pointer. Otherwise it will return null.
*/
QAbstractTransition *QState::addTransition(QAbstractTransition *transition)
{
Q_D(QState);
if (!transition) {
qWarning("QState::addTransition: cannot add null transition");
return 0;
}
transition->setParent(this);
const QList<QWeakPointer<QAbstractState> > &targets = QAbstractTransitionPrivate::get(transition)->targetStates;
for (int i = 0; i < targets.size(); ++i) {
QAbstractState *t = targets.at(i).data();
if (!t) {
qWarning("QState::addTransition: cannot add transition to null state");
return 0;
}
if ((QAbstractStatePrivate::get(t)->machine() != d->machine())
&& QAbstractStatePrivate::get(t)->machine() && d->machine()) {
qWarning("QState::addTransition: cannot add transition "
"to a state in a different state machine");
return 0;
}
}
if (machine() != 0 && machine()->configuration().contains(this))
QStateMachinePrivate::get(machine())->registerTransitions(this);
return transition;
}
I have no enough time to analyze this function. The next day, I will continue to do it.
November 7, 2009 00:22