From c07b3bd9c3c2d7195dfa52547961647a963c8d41 Mon Sep 17 00:00:00 2001 From: David Boddie Date: Wed, 29 Apr 2009 18:20:49 +0200 Subject: Merged local changes. --- doc/src/animation.qdoc | 14 +++++++------- doc/src/groups.qdoc | 2 +- src/corelib/animation/qanimationgroup.cpp | 2 +- src/gui/statemachine/qbasickeyeventtransition.cpp | 2 ++ src/gui/statemachine/qbasicmouseeventtransition.cpp | 2 ++ 5 files changed, 13 insertions(+), 9 deletions(-) diff --git a/doc/src/animation.qdoc b/doc/src/animation.qdoc index 081660c..5eb2eda 100644 --- a/doc/src/animation.qdoc +++ b/doc/src/animation.qdoc @@ -40,7 +40,7 @@ ****************************************************************************/ /*! - \page animation.html + \page animation-overview.html \title The Animation Framework \ingroup architecture \ingroup animation @@ -132,7 +132,7 @@ \endcode This code will move \c button from the top left corner of the - screen to the position (250, 250). + screen to the position (250, 250) in 10 seconds (10000 milliseconds). The example above will do a linear interpolation between the start and end value. It is also possible to set values @@ -162,7 +162,7 @@ that is not declared as a Qt property. The only requirement is that this value has a setter. You can then subclass the class containing the value and declare a property that uses this setter. - Note that all Qt properties requires a getter, so you will need to + Note that each Qt property requires a getter, so you will need to provide a getter yourself if this is not defined. \code @@ -184,9 +184,9 @@ \section1 Animations and the Graphics View Framework When you want to animate \l{QGraphicsItem}s, you also use - QPropertyAnimation. But, unfortunetly, QGraphicsItem does not - inherit QObject. A good solution is to subclass the graphics item - you wish to animate. This class will then also inherit QObject. + QPropertyAnimation. However, QGraphicsItem does not inherit QObject. + A good solution is to subclass the graphics item you wish to animate. + This class will then also inherit QObject. This way, QPropertyAnimation can be used for \l{QGraphicsItem}s. The example below shows how this is done. Another possibility is to inherit QGraphicsWidget, which already is a QObject. @@ -326,7 +326,7 @@ When using a \l{The State Machine Framework}{state machine}, we have a special state, QAnimationState, that will play one or more animations. - + The QState::addAnimatedTransition() convenience function lets you associate an animation to a state transition. The function will create the QAnimationState for you, and insert it into the state diff --git a/doc/src/groups.qdoc b/doc/src/groups.qdoc index 0411c3a..fda54dc 100644 --- a/doc/src/groups.qdoc +++ b/doc/src/groups.qdoc @@ -69,7 +69,7 @@ */ /*! - \group animations + \group animation \ingroup groups \title Animation Framework diff --git a/src/corelib/animation/qanimationgroup.cpp b/src/corelib/animation/qanimationgroup.cpp index 03573bb..0d87527 100644 --- a/src/corelib/animation/qanimationgroup.cpp +++ b/src/corelib/animation/qanimationgroup.cpp @@ -41,7 +41,7 @@ /*! \class QAnimationGroup - \brief The QAnimationGroup is an abstract base class for group of animations. + \brief The QAnimationGroup class is an abstract base class for groups of animations. \since 4.5 \ingroup animation \preliminary diff --git a/src/gui/statemachine/qbasickeyeventtransition.cpp b/src/gui/statemachine/qbasickeyeventtransition.cpp index 4e3fa11..189332d 100644 --- a/src/gui/statemachine/qbasickeyeventtransition.cpp +++ b/src/gui/statemachine/qbasickeyeventtransition.cpp @@ -25,6 +25,8 @@ QT_BEGIN_NAMESPACE /*! \internal \class QBasicKeyEventTransition + \since 4.6 + \ingroup statemachine \brief The QBasicKeyEventTransition class provides a transition for Qt key events. */ diff --git a/src/gui/statemachine/qbasicmouseeventtransition.cpp b/src/gui/statemachine/qbasicmouseeventtransition.cpp index 83254dc..a330c78 100644 --- a/src/gui/statemachine/qbasicmouseeventtransition.cpp +++ b/src/gui/statemachine/qbasicmouseeventtransition.cpp @@ -25,6 +25,8 @@ QT_BEGIN_NAMESPACE /*! \internal \class QBasicMouseEventTransition + \since 4.6 + \ingroup statemachine \brief The QBasicMouseEventTransition class provides a transition for Qt mouse events. */ -- cgit v0.12 From b39781b214e2502e0884bce88aa3ac324f2d0b12 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Thu, 7 May 2009 13:03:59 +0200 Subject: Make it impossible to have root state as source or target of transition or as error state Since the root state has no ancestors, it cannot be source or target in transitions since there will be no LCA for the transition, which is required for the algorithm of enterStates and exitStates. In SCXML the root state cannot be target or source of a transition. By the same logic, it cannot be an error state. The root state will always have a valid machine, since it's added to a machine immediately, which makes this code possible. --- src/corelib/statemachine/qabstracttransition.cpp | 15 ++++++++++++--- src/corelib/statemachine/qstate.cpp | 13 ++++++++++++- src/corelib/statemachine/qstatemachine.cpp | 2 ++ tests/auto/qstatemachine/tst_qstatemachine.cpp | 7 +++++-- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/src/corelib/statemachine/qabstracttransition.cpp b/src/corelib/statemachine/qabstracttransition.cpp index 1897aa6..c6a261d 100644 --- a/src/corelib/statemachine/qabstracttransition.cpp +++ b/src/corelib/statemachine/qabstracttransition.cpp @@ -180,7 +180,7 @@ QAbstractTransition::QAbstractTransition(const QList &targets, d_ptr->q_ptr = this; #endif Q_D(QAbstractTransition); - d->targetStates = targets; + setTargetStates(targets); } /*! @@ -221,7 +221,7 @@ QAbstractTransition::QAbstractTransition(QAbstractTransitionPrivate &dd, d_ptr->q_ptr = this; #endif Q_D(QAbstractTransition); - d->targetStates = targets; + setTargetStates(targets); } /*! @@ -265,7 +265,7 @@ void QAbstractTransition::setTargetState(QAbstractState* target) if (!target) d->targetStates.clear(); else - d->targetStates = QList() << target; + setTargetStates(QList() << target); } /*! @@ -284,6 +284,15 @@ QList QAbstractTransition::targetStates() const void QAbstractTransition::setTargetStates(const QList &targets) { Q_D(QAbstractTransition); + + for (int i=0; imachine() != 0 && target->machine()->rootState() == target) { + qWarning("QAbstractTransition::setTargetStates: root state cannot be target of transition"); + return; + } + } + d->targetStates = targets; } diff --git a/src/corelib/statemachine/qstate.cpp b/src/corelib/statemachine/qstate.cpp index 4c9e033..acee27d 100644 --- a/src/corelib/statemachine/qstate.cpp +++ b/src/corelib/statemachine/qstate.cpp @@ -280,11 +280,15 @@ QAbstractState *QState::errorState() const void QState::setErrorState(QAbstractState *state) { Q_D(QState); - if (state != 0 && QAbstractStatePrivate::get(state)->machine() != d->machine()) { + if (state != 0 && state->machine() != machine()) { qWarning("QState::setErrorState: error state cannot belong " "to a different state machine"); return; } + if (state->machine() != 0 && state->machine()->rootState() == state) { + qWarning("QStateMachine::setErrorState: root state cannot be error state"); + return; + } d->errorState = state; } @@ -301,6 +305,13 @@ QAbstractTransition *QState::addTransition(QAbstractTransition *transition) qWarning("QState::addTransition: cannot add null transition"); return 0; } + + // machine() will always be non-null for root state + if (machine() != 0 && machine()->rootState() == this) { + qWarning("QState::addTransition: cannot add transition from root state"); + return 0; + } + const QList &targets = QAbstractTransitionPrivate::get(transition)->targetStates; for (int i = 0; i < targets.size(); ++i) { QAbstractState *t = targets.at(i); diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp index 21e564c..110a4f8 100644 --- a/src/corelib/statemachine/qstatemachine.cpp +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -1011,6 +1011,8 @@ void QStateMachinePrivate::setError(QStateMachine::Error errorCode, QAbstractSta } Q_ASSERT(currentErrorState != 0); + Q_ASSERT(currentErrorState != rootState); + QState *lca = findLCA(QList() << currentErrorState << currentContext); addStatesToEnter(currentErrorState, lca, pendingErrorStates, pendingErrorStatesForDefaultEntry); } diff --git a/tests/auto/qstatemachine/tst_qstatemachine.cpp b/tests/auto/qstatemachine/tst_qstatemachine.cpp index edd6459..9b7bff3 100644 --- a/tests/auto/qstatemachine/tst_qstatemachine.cpp +++ b/tests/auto/qstatemachine/tst_qstatemachine.cpp @@ -254,6 +254,8 @@ private: void tst_QStateMachine::transitionToRootState() { + s_countWarnings = false; + QStateMachine machine; QState *initialState = new QState(); @@ -668,6 +670,8 @@ void tst_QStateMachine::errorStateHasErrors() void tst_QStateMachine::errorStateIsRootState() { + s_countWarnings = false; + QStateMachine machine; machine.setErrorState(machine.rootState()); @@ -691,9 +695,8 @@ void tst_QStateMachine::errorStateIsRootState() machine.postEvent(new QEvent(QEvent::Type(QEvent::User + 1))); QCoreApplication::processEvents(); - QEXPECT_FAIL("", "Covered by transitionToRootState test. Remove this when that test passes", Continue); QCOMPARE(machine.configuration().count(), 1); - QVERIFY(machine.configuration().contains(initialState)); + QVERIFY(machine.configuration().contains(machine.errorState())); } void tst_QStateMachine::errorStateEntersParentFirst() -- cgit v0.12 From 00f04527d3d28ab1aa5b90b9a05366012ff5d1e5 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Thu, 7 May 2009 17:18:51 +0200 Subject: don't add signal transition if target is null or signal doesn't exist --- src/corelib/statemachine/qstate.cpp | 9 +++++++++ tests/auto/qstatemachine/tst_qstatemachine.cpp | 23 +++++++++++++++++++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/corelib/statemachine/qstate.cpp b/src/corelib/statemachine/qstate.cpp index acee27d..4d12219 100644 --- a/src/corelib/statemachine/qstate.cpp +++ b/src/corelib/statemachine/qstate.cpp @@ -346,6 +346,15 @@ QSignalTransition *QState::addTransition(QObject *sender, const char *signal, qWarning("QState::addTransition: signal cannot be null"); return 0; } + if (!target) { + qWarning("QState::addTransition: cannot add transition to null state"); + return 0; + } + if (*signal && sender->metaObject()->indexOfSignal(signal+1) == -1) { + qWarning("QState::addTransition: no such signal %s::%s", + sender->metaObject()->className(), signal+1); + return 0; + } QSignalTransition *trans = new QSignalTransition(sender, signal, QList() << target); addTransition(trans); return trans; diff --git a/tests/auto/qstatemachine/tst_qstatemachine.cpp b/tests/auto/qstatemachine/tst_qstatemachine.cpp index 9b7bff3..f7fce94 100644 --- a/tests/auto/qstatemachine/tst_qstatemachine.cpp +++ b/tests/auto/qstatemachine/tst_qstatemachine.cpp @@ -1579,9 +1579,28 @@ void tst_QStateMachine::signalTransitions() { QStateMachine machine; QState *s0 = new QState(machine.rootState()); - QFinalState *s1 = new QFinalState(machine.rootState()); + QTest::ignoreMessage(QtWarningMsg, "QState::addTransition: sender cannot be null"); + QCOMPARE(s0->addTransition(0, SIGNAL(noSuchSignal()), 0), (QObject*)0); + SignalEmitter emitter; - s0->addTransition(&emitter, SIGNAL(signalWithNoArg()), s1); + QTest::ignoreMessage(QtWarningMsg, "QState::addTransition: signal cannot be null"); + QCOMPARE(s0->addTransition(&emitter, 0, 0), (QObject*)0); + + QTest::ignoreMessage(QtWarningMsg, "QState::addTransition: cannot add transition to null state"); + QCOMPARE(s0->addTransition(&emitter, SIGNAL(signalWithNoArg()), 0), (QObject*)0); + + QFinalState *s1 = new QFinalState(machine.rootState()); + QTest::ignoreMessage(QtWarningMsg, "QState::addTransition: no such signal SignalEmitter::noSuchSignal()"); + QCOMPARE(s0->addTransition(&emitter, SIGNAL(noSuchSignal()), s1), (QObject*)0); + + { + QSignalTransition *trans = s0->addTransition(&emitter, SIGNAL(signalWithNoArg()), s1); + QVERIFY(trans != 0); + QCOMPARE(trans->sourceState(), s0); + QCOMPARE(trans->targetState(), (QAbstractState*)s1); + QCOMPARE(trans->senderObject(), (QObject*)&emitter); + QCOMPARE(trans->signal(), QByteArray(SIGNAL(signalWithNoArg()))); + } QSignalSpy finishedSpy(&machine, SIGNAL(finished())); machine.setInitialState(s0); -- cgit v0.12 From 58bf96d7252df3346d0f325af265873897e56172 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Fri, 8 May 2009 09:55:28 +0200 Subject: don't create transition to null state --- src/corelib/statemachine/qstate.cpp | 7 +++++-- tests/auto/qstatemachine/tst_qstatemachine.cpp | 25 +++++++++++++++++++++++-- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/corelib/statemachine/qstate.cpp b/src/corelib/statemachine/qstate.cpp index 4d12219..63a0a69 100644 --- a/src/corelib/statemachine/qstate.cpp +++ b/src/corelib/statemachine/qstate.cpp @@ -381,9 +381,12 @@ protected: */ QAbstractTransition *QState::addTransition(QAbstractState *target) { + if (!target) { + qWarning("QState::addTransition: cannot add transition to null state"); + return 0; + } UnconditionalTransition *trans = new UnconditionalTransition(target); - addTransition(trans); - return trans; + return addTransition(trans); } /*! diff --git a/tests/auto/qstatemachine/tst_qstatemachine.cpp b/tests/auto/qstatemachine/tst_qstatemachine.cpp index f7fce94..93cd1b3 100644 --- a/tests/auto/qstatemachine/tst_qstatemachine.cpp +++ b/tests/auto/qstatemachine/tst_qstatemachine.cpp @@ -1091,11 +1091,28 @@ void tst_QStateMachine::stateEntryAndExit() QStateMachine machine; TestState *s1 = new TestState(machine.rootState()); + QTest::ignoreMessage(QtWarningMsg, "QState::addTransition: cannot add transition to null state"); + s1->addTransition((QAbstractState*)0); + QTest::ignoreMessage(QtWarningMsg, "QState::addTransition: cannot add null transition"); + s1->addTransition((QAbstractTransition*)0); + QTest::ignoreMessage(QtWarningMsg, "QState::removeTransition: cannot remove null transition"); + s1->removeTransition((QAbstractTransition*)0); + TestState *s2 = new TestState(machine.rootState()); QFinalState *s3 = new QFinalState(machine.rootState()); TestTransition *t = new TestTransition(s2); - s1->addTransition(t); - s2->addTransition(s3); + QCOMPARE(s1->addTransition(t), (QAbstractTransition*)t); + { + QAbstractTransition *trans = s2->addTransition(s3); + QVERIFY(trans != 0); + QCOMPARE(trans->sourceState(), (QAbstractState*)s2); + QCOMPARE(trans->targetState(), (QAbstractState*)s3); + s2->removeTransition(trans); + QCOMPARE(trans->sourceState(), (QAbstractState*)0); + QCOMPARE(trans->targetState(), (QAbstractState*)s3); + QCOMPARE(s2->addTransition(trans), trans); + QCOMPARE(trans->sourceState(), (QAbstractState*)s2); + } QSignalSpy startedSpy(&machine, SIGNAL(started())); QSignalSpy stoppedSpy(&machine, SIGNAL(stopped())); @@ -1311,6 +1328,8 @@ void tst_QStateMachine::assignPropertyWithAnimation() QStateMachine machine; QObject obj; QState *s1 = new QState(machine.rootState()); + QCOMPARE(s1->childMode(), QState::ExclusiveStates); + QCOMPARE(s1->initialState(), (QAbstractState*)0); s1->setObjectName("s1"); s1->assignProperty(&obj, "foo", 123); s1->assignProperty(&obj, "bar", 456); @@ -1324,6 +1343,7 @@ void tst_QStateMachine::assignPropertyWithAnimation() s22->setObjectName("s22"); s22->assignProperty(&obj, "bar", 789); s2->setInitialState(s21); + QCOMPARE(s2->initialState(), (QAbstractState*)s21); QAbstractTransition *trans = s1->addTransition(s2); QPropertyAnimation anim(&obj, "foo"); @@ -1448,6 +1468,7 @@ void tst_QStateMachine::parallelStates() QStateMachine machine; QState *s1 = new QState(QState::ParallelStates); + QCOMPARE(s1->childMode(), QState::ParallelStates); QState *s1_1 = new QState(s1); QState *s1_1_1 = new QState(s1_1); QFinalState *s1_1_f = new QFinalState(s1_1); -- cgit v0.12 From c7b44a8bd67f4eace20c11ee78af6afeda774386 Mon Sep 17 00:00:00 2001 From: Geir Vattekar Date: Fri, 8 May 2009 10:42:27 +0200 Subject: Doc: Work on QAnimationGroup class description. --- src/corelib/animation/qanimationgroup.cpp | 52 ++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/src/corelib/animation/qanimationgroup.cpp b/src/corelib/animation/qanimationgroup.cpp index 03573bb..2f8cc28 100644 --- a/src/corelib/animation/qanimationgroup.cpp +++ b/src/corelib/animation/qanimationgroup.cpp @@ -46,18 +46,46 @@ \ingroup animation \preliminary - QAnimationGroup represents a group of animations, such as parallel or sequential, - and lets you combine different animations into one. The group manages any animation - that inherits QAbstractAnimation. By combining groups, you can easily construct - complex animation graphs. - - The QAnimationGroup base class provides methods for adding and retrieving animations. - Besides that, you can remove animations by calling remove(), and clear the animation - group by calling clearAnimations(). You may keep track of changes in the group's animations by - listening to QEvent::ChildAdded and QEvent::ChildRemoved events. - - QAnimationGroup takes ownership of the animations it manages, and ensures that they are - deleted when the animation group is deleted. + An animation group is a container for animations (subclasses of + QAbstractAnimation). A group is usually responsible for managing + the \l{QAbstractAnimation::State}{state} of its animations, i.e., + it decides when to start, stop, resume, and pause them. Currently, + Qt provides two such groups: QParallelAnimationGroup and + QSequentialAnimationGroup. Look up their class descriptions for + details. + + Since QAnimationGroup inherits from QAbstractAnimation, you can + combine groups, and easily construct complex animation graphs. + You can query QAbstractAnimation for the group it belongs to + (using the \l{QAbstractAnimation::}{group()} function). + + To start a top-level animation group, you simply use the + \l{QAbstractAnimation::}{start()} function from + QAbstractAnimation. By a top-level animation group, we think of a + group that itself is not contained within another group. Starting + sub groups directly is not supported, and may lead to unexpected + behavior. + + \omit OK, we'll put in a snippet on this here \endomit + + QAnimationGroup provides methods for adding and retrieving + animations. Besides that, you can remove animations by calling + remove(), and clear the animation group by calling + clearAnimations(). You may keep track of changes in the group's + animations by listening to QEvent::ChildAdded and + QEvent::ChildRemoved events. + + \omit OK, let's find a snippet here as well. \endomit + + QAnimationGroup takes ownership of the animations it manages, and + ensures that they are deleted when the animation group is deleted. + + You can also use a \l{The State Machine Framework}{state machine} + to create complex animations. The framework provides a special + state, QAnimationState, that plays an animation upon entry and + transitions to a new state when the animation has finished + playing. This technique can also be combined with using animation + groups. \sa QAbstractAnimation, QVariantAnimation, {The Animation Framework} */ -- cgit v0.12 From f64170262441cce51b52d05a2f2ba2f0021ba8e4 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Fri, 8 May 2009 12:59:03 +0200 Subject: make sure connections and event filters are removed when state machine halts --- src/corelib/statemachine/qstatemachine.cpp | 16 ++++++++++++++++ src/corelib/statemachine/qstatemachine_p.h | 1 + tests/auto/qstatemachine/tst_qstatemachine.cpp | 4 ++++ 3 files changed, 21 insertions(+) diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp index 110a4f8..24af8e4 100644 --- a/src/corelib/statemachine/qstatemachine.cpp +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -1224,10 +1224,12 @@ void QStateMachinePrivate::_q_process() break; case Finished: state = NotRunning; + unregisterAllTransitions(); emit q->finished(); break; case Stopped: state = NotRunning; + unregisterAllTransitions(); emit q->stopped(); break; } @@ -1358,6 +1360,20 @@ void QStateMachinePrivate::unregisterSignalTransition(QSignalTransition *transit #endif } +void QStateMachinePrivate::unregisterAllTransitions() +{ + { + QList transitions = qFindChildren(rootState); + for (int i = 0; i < transitions.size(); ++i) + unregisterSignalTransition(transitions.at(i)); + } + { + QList transitions = qFindChildren(rootState); + for (int i = 0; i < transitions.size(); ++i) + unregisterEventTransition(transitions.at(i)); + } +} + #ifndef QT_NO_STATEMACHINE_EVENTFILTER void QStateMachinePrivate::registerEventTransition(QEventTransition *transition) { diff --git a/src/corelib/statemachine/qstatemachine_p.h b/src/corelib/statemachine/qstatemachine_p.h index bb4a78c..47b139c 100644 --- a/src/corelib/statemachine/qstatemachine_p.h +++ b/src/corelib/statemachine/qstatemachine_p.h @@ -150,6 +150,7 @@ public: void unregisterEventTransition(QEventTransition *transition); #endif void unregisterTransition(QAbstractTransition *transition); + void unregisterAllTransitions(); void handleTransitionSignal(const QObject *sender, int signalIndex, void **args); void scheduleProcess(); diff --git a/tests/auto/qstatemachine/tst_qstatemachine.cpp b/tests/auto/qstatemachine/tst_qstatemachine.cpp index 93cd1b3..288cbec 100644 --- a/tests/auto/qstatemachine/tst_qstatemachine.cpp +++ b/tests/auto/qstatemachine/tst_qstatemachine.cpp @@ -1631,6 +1631,8 @@ void tst_QStateMachine::signalTransitions() emitter.emitSignalWithNoArg(); QTRY_COMPARE(finishedSpy.count(), 1); + + emitter.emitSignalWithNoArg(); } { QStateMachine machine; @@ -1722,6 +1724,8 @@ void tst_QStateMachine::eventTransitions() QCoreApplication::processEvents(); QTRY_COMPARE(finishedSpy.count(), 1); + + QTest::mousePress(&button, Qt::LeftButton); } { QStateMachine machine; -- cgit v0.12 From a70e9bc07f2252a49060132fc88ddd4c5963d456 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Fri, 8 May 2009 13:34:22 +0200 Subject: Test what happens when target state doesn't have a parent --- tests/auto/qstatemachine/tst_qstatemachine.cpp | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/auto/qstatemachine/tst_qstatemachine.cpp b/tests/auto/qstatemachine/tst_qstatemachine.cpp index 288cbec..fa49ecb 100644 --- a/tests/auto/qstatemachine/tst_qstatemachine.cpp +++ b/tests/auto/qstatemachine/tst_qstatemachine.cpp @@ -105,6 +105,7 @@ private slots: void eventTransitions(); void historyStates(); void startAndStop(); + void targetStateWithNoParent(); void transitionToRootState(); void transitionEntersParent(); @@ -1490,6 +1491,8 @@ void tst_QStateMachine::parallelStates() QSignalSpy finishedSpy(&machine, SIGNAL(finished())); machine.start(); QTRY_COMPARE(finishedSpy.count(), 1); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s2)); } void tst_QStateMachine::allSourceToTargetConfigurations() @@ -1915,6 +1918,28 @@ void tst_QStateMachine::startAndStop() QVERIFY(machine.configuration().contains(s1)); } +void tst_QStateMachine::targetStateWithNoParent() +{ + QStateMachine machine; + QState *s1 = new QState(machine.rootState()); + s1->setObjectName("s1"); + QState *s2 = new QState(); + s1->addTransition(s2); + machine.setInitialState(s1); + QSignalSpy startedSpy(&machine, SIGNAL(started())); + QSignalSpy stoppedSpy(&machine, SIGNAL(stopped())); + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + machine.start(); + QTest::ignoreMessage(QtWarningMsg, "Unrecoverable error detected in running state machine: No common ancestor for targets and source of transition from state 's1'"); + QTRY_COMPARE(machine.isRunning(), true); + QTRY_COMPARE(startedSpy.count(), 1); + QCOMPARE(stoppedSpy.count(), 0); + QCOMPARE(finishedSpy.count(), 0); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(machine.errorState())); + QCOMPARE(machine.error(), QStateMachine::NoCommonAncestorForTransitionError); +} + void tst_QStateMachine::defaultGlobalRestorePolicy() { QStateMachine machine; -- cgit v0.12 From 110473e9eece3231c3df4fc50c3a958c6c25f2de Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Fri, 8 May 2009 14:29:28 +0200 Subject: get rid of warnings --- src/corelib/statemachine/qabstracttransition.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/corelib/statemachine/qabstracttransition.cpp b/src/corelib/statemachine/qabstracttransition.cpp index c6a261d..696da9f 100644 --- a/src/corelib/statemachine/qabstracttransition.cpp +++ b/src/corelib/statemachine/qabstracttransition.cpp @@ -179,7 +179,6 @@ QAbstractTransition::QAbstractTransition(const QList &targets, #ifdef QT_STATEMACHINE_SOLUTION d_ptr->q_ptr = this; #endif - Q_D(QAbstractTransition); setTargetStates(targets); } @@ -220,7 +219,6 @@ QAbstractTransition::QAbstractTransition(QAbstractTransitionPrivate &dd, #ifdef QT_STATEMACHINE_SOLUTION d_ptr->q_ptr = this; #endif - Q_D(QAbstractTransition); setTargetStates(targets); } -- cgit v0.12 From 98f3ebcf1f6751cb76f9268d33faf0bc5ac70f6e Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Fri, 8 May 2009 16:37:21 +0200 Subject: gracefully handle deletion of transition's target state --- src/corelib/statemachine/qabstracttransition.cpp | 16 ++++++++++++++-- src/corelib/statemachine/qabstracttransition_p.h | 3 ++- src/corelib/statemachine/qstate.cpp | 2 +- tests/auto/qstatemachine/tst_qstatemachine.cpp | 13 +++++++++++++ 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/src/corelib/statemachine/qabstracttransition.cpp b/src/corelib/statemachine/qabstracttransition.cpp index 696da9f..6ddb416 100644 --- a/src/corelib/statemachine/qabstracttransition.cpp +++ b/src/corelib/statemachine/qabstracttransition.cpp @@ -273,7 +273,13 @@ void QAbstractTransition::setTargetState(QAbstractState* target) QList QAbstractTransition::targetStates() const { Q_D(const QAbstractTransition); - return d->targetStates; + QList result; + for (int i = 0; i < d->targetStates.size(); ++i) { + QAbstractState *target = d->targetStates.at(i); + if (target) + result.append(target); + } + return result; } /*! @@ -285,13 +291,19 @@ void QAbstractTransition::setTargetStates(const QList &targets) for (int i=0; imachine() != 0 && target->machine()->rootState() == target) { qWarning("QAbstractTransition::setTargetStates: root state cannot be target of transition"); return; } } - d->targetStates = targets; + d->targetStates.clear(); + for (int i = 0; i < targets.size(); ++i) + d->targetStates.append(targets.at(i)); } /*! diff --git a/src/corelib/statemachine/qabstracttransition_p.h b/src/corelib/statemachine/qabstracttransition_p.h index b4e1c88..eb0ec21 100644 --- a/src/corelib/statemachine/qabstracttransition_p.h +++ b/src/corelib/statemachine/qabstracttransition_p.h @@ -58,6 +58,7 @@ #endif #include +#include QT_BEGIN_NAMESPACE @@ -83,7 +84,7 @@ public: QState *sourceState() const; QStateMachine *machine() const; - QList targetStates; + QList > targetStates; #ifndef QT_NO_ANIMATION QList animations; diff --git a/src/corelib/statemachine/qstate.cpp b/src/corelib/statemachine/qstate.cpp index 63a0a69..5f61865 100644 --- a/src/corelib/statemachine/qstate.cpp +++ b/src/corelib/statemachine/qstate.cpp @@ -312,7 +312,7 @@ QAbstractTransition *QState::addTransition(QAbstractTransition *transition) return 0; } - const QList &targets = QAbstractTransitionPrivate::get(transition)->targetStates; + const QList > &targets = QAbstractTransitionPrivate::get(transition)->targetStates; for (int i = 0; i < targets.size(); ++i) { QAbstractState *t = targets.at(i); if (!t) { diff --git a/tests/auto/qstatemachine/tst_qstatemachine.cpp b/tests/auto/qstatemachine/tst_qstatemachine.cpp index fa49ecb..dbc67d1 100644 --- a/tests/auto/qstatemachine/tst_qstatemachine.cpp +++ b/tests/auto/qstatemachine/tst_qstatemachine.cpp @@ -106,6 +106,7 @@ private slots: void historyStates(); void startAndStop(); void targetStateWithNoParent(); + void targetStateDeleted(); void transitionToRootState(); void transitionEntersParent(); @@ -1940,6 +1941,18 @@ void tst_QStateMachine::targetStateWithNoParent() QCOMPARE(machine.error(), QStateMachine::NoCommonAncestorForTransitionError); } +void tst_QStateMachine::targetStateDeleted() +{ + QStateMachine machine; + QState *s1 = new QState(machine.rootState()); + s1->setObjectName("s1"); + QState *s2 = new QState(machine.rootState()); + QAbstractTransition *trans = s1->addTransition(s2); + delete s2; + QCOMPARE(trans->targetState(), (QAbstractState*)0); + QVERIFY(trans->targetStates().isEmpty()); +} + void tst_QStateMachine::defaultGlobalRestorePolicy() { QStateMachine machine; -- cgit v0.12 From 9fe787a58abe38d35e801c95132b7ee5886f565b Mon Sep 17 00:00:00 2001 From: Geir Vattekar Date: Mon, 11 May 2009 13:34:03 +0200 Subject: Doc: Work on QParallelAnimationGroup and QSequentialAnimationGroup --- src/corelib/animation/qparallelanimationgroup.cpp | 23 +++++++++++++-- .../animation/qsequentialanimationgroup.cpp | 33 ++++++++++++++++++---- 2 files changed, 49 insertions(+), 7 deletions(-) diff --git a/src/corelib/animation/qparallelanimationgroup.cpp b/src/corelib/animation/qparallelanimationgroup.cpp index 407ffde..e4bce6a 100644 --- a/src/corelib/animation/qparallelanimationgroup.cpp +++ b/src/corelib/animation/qparallelanimationgroup.cpp @@ -46,8 +46,27 @@ \ingroup animation \preliminary - The animations are all started at the same time, and run in parallel. The animation group - finishes when the longest lasting animation has finished. + QParallelAnimationGroup--a \l{QAnimationGroup}{container for + animations}--starts all its animations when it is + \l{QAbstractAnimation::start()}{started} itself, i.e., runs all + animations in parallel. The animation group finishes when the + longest lasting animation has finished. + + You can treat QParallelAnimation as any other QAbstractAnimation, + e.g., pause, resume, or add it to other animation groups. + + \code + QParallelAnimationGroup *group = new QParallelAnimationGroup; + group->addAnimation(anim1); + group->addAnimation(anim2); + + group->start(); + \endcode + + In this example, \c anim1 and \c anim2 are two + \l{QPropertyAnimation}s that have already been set up. + + \sa QAnimationGroup, QPropertyAnimation, {The Animation Framework} */ #ifndef QT_NO_ANIMATION diff --git a/src/corelib/animation/qsequentialanimationgroup.cpp b/src/corelib/animation/qsequentialanimationgroup.cpp index 61ff98d..38f4818 100644 --- a/src/corelib/animation/qsequentialanimationgroup.cpp +++ b/src/corelib/animation/qsequentialanimationgroup.cpp @@ -46,13 +46,36 @@ \ingroup animation \preliminary - The first animation in the group is started first, and when it finishes, the next animation - is started, and so on. The animation group finishes when the last animation has finished. + QSequentialAnimationGroup is a QAnimationGroup that runs its + animations in sequence, i.e., it starts one animation after + another has finished playing. The animations are played in the + order they are added to the group (using + \l{QAnimationGroup::}{addAnimation()} or + \l{QAnimationGroup::}{insertAnimationAt()}). The animation group + finishes when its last animation has finished. - At each moment there is at most one animation that is active in the group, called currentAnimation. - An empty group has no current animation. + At each moment there is at most one animation that is active in + the group; it is returned by currentAnimation(). An empty group + has no current animation. - You can call addPause() or insertPause() to add a pause to a sequential animation group. + A sequential animation group can be treated as any other + animation, i.e., it can be started, stopped, and added to other + groups. You can also call addPause() or insertPauseAt() to add a + pause to a sequential animation group. + + \code + QSequentialAnimationGroup group; + + group.addAnimation(anim1); + group.addAnimation(anim2); + + group.start(); + \endcode + + In this example, \c anim1 and \c anim2 are two already set up + \l{QPropertyAnimation}s. + + \sa QAnimationGroup, QAbstractAnimation, {The Animation Framework} */ #ifndef QT_NO_ANIMATION -- cgit v0.12 From 8cd40802bd50bc02485738e49938eda3a1e45c1d Mon Sep 17 00:00:00 2001 From: Geir Vattekar Date: Mon, 11 May 2009 14:23:15 +0200 Subject: Doc: Work on QPauseAnimation --- src/corelib/animation/qpauseanimation.cpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/corelib/animation/qpauseanimation.cpp b/src/corelib/animation/qpauseanimation.cpp index 30ea92c..685fa98 100644 --- a/src/corelib/animation/qpauseanimation.cpp +++ b/src/corelib/animation/qpauseanimation.cpp @@ -45,6 +45,22 @@ \since 4.5 \ingroup animation \preliminary + + If you wish to introduce a delay between animations in a + QSequentialAnimationGroup, you can insert a QPauseAnimation. This + class does not animate anything, but does not + \l{QAbstractAnimation::finished()}{finish} before a specified + number of milliseconds have elapsed from when it was started. You + specify the duration of the pause in the constructor. It can also + be set directly with setDuration(). + + It is not necessary to construct a QPauseAnimation yourself. + QSequentialAnimationGroup provides the convenience functions + \l{QSequentialAnimationGroup::}{addPause()} and + \l{QSequentialAnimationGroup::}{insertPauseAt()}. These functions + simply take the number of milliseconds the pause should last. + + \sa QSequentialAnimationGroup */ #ifndef QT_NO_ANIMATION -- cgit v0.12 From 65165a0a3c32843486ebd8901b6cb825d9403766 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Mon, 11 May 2009 16:19:27 +0200 Subject: Make selecting plugins more user friendly and platform independent Using *.dll was just a placeholder here. The code is mostly copied from the Plug&Paint example, and goes through all the plugins to find the compatible ones with some hacks to find out the application directory on windows and mac. --- examples/statemachine/errorstate/mainwindow.cpp | 76 +++++++++++++++++----- .../errorstateplugins/random_ai/random_ai_plugin.h | 2 + .../errorstateplugins/seek_ai/seek_ai.h | 2 + .../errorstateplugins/spin_ai/spin_ai.h | 2 + .../spin_ai_with_error/spin_ai_with_error.h | 2 + 5 files changed, 66 insertions(+), 18 deletions(-) diff --git a/examples/statemachine/errorstate/mainwindow.cpp b/examples/statemachine/errorstate/mainwindow.cpp index 39b8663..07719bc 100644 --- a/examples/statemachine/errorstate/mainwindow.cpp +++ b/examples/statemachine/errorstate/mainwindow.cpp @@ -12,6 +12,9 @@ #include #include #include +#include +#include +#include MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), m_scene(0), m_machine(0), m_runningState(0), m_started(false) @@ -193,25 +196,62 @@ void MainWindow::addTank() { Q_ASSERT(!m_spawns.isEmpty()); - QString fileName = QFileDialog::getOpenFileName(this, "Select plugin file", "plugins/", "*.dll"); - QPluginLoader loader(fileName); + QDir pluginsDir(qApp->applicationDirPath()); +#if defined(Q_OS_WIN) + if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release") + pluginsDir.cdUp(); +#elif defined(Q_OS_MAC) + if (pluginsDir.dirName() == "MacOS") { + pluginsDir.cdUp(); + pluginsDir.cdUp(); + pluginsDir.cdUp(); + } +#endif + + pluginsDir.cd("plugins"); + + QStringList itemNames; + QList items; + foreach (QString fileName, pluginsDir.entryList(QDir::Files)) { + QPluginLoader loader(pluginsDir.absoluteFilePath(fileName)); + QObject *possiblePlugin = loader.instance(); + if (Plugin *plugin = qobject_cast(possiblePlugin)) { + QString objectName = possiblePlugin->objectName(); + if (objectName.isEmpty()) + objectName = fileName; + + itemNames.append(objectName); + items.append(plugin); + } + } + + if (items.isEmpty()) { + QMessageBox::information(this, "No tank types found", "Please build the errorstateplugins directory"); + return; + } + + bool ok; + QString selectedName = QInputDialog::getItem(this, "Select a tank type", "Tank types", + itemNames, 0, false, &ok); - Plugin *plugin = qobject_cast(loader.instance()); - if (plugin != 0) { - TankItem *tankItem = m_spawns.takeLast(); - m_scene->addItem(tankItem); - connect(tankItem, SIGNAL(cannonFired()), this, SLOT(addRocket())); - if (m_spawns.isEmpty()) - emit mapFull(); - - QState *region = new QState(m_runningState); - QState *pluginState = plugin->create(region, tankItem); - region->setInitialState(pluginState); - - // If the plugin has an error it is disabled - QState *errorState = new QState(region); - errorState->assignProperty(tankItem, "enabled", false); - pluginState->setErrorState(errorState); + if (ok && !selectedName.isEmpty()) { + int idx = itemNames.indexOf(selectedName); + if (Plugin *plugin = idx >= 0 ? items.at(idx) : 0) { + TankItem *tankItem = m_spawns.takeLast(); + m_scene->addItem(tankItem); + connect(tankItem, SIGNAL(cannonFired()), this, SLOT(addRocket())); + if (m_spawns.isEmpty()) + emit mapFull(); + + QState *region = new QState(m_runningState); + QState *pluginState = plugin->create(region, tankItem); + region->setInitialState(pluginState); + + // If the plugin has an error it is disabled + QState *errorState = new QState(region); + errorState->assignProperty(tankItem, "enabled", false); + pluginState->setErrorState(errorState); + } } } diff --git a/examples/statemachine/errorstateplugins/random_ai/random_ai_plugin.h b/examples/statemachine/errorstateplugins/random_ai/random_ai_plugin.h index 3db464b..10e6f48 100644 --- a/examples/statemachine/errorstateplugins/random_ai/random_ai_plugin.h +++ b/examples/statemachine/errorstateplugins/random_ai/random_ai_plugin.h @@ -56,6 +56,8 @@ class RandomAiPlugin: public QObject, public Plugin Q_OBJECT Q_INTERFACES(Plugin) public: + RandomAiPlugin() { setObjectName("Random"); } + virtual QState *create(QState *parentState, QObject *tank); }; diff --git a/examples/statemachine/errorstateplugins/seek_ai/seek_ai.h b/examples/statemachine/errorstateplugins/seek_ai/seek_ai.h index 34d203e..a1b5749 100644 --- a/examples/statemachine/errorstateplugins/seek_ai/seek_ai.h +++ b/examples/statemachine/errorstateplugins/seek_ai/seek_ai.h @@ -196,6 +196,8 @@ class SeekAi: public QObject, public Plugin Q_OBJECT Q_INTERFACES(Plugin) public: + SeekAi() { setObjectName("Seek and destroy"); } + virtual QState *create(QState *parentState, QObject *tank); }; diff --git a/examples/statemachine/errorstateplugins/spin_ai/spin_ai.h b/examples/statemachine/errorstateplugins/spin_ai/spin_ai.h index 4b4629c..6e220ed 100644 --- a/examples/statemachine/errorstateplugins/spin_ai/spin_ai.h +++ b/examples/statemachine/errorstateplugins/spin_ai/spin_ai.h @@ -38,6 +38,8 @@ class SpinAi: public QObject, public Plugin Q_OBJECT Q_INTERFACES(Plugin) public: + SpinAi() { setObjectName("Spin and destroy"); } + virtual QState *create(QState *parentState, QObject *tank); }; diff --git a/examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.h b/examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.h index 9a96a8b..d520455 100644 --- a/examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.h +++ b/examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.h @@ -38,6 +38,8 @@ class SpinAiWithError: public QObject, public Plugin Q_OBJECT Q_INTERFACES(Plugin) public: + SpinAiWithError() { setObjectName("Spin and destroy with runtime error in state machine"); } + virtual QState *create(QState *parentState, QObject *tank); }; -- cgit v0.12 From 45c063d91d6a682821ab337cdc8839cc2229f6ef Mon Sep 17 00:00:00 2001 From: Geir Vattekar Date: Mon, 11 May 2009 16:51:37 +0200 Subject: Doc: Assign state machine overview to architecture group (get it in the overview list) --- doc/src/statemachine.qdoc | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/src/statemachine.qdoc b/doc/src/statemachine.qdoc index 60ae815..abc089d 100644 --- a/doc/src/statemachine.qdoc +++ b/doc/src/statemachine.qdoc @@ -13,6 +13,7 @@ \page statemachine-api.html \title The State Machine Framework \brief An overview of the State Machine framework for constructing and executing state graphs. + \ingroup architecture \tableofcontents -- cgit v0.12 From e51a4a22cdeb200d2851c96a82b4c282ec5dd2d3 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 12 May 2009 11:49:15 +0200 Subject: Avoid warnings in assignPropertyWithAnimation test They added a warning when you animate a non-existent property, so we make sure the properties are defined. --- tests/auto/qstatemachine/tst_qstatemachine.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/auto/qstatemachine/tst_qstatemachine.cpp b/tests/auto/qstatemachine/tst_qstatemachine.cpp index dbc67d1..df19f26 100644 --- a/tests/auto/qstatemachine/tst_qstatemachine.cpp +++ b/tests/auto/qstatemachine/tst_qstatemachine.cpp @@ -1253,6 +1253,8 @@ void tst_QStateMachine::assignPropertyWithAnimation() { QStateMachine machine; QObject obj; + obj.setProperty("foo", 321); + obj.setProperty("bar", 654); QState *s1 = new QState(machine.rootState()); s1->assignProperty(&obj, "foo", 123); QState *s2 = new QState(machine.rootState()); @@ -1276,6 +1278,8 @@ void tst_QStateMachine::assignPropertyWithAnimation() { QStateMachine machine; QObject obj; + obj.setProperty("foo", 321); + obj.setProperty("bar", 654); QState *s1 = new QState(machine.rootState()); s1->assignProperty(&obj, "foo", 123); QState *s2 = new QState(machine.rootState()); @@ -1302,6 +1306,8 @@ void tst_QStateMachine::assignPropertyWithAnimation() { QStateMachine machine; QObject obj; + obj.setProperty("foo", 321); + obj.setProperty("bar", 654); QState *s1 = new QState(machine.rootState()); s1->assignProperty(&obj, "foo", 123); s1->assignProperty(&obj, "bar", 321); @@ -1329,6 +1335,8 @@ void tst_QStateMachine::assignPropertyWithAnimation() { QStateMachine machine; QObject obj; + obj.setProperty("foo", 321); + obj.setProperty("bar", 654); QState *s1 = new QState(machine.rootState()); QCOMPARE(s1->childMode(), QState::ExclusiveStates); QCOMPARE(s1->initialState(), (QAbstractState*)0); -- cgit v0.12 From 5e12abc10ced54cf169773be78d19fc767b2439f Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 12 May 2009 12:19:53 +0200 Subject: Fixes crash when calling QState::setErrorState() with null pointer When the state is null, it isn't the root state. --- src/corelib/statemachine/qstate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/corelib/statemachine/qstate.cpp b/src/corelib/statemachine/qstate.cpp index 5f61865..3a3bfc3 100644 --- a/src/corelib/statemachine/qstate.cpp +++ b/src/corelib/statemachine/qstate.cpp @@ -285,7 +285,7 @@ void QState::setErrorState(QAbstractState *state) "to a different state machine"); return; } - if (state->machine() != 0 && state->machine()->rootState() == state) { + if (state != 0 && state->machine() != 0 && state->machine()->rootState() == state) { qWarning("QStateMachine::setErrorState: root state cannot be error state"); return; } -- cgit v0.12 From 0b01202d770a5847af00dc6ebba0d2a75f1a271b Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 12 May 2009 12:23:31 +0200 Subject: Kill warning in removeDefaultAnimation() test Warning about animating non-existent properties. --- tests/auto/qstatemachine/tst_qstatemachine.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/auto/qstatemachine/tst_qstatemachine.cpp b/tests/auto/qstatemachine/tst_qstatemachine.cpp index df19f26..d24a945 100644 --- a/tests/auto/qstatemachine/tst_qstatemachine.cpp +++ b/tests/auto/qstatemachine/tst_qstatemachine.cpp @@ -2715,9 +2715,12 @@ void tst_QStateMachine::removeDefaultAnimation() { QStateMachine machine; + QObject propertyHolder; + propertyHolder.setProperty("foo", 0); + QCOMPARE(machine.defaultAnimations().size(), 0); - QPropertyAnimation *anim = new QPropertyAnimation(this, "foo"); + QPropertyAnimation *anim = new QPropertyAnimation(&propertyHolder, "foo"); machine.addDefaultAnimation(anim); @@ -2730,7 +2733,7 @@ void tst_QStateMachine::removeDefaultAnimation() machine.addDefaultAnimation(anim); - QPropertyAnimation *anim2 = new QPropertyAnimation(this, "foo"); + QPropertyAnimation *anim2 = new QPropertyAnimation(&propertyHolder, "foo"); machine.addDefaultAnimation(anim2); QCOMPARE(machine.defaultAnimations().size(), 2); -- cgit v0.12 From 0b5f851adea024df6a81bb9dc69c51e000c9253e Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 12 May 2009 12:49:49 +0200 Subject: Don't interrupt test before animation finishes We're testing if both animations actually run and finish, so we need to make sure one of the polished signals is emitted last, and then react to this. I've done this by setting the duration of the animation which animated the property set by s2Child, so that it's sufficient to listen to this polished signal. --- tests/auto/qstatemachine/tst_qstatemachine.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/auto/qstatemachine/tst_qstatemachine.cpp b/tests/auto/qstatemachine/tst_qstatemachine.cpp index d24a945..9058cb6 100644 --- a/tests/auto/qstatemachine/tst_qstatemachine.cpp +++ b/tests/auto/qstatemachine/tst_qstatemachine.cpp @@ -2529,6 +2529,7 @@ void tst_QStateMachine::nestedTargetStateForAnimation() QAbstractTransition *at = s2Child->addTransition(new EventTransition(QEvent::User, s2Child2)); QPropertyAnimation *animation = new QPropertyAnimation(object, "bar", s2); + animation->setDuration(2000); connect(animation, SIGNAL(finished()), &counter, SLOT(slot())); at->addAnimation(animation); @@ -2541,10 +2542,11 @@ void tst_QStateMachine::nestedTargetStateForAnimation() animation = new QPropertyAnimation(object, "bar", s2); connect(animation, SIGNAL(finished()), &counter, SLOT(slot())); at->addAnimation(animation); - + QState *s3 = new QState(machine.rootState()); + s2->addTransition(s2Child, SIGNAL(polished()), s3); + QObject::connect(s3, SIGNAL(entered()), QCoreApplication::instance(), SLOT(quit())); - s2->addTransition(s2, SIGNAL(polished()), s3); machine.setInitialState(s1); machine.start(); -- cgit v0.12 From 636f82be8db71c6f2ba815ca5b5d2e983476701b Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Tue, 12 May 2009 13:16:46 +0200 Subject: redo statecharts in proper UML notation, expand documentation --- doc/src/images/statemachine-button-history.png | Bin 91677 -> 8493 bytes doc/src/images/statemachine-button-nested.png | Bin 73774 -> 7051 bytes doc/src/images/statemachine-button.png | Bin 32767 -> 4233 bytes doc/src/images/statemachine-customevents.png | Bin 0 -> 2544 bytes doc/src/images/statemachine-finished.png | Bin 37907 -> 5518 bytes doc/src/images/statemachine-nonparallel.png | Bin 68482 -> 5350 bytes doc/src/images/statemachine-parallel.png | Bin 99587 -> 8631 bytes doc/src/statemachine.qdoc | 172 +++++++++++++++++++++---- 8 files changed, 150 insertions(+), 22 deletions(-) create mode 100644 doc/src/images/statemachine-customevents.png diff --git a/doc/src/images/statemachine-button-history.png b/doc/src/images/statemachine-button-history.png index cd66478..7f51cae 100644 Binary files a/doc/src/images/statemachine-button-history.png and b/doc/src/images/statemachine-button-history.png differ diff --git a/doc/src/images/statemachine-button-nested.png b/doc/src/images/statemachine-button-nested.png index 60360d1..762ac14 100644 Binary files a/doc/src/images/statemachine-button-nested.png and b/doc/src/images/statemachine-button-nested.png differ diff --git a/doc/src/images/statemachine-button.png b/doc/src/images/statemachine-button.png index 75d9e53..10102bd 100644 Binary files a/doc/src/images/statemachine-button.png and b/doc/src/images/statemachine-button.png differ diff --git a/doc/src/images/statemachine-customevents.png b/doc/src/images/statemachine-customevents.png new file mode 100644 index 0000000..62a4222 Binary files /dev/null and b/doc/src/images/statemachine-customevents.png differ diff --git a/doc/src/images/statemachine-finished.png b/doc/src/images/statemachine-finished.png index 802621e..0ac081d 100644 Binary files a/doc/src/images/statemachine-finished.png and b/doc/src/images/statemachine-finished.png differ diff --git a/doc/src/images/statemachine-nonparallel.png b/doc/src/images/statemachine-nonparallel.png index 1fe60d8..f9850a7 100644 Binary files a/doc/src/images/statemachine-nonparallel.png and b/doc/src/images/statemachine-nonparallel.png differ diff --git a/doc/src/images/statemachine-parallel.png b/doc/src/images/statemachine-parallel.png index 1868792..a65c297 100644 Binary files a/doc/src/images/statemachine-parallel.png and b/doc/src/images/statemachine-parallel.png differ diff --git a/doc/src/statemachine.qdoc b/doc/src/statemachine.qdoc index 60ae815..dff6731 100644 --- a/doc/src/statemachine.qdoc +++ b/doc/src/statemachine.qdoc @@ -33,10 +33,10 @@ The State Machine framework provides an API and execution model that can be used to effectively embed the elements and semantics of statecharts in Qt - applications. The framework integrates tightly with Qt's existing event - system and meta-object system; for example, transitions between states can - be triggered by signals, and states can be configured to set properties and - invoke methods on QObjects. + applications. The framework integrates tightly with Qt's meta-object system; + for example, transitions between states can be triggered by signals, and + states can be configured to set properties and invoke methods on QObjects. + Qt's event system is used to drive the state machines. \section1 A Simple State Machine @@ -52,34 +52,49 @@ \endomit The following snippet shows the code needed to create such a state machine. + First, we create the state machine and states: \code QStateMachine machine; QState *s1 = new QState(); QState *s2 = new QState(); QState *s3 = new QState(); + \endcode + + Then, we create the transitions by using the QState::addTransition() + function: + \code s1->addTransition(button, SIGNAL(clicked()), s2); s2->addTransition(button, SIGNAL(clicked()), s3); s3->addTransition(button, SIGNAL(clicked()), s1); + \endcode + Next, we add the states to the machine and set the machine's initial state: + + \code machine.addState(s1); machine.addState(s2); machine.addState(s3); machine.setInitialState(s1); + \endcode + Finally, we start the state machine: + + \code machine.start(); \endcode - Once the state machine has been set up, you need to start it by calling - QStateMachine::start(). The state machine executes asynchronously, i.e. it - becomes part of your application's event loop. + The state machine executes asynchronously, i.e. it becomes part of your + application's event loop. + + \section1 Doing Useful Work on State Entry and Exit - The above state machine is perfectly fine, but it doesn't \e do anything; it - merely transitions from one state to another. The QState::assignProperty() - function can be used to have a state set a property of a QObject when the - state is entered. In the following snippet, the value that should be - assigned to a QLabel's text property is specified for each state: + The above state machine merely transitions from one state to another, it + doesn't perform any operations. The QState::assignProperty() function can be + used to have a state set a property of a QObject when the state is + entered. In the following snippet, the value that should be assigned to a + QLabel's text property is specified for each state: \code s1->assignProperty(label, "text", "In state s1"); @@ -90,20 +105,26 @@ When any of the states is entered, the label's text will be changed accordingly. - The QActionState::entered() signal is emitted when the state is entered. In the + The QState::entered() signal is emitted when the state is entered, and the + QState::exited() signal is emitted when the state is exited. In the following snippet, the button's showMaximized() slot will be called when - state \c s3 is entered: + state \c s3 is entered, and the button's showMinimized() slot will be called + when \c s3 is exited: \code QObject::connect(s3, SIGNAL(entered()), button, SLOT(showMaximized())); + QObject::connect(s3, SIGNAL(exited()), button, SLOT(showMinimized())); \endcode - \section1 Sharing Transitions By Grouping States + \section1 State Machines That Finish The state machine defined in the previous section never finishes. In order for a state machine to be able to finish, it needs to have a top-level \e - final state. When the state machine enters a top-level final state, the - machine will emit the finished() signal and halt. + final state (QFinalState object). When the state machine enters a top-level + final state, the machine will emit the QStateMachine::finished() signal and + halt. + + \section1 Sharing Transitions By Grouping States Assume we wanted the user to be able to quit the application at any time by clicking a Quit button. In order to achieve this, we need to create a final @@ -165,6 +186,9 @@ s12>addTransition(quitButton, SIGNAL(clicked()), s12); \endcode + A transition can have any state as its target, i.e. the target state does + not have to be on the same level in the state hierarchy as the source state. + \section1 Using History States to Save and Restore the Current State Imagine that we wanted to add an "interrupt" mechanism to the example @@ -251,10 +275,16 @@ QState *s12 = new QState(s1); \endcode + When a parallel state group is entered, all its child states will be + simultaneously entered. Transitions within the individual child states + operate normally. However, any of the child states may take a transition + outside the parent state. When this happens, the parent state and all of its + child states are exited. + \section1 Detecting that a Composite State has Finished - A child state can be final; when a final child state is entered, the parent - state emits the QState::finished() signal. + A child state can be final (a QFinalState object); when a final child state + is entered, the parent state emits the QState::finished() signal. \img statemachine-finished.png \omit @@ -263,7 +293,105 @@ This is useful when you want to hide the internal details of a state; i.e. the only thing the outside world should be able to do is enter the - state, and get a notification when the state has finished (i.e. when a final - child state has been entered). + state, and get a notification when the state has completed its work. + + For parallel state groups, the QState::finished() signal is emitted when \e + all the child states have entered final states. + + \section1 Events, Transitions and Guards + + A QStateMachine runs its own event loop. For signal transitions + (QSignalTransition objects), QStateMachine automatically posts a + QSignalEvent to itself when it intercepts the corresponding signal; + similarly, for QObject event transitions (QEventTransition objects) a + QWrappedEvent is posted. + + You can post your own events to the state machine using + QStateMachine::postEvent(). + + When posting a custom event to the state machine, you typically also have + one or more custom transitions that can be triggered from events of that + type. To create such a transition, you subclass QAbstractTransition and + reimplement QAbstractTransition::eventTest(), where you check if an event + matches your event type (and optionally other criteria, e.g. attributes of + the event object). + + Here we define our own custom event type, \c StringEvent, for posting + strings to the state machine: + + \code + struct StringEvent : public QEvent + { + StringEvent(const QString &val) + : QEvent(QEvent::Type(QEvent::User+1)), + value(val) {} + + QString value; + }; + \endcode + + Next, we define a transition that only triggers when the event's string + matches a particular string (a \e guarded transition): - */ + \code + 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(e); + return (m_value == se->value); + } + + virtual void onTransition(QEvent *) {} + + private: + QString m_value; + }; + \endcode + + In the eventTest() reimplementation, we first check if the event type is the + desired one; if so, we cast the event to a StringEvent and perform the + string comparison. + + The following is a statechart that uses the custom event and transition: + + \img statemachine-customevents.png + \omit + \caption This is a caption + \endomit + + Here's what the implementation of the statechart looks like: + + \code + QStateMachine machine; + QState *s1 = new QState(); + QState *s2 = new QState(); + QFinalState *done = new QFinalState(); + + StringTransition *t1 = new StringTransition("Hello"); + t1->setTargetState(s2); + s1->addTransition(t1); + StringTransition *t2 = new StringTransition("world"); + t2->setTargetState(done); + s2->addTransition(t2); + + machine.addState(s1); + machine.addState(s2); + machine.addState(done); + machine.setInitialState(s1); + \endcode + + Once the machine is started, we can post events to it. + + \code + machine.postEvent(new StringEvent("Hello")); + machine.postEvent(new StringEvent("world")); + \endcode +*/ -- cgit v0.12 From a62575f61db7d4b34799205de1956600c8e9c47e Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Tue, 12 May 2009 14:27:25 +0200 Subject: improve the docs for the trafficlight example --- doc/src/examples/trafficlight.qdoc | 43 +++++++++++++++---------- doc/src/images/trafficlight-example1.png | Bin 0 -> 3694 bytes doc/src/images/trafficlight-example2.png | Bin 0 -> 7257 bytes examples/statemachine/trafficlight/main.cpp | 47 +++++++++++++--------------- 4 files changed, 49 insertions(+), 41 deletions(-) create mode 100644 doc/src/images/trafficlight-example1.png create mode 100644 doc/src/images/trafficlight-example2.png diff --git a/doc/src/examples/trafficlight.qdoc b/doc/src/examples/trafficlight.qdoc index 16ee8ad..50cab035 100644 --- a/doc/src/examples/trafficlight.qdoc +++ b/doc/src/examples/trafficlight.qdoc @@ -26,30 +26,41 @@ \snippet examples/statemachine/trafficlight/main.cpp 0 The LightWidget class represents a single light of the traffic light. It - provides a setOn() function to turn the light on or off. It paints itself - in the color that's passed to the constructor. + provides an \c on property and two slots, turnOn() and turnOff(), to turn + the light on and off, respectively. The widget paints itself in the color + that's passed to the constructor. - \snippet examples/statemachine/trafficlight/main.cpp 2 + \snippet examples/statemachine/trafficlight/main.cpp 1 The TrafficLightWidget class represents the visual part of the traffic - light; it's a widget that contains three lights, and provides accessor - functions for these. + light; it's a widget that contains three lights arranged vertically, and + provides accessor functions for these. - \snippet examples/statemachine/trafficlight/main.cpp 1 + \snippet examples/statemachine/trafficlight/main.cpp 2 + + The createLightState() function creates a state that turns a light on when + the state is entered, and off when the state is exited. The state uses a + timer, and as we shall see the timeout is used to transition from one + LightState to another. Here is the statechart for the light state: - The LightState class represents a state that turns a light on when the - state is entered, and off when the state is exited. The class is a timer, - and as we shall see the timeout is used to transition from one LightState - to another. + \img trafficlight-example1.png + \omit + \caption This is a caption + \endomit \snippet examples/statemachine/trafficlight/main.cpp 3 - The TrafficLight class combines the TrafficLightWidget with control flow - based on the LightState class. The state graph has four states: - red-to-yellow, yellow-to-green, green-to-yellow and yellow-to-red. The - initial state is red-to-yellow; when the state's timer times out, the - state machine transitions to yellow-to-green. The same process repeats - through the other states. + The TrafficLight class combines the TrafficLightWidget with a state + machine. The state graph has four states: red-to-yellow, yellow-to-green, + green-to-yellow and yellow-to-red. The initial state is red-to-yellow; + when the state's timer times out, the state machine transitions to + yellow-to-green. The same process repeats through the other states. + This is what the statechart looks like: + + \img trafficlight-example2.png + \omit + \caption This is a caption + \endomit \snippet examples/statemachine/trafficlight/main.cpp 4 diff --git a/doc/src/images/trafficlight-example1.png b/doc/src/images/trafficlight-example1.png new file mode 100644 index 0000000..ec8c7ff Binary files /dev/null and b/doc/src/images/trafficlight-example1.png differ diff --git a/doc/src/images/trafficlight-example2.png b/doc/src/images/trafficlight-example2.png new file mode 100644 index 0000000..a12e4db Binary files /dev/null and b/doc/src/images/trafficlight-example2.png differ diff --git a/examples/statemachine/trafficlight/main.cpp b/examples/statemachine/trafficlight/main.cpp index 3c47a51..8a46fff 100644 --- a/examples/statemachine/trafficlight/main.cpp +++ b/examples/statemachine/trafficlight/main.cpp @@ -87,27 +87,6 @@ private: //! [0] //! [1] -class LightState : public QState -{ -public: - LightState(LightWidget *light, int duration, QState *parent = 0) - : QState(parent) - { - QTimer *timer = new QTimer(this); - timer->setInterval(duration); - timer->setSingleShot(true); - QState *timing = new QState(this); - QObject::connect(timing, SIGNAL(entered()), light, SLOT(turnOn())); - QObject::connect(timing, SIGNAL(entered()), timer, SLOT(start())); - QObject::connect(timing, SIGNAL(exited()), light, SLOT(turnOff())); - QFinalState *done = new QFinalState(this); - timing->addTransition(timer, SIGNAL(timeout()), done); - setInitialState(timing); - } -}; -//! [1] - -//! [2] class TrafficLightWidget : public QWidget { public: @@ -139,6 +118,24 @@ private: LightWidget *m_yellow; LightWidget *m_green; }; +//! [1] + +//! [2] +QState *createLightState(LightWidget *light, int duration, QState *parent = 0) +{ + QState *lightState = new QState(parent); + QTimer *timer = new QTimer(lightState); + timer->setInterval(duration); + timer->setSingleShot(true); + QState *timing = new QState(lightState); + QObject::connect(timing, SIGNAL(entered()), light, SLOT(turnOn())); + QObject::connect(timing, SIGNAL(entered()), timer, SLOT(start())); + QObject::connect(timing, SIGNAL(exited()), light, SLOT(turnOff())); + QFinalState *done = new QFinalState(lightState); + timing->addTransition(timer, SIGNAL(timeout()), done); + lightState->setInitialState(timing); + return lightState; +} //! [2] //! [3] @@ -154,15 +151,15 @@ public: vbox->setMargin(0); QStateMachine *machine = new QStateMachine(this); - LightState *redGoingYellow = new LightState(widget->redLight(), 3000); + QState *redGoingYellow = createLightState(widget->redLight(), 3000); redGoingYellow->setObjectName("redGoingYellow"); - LightState *yellowGoingGreen = new LightState(widget->yellowLight(), 1000); + QState *yellowGoingGreen = createLightState(widget->yellowLight(), 1000); yellowGoingGreen->setObjectName("yellowGoingGreen"); redGoingYellow->addTransition(redGoingYellow, SIGNAL(finished()), yellowGoingGreen); - LightState *greenGoingYellow = new LightState(widget->greenLight(), 3000); + QState *greenGoingYellow = createLightState(widget->greenLight(), 3000); greenGoingYellow->setObjectName("greenGoingYellow"); yellowGoingGreen->addTransition(yellowGoingGreen, SIGNAL(finished()), greenGoingYellow); - LightState *yellowGoingRed = new LightState(widget->yellowLight(), 1000); + QState *yellowGoingRed = createLightState(widget->yellowLight(), 1000); yellowGoingRed->setObjectName("yellowGoingRed"); greenGoingYellow->addTransition(greenGoingYellow, SIGNAL(finished()), yellowGoingRed); yellowGoingRed->addTransition(yellowGoingRed, SIGNAL(finished()), redGoingYellow); -- cgit v0.12 From 5d6f17acc0995558f919ea4c2e974530df7b8a08 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Tue, 12 May 2009 14:29:02 +0200 Subject: update license headers --- doc/src/examples/trafficlight.qdoc | 36 +++++++++++++++++++++++++++++++++--- doc/src/statemachine.qdoc | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 66 insertions(+), 6 deletions(-) diff --git a/doc/src/examples/trafficlight.qdoc b/doc/src/examples/trafficlight.qdoc index 50cab035..ae62127 100644 --- a/doc/src/examples/trafficlight.qdoc +++ b/doc/src/examples/trafficlight.qdoc @@ -1,11 +1,41 @@ /**************************************************************************** ** -** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Qt Software Information (qt-info@nokia.com) ** -** This file is part of the $MODULE$ of the Qt Toolkit. +** This file is part of the documentation of the Qt Toolkit. ** -** $TROLLTECH_DUAL_LICENSE$ +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ ** ****************************************************************************/ diff --git a/doc/src/statemachine.qdoc b/doc/src/statemachine.qdoc index 16eae39..18c5307 100644 --- a/doc/src/statemachine.qdoc +++ b/doc/src/statemachine.qdoc @@ -1,11 +1,41 @@ /**************************************************************************** ** -** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Qt Software Information (qt-info@nokia.com) ** -** This file is part of the $MODULE$ of the Qt Toolkit. +** This file is part of the documentation of the Qt Toolkit. ** -** $TROLLTECH_DUAL_LICENSE$ +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ ** ****************************************************************************/ -- cgit v0.12 From c6add575d50ee30b19580fc2c1ebda5316a2f51b Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 12 May 2009 14:32:04 +0200 Subject: doc: Add documentation for animations, restore policy and polished signal --- doc/src/statemachine.qdoc | 182 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/doc/src/statemachine.qdoc b/doc/src/statemachine.qdoc index 18c5307..27bd4f8 100644 --- a/doc/src/statemachine.qdoc +++ b/doc/src/statemachine.qdoc @@ -425,4 +425,186 @@ machine.postEvent(new StringEvent("Hello")); machine.postEvent(new StringEvent("world")); \endcode + + \section1 Using Restore Policy To Automatically Restore Properties + + In some state machines it can be useful to focus the attention on assigning properties in states, + not on restoring them when the state is no longer active. If you know that a property should + always be restored to its initial value when the machine enters a state that does not explicitly + give the property a value, you can set the global restore policy to + QStateMachine::RestoreProperties. + + \code + QStateMachine machine; + machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties); + \endcode + + When this restore policy is set, the machine will automatically restore all properties. If it + enters a state where a given property is not set, it will first search the hierarchy of ancestors + to see if the property is defined there. If it is, the property will be restored to the value + defined by the closest ancestor. If not, it will be restored to its initial value (i.e. the + value of the property before any property assignments in states were executed.) + + Take the following code: + \code + QStateMachine machine; + machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties); + + QState *s1 = new QState(); + s1->assignProperty(object, "fooBar", 1.0); + machine.addState(s1); + machine.setInitialState(s1); + + QState *s2 = new QState(); + machine.addState(s2); + \endcode + + Lets say the property \c fooBar is 0.0 when the machine starts. When the machine is in state + \c s1, the property will be 1.0, since the state explicitly assigns this value to it. When the + machine is in state \c s2, no value is explicitly defined for the property, so it will implicitly + be restored to 0.0. + + If we are using nested states, the parent defines a value for the property which is inherited by + all descendants that do not explicitly assign a value to the property. + \code + QStateMachine machine; + machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties); + + QState *s1 = new QState(); + s1->assignProperty(object, "fooBar", 1.0); + machine.addState(s1); + machine.setInitialState(s1); + + QState *s2 = new QState(s1); + s2->assignProperty(object, "fooBar", 2.0); + s1->setInitialState(s2); + + QState *s3 = new QState(s1); + \endcode + + Here \c s1 has two children: \c s2 and \c s3. When \c s2 is entered, the property \c fooBar + will have the value 2.0, since this is explicitly defined for the state. When the machine is in + state \c s3, no value is defined for the state, but \c s1 defines the property to be 1.0, so this + is the value that will be assigned to \c fooBar. + + \section1 Animating Property Assignments + + The State Machine API connects with the Animation API in Qt to allow automatically animating + properties as they are assigned in states. + + Say we have the following code: + \code + QState *s1 = new QState(); + QState *s2 = new QState(); + + s1->assignProperty(button, "geometry", QRectF(0, 0, 50, 50)); + s2->assignProperty(button, "geometry", QRectF(0, 0, 100, 100)); + + s1->addTransition(button, SIGNAL(clicked()), s2); + \endcode + + Here we define two states of a user interface. In \c s1 the \c button is small, and in \c s2 + it is bigger. If we click the button to transition from \c s1 to \c s2, the geometry of the button + will be set immediately when a given state has been entered. If we want the transition to be + smooth, however, all we need to do is make a QPropertyAnimation and add this to the transition + object. + + \code + QState *s1 = new QState(); + QState *s2 = new QState(); + + s1->assignProperty(button, "geometry", QRectF(0, 0, 50, 50)); + s2->assignProperty(button, "geometry", QRectF(0, 0, 100, 100)); + + QSignalTransition *transition = s1->addTransition(button, SIGNAL(clicked()), s2); + transition->addAnimation(new QPropertyAnimation(button, "geometry")); + \endcode + + Adding an animation for the property in question means that the property assignment will no + longer take immediate effect when the state has been entered. Instead, the animation will start + playing when the state has been entered and smoothly animate the property assignment. Since we + do not set the start value or end value of the animation, these will be set implicitly. The + start value of the animation will be the property's current value when the animation starts, and + the end value will be set based on the property assignments defined for the state. + + If the global restore policy of the state machine is set to QStateMachine::RestoreProperties, + it is possible to also add animations for the property restorations. + + \section1 Detecting That All Properties Have Been Set In A State + + When animations are used to assign properties, a state no longer defines the exact values that a + property will have when the machine is in the given state. While the animation is running, the + property can potentially have any value, depending on the animation. + + In some cases, it can be useful to be able to detect when the property has actually been assigned + the value defined by a state. For this, we can use the state's polished() signal. + \code + QState *s1 = new QState(); + s1->assignProperty(button, "geometry", QRectF(0, 0, 50, 50)); + + QState *s2 = new QState(); + + s1->addTransition(s1, SIGNAL(polished()), s2); + \endcode + + The machine will be in state \c s1 until the \c geometry property has been set. Then it will + immediately transition into \c s2. If the transition into \c s1 has an animation for the \c + geometry property, then the machine will stay in \c s1 until the animation has finished. If there + is no animation, it will simply set the property and immediately enter state \c s2. + + Either way, when the machine is in state \c s2, the property \c geometry has been assigned the + defined value. + + If the global restore policy is set to QStateMachine::RestoreProperties, the state will not emit + the polished() signal until these have been executed as well. + + \section1 What happens if a state is exited before the animation has finished + + If a state has property assignments, and the transition into the state has animations for the + properties, the state can potentially be exited before the properties have been assigned to the + values defines by the state. This is true in particular when there are transitions out from the + state that do not depend on the state being polished, as described in the previous section. + + The State Machine API guarantees that a property assigned by the state machine either: + \list + \o Has a value explicitly assigned to the property. + \o Is currently being animated into a value explicitly assigned to the property. + \endlist + + When a state is exited prior to the animation finishing, the behavior of the state machine depends + on the target state of the transition. If the target state explicitly assigns a value to the + property, no additional action will be taken. The property will be assigned the value defined by + the target state. + + If the target state does not assign any value to the property, there are two + options: By default, the property will be assigned the value defined by the state it is leaving + (the value it would have been assigned if the animation had been permitted to finish playing.) If + a global restore policy is set, however, this will take precedence, and the property will be + restored as usual. + + \section1 Default Animations + + As described earlier, you can add animations to transitions to make sure property assignments + in the target state are animated. If you want a specific animation to be used for a given property + regardless of which transition is taken, you can add it as a default animation to the state + machine. This is in particular useful when the properties assigned (or restored) by specific + states is not known when the machine is constructed. + + \code + QState *s1 = new QState(); + QState *s2 = new QState(); + + s2->assignProperty(object, "fooBar", 2.0); + s1->addTransition(s2); + + QStateMachine machine; + machine.setInitialState(s1); + machine.addDefaultAnimation(new QPropertyAnimation(object, "fooBar")); + \endcode + + When the machine is in state \c s2, the machine will play the default animation for the + property \c fooBar since this property is assigned by \c s2. + + Note that animations explicitly set on transitions will take precedence over any default + animation for the given property. */ -- cgit v0.12 From e863e3195e90db89c29603508e6857a5cc874ca4 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Tue, 12 May 2009 15:51:39 +0200 Subject: document the statemachine/pingpong example --- doc/src/examples.qdoc | 1 + doc/src/examples/pingpong.qdoc | 107 ++++++++++++++++++++++++++++++++ doc/src/images/pingpong-example.png | Bin 0 -> 7843 bytes examples/statemachine/pingpong/main.cpp | 14 +++++ 4 files changed, 122 insertions(+) create mode 100644 doc/src/examples/pingpong.qdoc create mode 100644 doc/src/images/pingpong-example.png diff --git a/doc/src/examples.qdoc b/doc/src/examples.qdoc index 06d7727..4bedcde 100644 --- a/doc/src/examples.qdoc +++ b/doc/src/examples.qdoc @@ -312,6 +312,7 @@ \list \o \l{statemachine/trafficlight}{Traffic Light}\raisedaster + \o \l{statemachine/pingpong}{Ping Pong States}\raisedaster \endlist \section1 Threads diff --git a/doc/src/examples/pingpong.qdoc b/doc/src/examples/pingpong.qdoc new file mode 100644 index 0000000..040e429 --- /dev/null +++ b/doc/src/examples/pingpong.qdoc @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example statemachine/pingpong + \title Ping Pong States Example + + The Ping Pong States example shows how to use parallel states together + with custom events and transitions in \l{The State Machine Framework}. + + This example implements a statechart where two states communicate by + posting events to the state machine. The state chart looks as follows: + + \img pingpong-example.png + \omit + \caption This is a caption + \endomit + + The \c pinger and \c ponger states are parallel states, i.e. they are + entered simultaneously and will take transitions independently of + eachother. + + The \c pinger state will post the first \c ping event upon entry; the \c + ponger state will respond by posting a \c pong event; this will cause the + \c pinger state to post a new \c ping event; and so on. + + \snippet examples/statemachine/pingpong/main.cpp 0 + + Two custom events are defined, \c PingEvent and \c PongEvent. + + \snippet examples/statemachine/pingpong/main.cpp 1 + + The \c Pinger class defines a state that posts a \c PingEvent to the state + machine when the state is entered. + + \snippet examples/statemachine/pingpong/main.cpp 2 + + The \c PingTransition class defines a transition that is triggered by + events of type \c PingEvent, and that posts a \c PongEvent (with a delay + of 500 milliseconds) to the state machine when the transition is + triggered. + + \snippet examples/statemachine/pingpong/main.cpp 3 + + The \c PongTransition class defines a transition that is triggered by + events of type \c PongEvent, and that posts a \c PingEvent (with a delay + of 500 milliseconds) to the state machine when the transition is + triggered. + + \snippet examples/statemachine/pingpong/main.cpp 4 + + The main() function begins by creating a state machine and a parallel + state group. + + \snippet examples/statemachine/pingpong/main.cpp 5 + + Next, the \c pinger and \c ponger states are created, with the parallel + state group as their parent state. Note that the transitions are \e + targetless. When such a transition is triggered, the source state won't be + exited and re-entered; only the transition's onTransition() function will + be called, and the state machine's configuration will remain the same, + which is precisely what we want in this case. + + \snippet examples/statemachine/pingpong/main.cpp 6 + + Finally, the group is added to the state machine, the machine is started, + and the application event loop is entered. + + */ diff --git a/doc/src/images/pingpong-example.png b/doc/src/images/pingpong-example.png new file mode 100644 index 0000000..af707e4 Binary files /dev/null and b/doc/src/images/pingpong-example.png differ diff --git a/examples/statemachine/pingpong/main.cpp b/examples/statemachine/pingpong/main.cpp index eb8fd5d..db66bfd 100644 --- a/examples/statemachine/pingpong/main.cpp +++ b/examples/statemachine/pingpong/main.cpp @@ -47,6 +47,7 @@ #include #endif +//! [0] class PingEvent : public QEvent { public: @@ -60,7 +61,9 @@ public: PongEvent() : QEvent(QEvent::Type(QEvent::User+3)) {} }; +//! [0] +//! [1] class Pinger : public QState { public: @@ -74,7 +77,9 @@ protected: fprintf(stdout, "ping?\n"); } }; +//! [1] +//! [3] class PongTransition : public QAbstractTransition { public: @@ -90,7 +95,9 @@ protected: fprintf(stdout, "ping?\n"); } }; +//! [3] +//! [2] class PingTransition : public QAbstractTransition { public: @@ -106,7 +113,9 @@ protected: fprintf(stdout, "pong!\n"); } }; +//! [2] +//! [4] int main(int argc, char **argv) { QCoreApplication app(argc, argv); @@ -114,7 +123,9 @@ int main(int argc, char **argv) QStateMachine machine; QState *group = new QState(QState::ParallelStates); group->setObjectName("group"); +//! [4] +//! [5] Pinger *pinger = new Pinger(group); pinger->setObjectName("pinger"); pinger->addTransition(new PongTransition()); @@ -122,10 +133,13 @@ int main(int argc, char **argv) QState *ponger = new QState(group); ponger->setObjectName("ponger"); ponger->addTransition(new PingTransition()); +//! [5] +//! [6] machine.addState(group); machine.setInitialState(group); machine.start(); return app.exec(); } +//! [6] -- cgit v0.12 From 4ece5c8923e559643f79833146d02a3976021f63 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Tue, 12 May 2009 16:16:35 +0200 Subject: kill some simplistic/overlapping/under-developed examples --- .../statemachine/clockticking/clockticking.pro | 10 -- examples/statemachine/clockticking/main.cpp | 123 --------------------- examples/statemachine/composition/composition.pro | 7 -- examples/statemachine/composition/main.cpp | 104 ----------------- examples/statemachine/helloworld/helloworld.pro | 10 -- examples/statemachine/helloworld/main.cpp | 78 ------------- examples/statemachine/pauseandresume/main.cpp | 102 ----------------- .../statemachine/pauseandresume/pauseandresume.pro | 7 -- examples/statemachine/statemachine.pro | 4 - 9 files changed, 445 deletions(-) delete mode 100644 examples/statemachine/clockticking/clockticking.pro delete mode 100644 examples/statemachine/clockticking/main.cpp delete mode 100644 examples/statemachine/composition/composition.pro delete mode 100644 examples/statemachine/composition/main.cpp delete mode 100644 examples/statemachine/helloworld/helloworld.pro delete mode 100644 examples/statemachine/helloworld/main.cpp delete mode 100644 examples/statemachine/pauseandresume/main.cpp delete mode 100644 examples/statemachine/pauseandresume/pauseandresume.pro diff --git a/examples/statemachine/clockticking/clockticking.pro b/examples/statemachine/clockticking/clockticking.pro deleted file mode 100644 index bff9cb8..0000000 --- a/examples/statemachine/clockticking/clockticking.pro +++ /dev/null @@ -1,10 +0,0 @@ -QT = core -TEMPLATE = app -TARGET = -win32: CONFIG += console -mac:CONFIG -= app_bundle -DEPENDPATH += . -INCLUDEPATH += . - -# Input -SOURCES += main.cpp diff --git a/examples/statemachine/clockticking/main.cpp b/examples/statemachine/clockticking/main.cpp deleted file mode 100644 index ea8e692..0000000 --- a/examples/statemachine/clockticking/main.cpp +++ /dev/null @@ -1,123 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#include -#ifdef QT_STATEMACHINE_SOLUTION -#include -#include -#include -#endif - -class ClockEvent : public QEvent -{ -public: - ClockEvent() : QEvent(QEvent::Type(QEvent::User+2)) - {} -}; - -class ClockState : public QState -{ -public: - ClockState(QState *parent) - : QState(parent) {} - -protected: - virtual void onEntry(QEvent *) - { - fprintf(stdout, "ClockState entered; posting the initial tick\n"); - machine()->postEvent(new ClockEvent()); - } -}; - -class ClockTransition : public QAbstractTransition -{ -public: - ClockTransition() {} - -protected: - virtual bool eventTest(QEvent *e) const { - return (e->type() == QEvent::User+2); - } - virtual void onTransition(QEvent *) - { - fprintf(stdout, "ClockTransition triggered; posting another tick with a delay of 1 second\n"); - machine()->postEvent(new ClockEvent(), 1000); - } -}; - -class ClockListener : public QAbstractTransition -{ -public: - ClockListener() {} - -protected: - virtual bool eventTest(QEvent *e) const { - return (e->type() == QEvent::User+2); - } - virtual void onTransition(QEvent *) - { - fprintf(stdout, "ClockListener heard a tick!\n"); - } -}; - -int main(int argc, char **argv) -{ - QCoreApplication app(argc, argv); - - QStateMachine machine; - QState *group = new QState(QState::ParallelStates); - group->setObjectName("group"); - - ClockState *clock = new ClockState(group); - clock->setObjectName("clock"); - clock->addTransition(new ClockTransition()); - - QState *listener = new QState(group); - listener->setObjectName("listener"); - listener->addTransition(new ClockListener()); - - machine.addState(group); - machine.setInitialState(group); - machine.start(); - - return app.exec(); -} diff --git a/examples/statemachine/composition/composition.pro b/examples/statemachine/composition/composition.pro deleted file mode 100644 index 6a976cb..0000000 --- a/examples/statemachine/composition/composition.pro +++ /dev/null @@ -1,7 +0,0 @@ -TEMPLATE = app -TARGET = -DEPENDPATH += . -INCLUDEPATH += . - -# Input -SOURCES += main.cpp diff --git a/examples/statemachine/composition/main.cpp b/examples/statemachine/composition/main.cpp deleted file mode 100644 index 0afff66..0000000 --- a/examples/statemachine/composition/main.cpp +++ /dev/null @@ -1,104 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#ifdef QT_STATEMACHINE_SOLUTION -#include -#include -#include -#endif - -int main(int argc, char **argv) -{ - QApplication app(argc, argv); - QLabel label; - label.setAlignment(Qt::AlignCenter); - - QStateMachine machine; - - QState *s1 = new QState(); - s1->setObjectName("s1"); - s1->assignProperty(&label, "text", "In S1, hang on..."); - s1->assignProperty(&label, "geometry", QRect(100, 100, 200, 100)); - - QState *s1_timer = new QState(s1); - s1_timer->setObjectName("s1_timer"); - QTimer t1; - t1.setInterval(2000); - QObject::connect(s1_timer, SIGNAL(entered()), &t1, SLOT(start())); - QFinalState *s1_done = new QFinalState(s1); - s1_done->setObjectName("s1_done"); - s1_timer->addTransition(&t1, SIGNAL(timeout()), s1_done); - s1->setInitialState(s1_timer); - - QState *s2 = new QState(); - s2->setObjectName("s2"); - s2->assignProperty(&label, "text", "In S2, I'm gonna quit on you..."); - s2->assignProperty(&label, "geometry", QRect(300, 300, 300, 100)); -// s2->invokeMethodOnEntry(&label, "setNum", QList() << 123); -// s2->invokeMethodOnEntry(&label, "showMaximized"); - - QState *s2_timer = new QState(s2); - s2_timer->setObjectName("s2_timer"); - QTimer t2; - t2.setInterval(2000); - QObject::connect(s2_timer, SIGNAL(entered()), &t2, SLOT(start())); - QFinalState *s2_done = new QFinalState(s2); - s2_done->setObjectName("s2_done"); - s2_timer->addTransition(&t2, SIGNAL(timeout()), s2_done); - s2->setInitialState(s2_timer); - - s1->addTransition(s1, SIGNAL(finished()), s2); - - QFinalState *s3 = new QFinalState(); - s3->setObjectName("s3"); - s2->addTransition(s2, SIGNAL(finished()), s3); - - machine.addState(s1); - machine.addState(s2); - machine.addState(s3); - machine.setInitialState(s1); - QObject::connect(&machine, SIGNAL(finished()), &app, SLOT(quit())); - machine.start(); - - label.show(); - return app.exec(); -} diff --git a/examples/statemachine/helloworld/helloworld.pro b/examples/statemachine/helloworld/helloworld.pro deleted file mode 100644 index ac79117..0000000 --- a/examples/statemachine/helloworld/helloworld.pro +++ /dev/null @@ -1,10 +0,0 @@ -TEMPLATE = app -TARGET = -QT = core -win32: CONFIG += console -mac:CONFIG -= app_bundle -DEPENDPATH += . -INCLUDEPATH += . - -# Input -SOURCES += main.cpp diff --git a/examples/statemachine/helloworld/main.cpp b/examples/statemachine/helloworld/main.cpp deleted file mode 100644 index fbe34b5..0000000 --- a/examples/statemachine/helloworld/main.cpp +++ /dev/null @@ -1,78 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#ifdef QT_STATEMACHINE_SOLUTION -#include -#include -#include -#endif - -class S0 : public QState -{ -public: - S0(QState *parent = 0) - : QState(parent) {} - - virtual void onEntry(QEvent *) - { - fprintf(stdout, "Hello world!\n"); - } -}; - -int main(int argc, char **argv) -{ - QCoreApplication app(argc, argv); - - QStateMachine machine; - QState *s0 = new S0(); - QFinalState *s1 = new QFinalState(); - s0->addTransition(s1); - - machine.addState(s0); - machine.addState(s1); - machine.setInitialState(s0); - - QObject::connect(&machine, SIGNAL(finished()), QCoreApplication::instance(), SLOT(quit())); - machine.start(); - - return app.exec(); -} diff --git a/examples/statemachine/pauseandresume/main.cpp b/examples/statemachine/pauseandresume/main.cpp deleted file mode 100644 index 5bacb41..0000000 --- a/examples/statemachine/pauseandresume/main.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). -** Contact: Qt Software Information (qt-info@nokia.com) -** -** This file is part of the QtCore module of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:LGPL$ -** No Commercial Usage -** This file contains pre-release code and may not be distributed. -** You may use this file in accordance with the terms and conditions -** contained in the either Technology Preview License Agreement or the -** Beta Release License Agreement. -** -** GNU Lesser General Public License Usage -** Alternatively, this file may be used under the terms of the GNU Lesser -** General Public License version 2.1 as published by the Free Software -** Foundation and appearing in the file LICENSE.LGPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU Lesser General Public License version 2.1 requirements -** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. -** -** In addition, as a special exception, Nokia gives you certain -** additional rights. These rights are described in the Nokia Qt LGPL -** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this -** package. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3.0 as published by the Free Software -** Foundation and appearing in the file LICENSE.GPL included in the -** packaging of this file. Please review the following information to -** ensure the GNU General Public License version 3.0 requirements will be -** met: http://www.gnu.org/copyleft/gpl.html. -** -** If you are unsure which license is appropriate for your use, please -** contact the sales department at qt-sales@nokia.com. -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include -#ifdef QT_STATEMACHINE_SOLUTION -#include -#include -#include -#include -#endif - -class Window : public QWidget -{ -public: - Window(QWidget *parent = 0) - : QWidget(parent) - { - QPushButton *pb = new QPushButton("Go"); - QPushButton *pauseButton = new QPushButton("Pause"); - QPushButton *quitButton = new QPushButton("Quit"); - QVBoxLayout *vbox = new QVBoxLayout(this); - vbox->addWidget(pb); - vbox->addWidget(pauseButton); - vbox->addWidget(quitButton); - - QStateMachine *machine = new QStateMachine(this); - - QState *process = new QState(machine->rootState()); - process->setObjectName("process"); - - QState *s1 = new QState(process); - s1->setObjectName("s1"); - QState *s2 = new QState(process); - s2->setObjectName("s2"); - s1->addTransition(pb, SIGNAL(clicked()), s2); - s2->addTransition(pb, SIGNAL(clicked()), s1); - - QHistoryState *h = new QHistoryState(process); - h->setDefaultState(s1); - - QState *interrupted = new QState(machine->rootState()); - interrupted->setObjectName("interrupted"); - QFinalState *terminated = new QFinalState(machine->rootState()); - terminated->setObjectName("terminated"); - interrupted->addTransition(pauseButton, SIGNAL(clicked()), h); - interrupted->addTransition(quitButton, SIGNAL(clicked()), terminated); - - process->addTransition(pauseButton, SIGNAL(clicked()), interrupted); - process->addTransition(quitButton, SIGNAL(clicked()), terminated); - - process->setInitialState(s1); - machine->setInitialState(process); - QObject::connect(machine, SIGNAL(finished()), QApplication::instance(), SLOT(quit())); - machine->start(); - } -}; - -int main(int argc, char **argv) -{ - QApplication app(argc, argv); - Window win; - win.show(); - return app.exec(); -} diff --git a/examples/statemachine/pauseandresume/pauseandresume.pro b/examples/statemachine/pauseandresume/pauseandresume.pro deleted file mode 100644 index 6a976cb..0000000 --- a/examples/statemachine/pauseandresume/pauseandresume.pro +++ /dev/null @@ -1,7 +0,0 @@ -TEMPLATE = app -TARGET = -DEPENDPATH += . -INCLUDEPATH += . - -# Input -SOURCES += main.cpp diff --git a/examples/statemachine/statemachine.pro b/examples/statemachine/statemachine.pro index ba32c12..ea3e7a8 100644 --- a/examples/statemachine/statemachine.pro +++ b/examples/statemachine/statemachine.pro @@ -1,11 +1,7 @@ TEMPLATE = subdirs SUBDIRS = \ - clockticking \ - composition \ eventtransitions \ factorial \ - helloworld \ - pauseandresume \ pingpong \ trafficlight \ twowaybutton -- cgit v0.12 From cb516fd65149991a2105c545192a52f26a6ab67d Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Tue, 12 May 2009 16:33:28 +0200 Subject: Fixes: document statemachine/twowaybutton example --- doc/src/examples.qdoc | 1 + doc/src/examples/twowaybutton.qdoc | 82 +++++++++++++++++++++++++++++ examples/statemachine/twowaybutton/main.cpp | 22 +++++--- 3 files changed, 98 insertions(+), 7 deletions(-) create mode 100644 doc/src/examples/twowaybutton.qdoc diff --git a/doc/src/examples.qdoc b/doc/src/examples.qdoc index 4bedcde..bf169e3 100644 --- a/doc/src/examples.qdoc +++ b/doc/src/examples.qdoc @@ -311,6 +311,7 @@ \section1 State Machine \list + \o \l{statemachine/twowaybutton}{Two-way Button}\raisedaster \o \l{statemachine/trafficlight}{Traffic Light}\raisedaster \o \l{statemachine/pingpong}{Ping Pong States}\raisedaster \endlist diff --git a/doc/src/examples/twowaybutton.qdoc b/doc/src/examples/twowaybutton.qdoc new file mode 100644 index 0000000..87de2e8 --- /dev/null +++ b/doc/src/examples/twowaybutton.qdoc @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example statemachine/twowaybutton + \title Two-way Button Example + + The Two-way button example shows how to use \l{The State Machine + Framework} to implement a simple state machine that toggles the current + state when a button is clicked. + + \snippet examples/statemachine/twowaybutton/main.cpp 0 + + The application's main() function begins by constructing the application + object, a button and a state machine. + + \snippet examples/statemachine/twowaybutton/main.cpp 1 + + The state machine has two states; \c on and \c off. When either state is + entered, the text of the button will be set accordingly. + + \snippet examples/statemachine/twowaybutton/main.cpp 2 + + When the state machine is in the \c off state and the button is clicked, + it will transition to the \c on state; when the state machine is in the \c + on state and the button is clicked, it will transition to the \c off + state. + + \snippet examples/statemachine/twowaybutton/main.cpp 3 + + The states are added to the state machine; they become top-level (sibling) + states. + + \snippet examples/statemachine/twowaybutton/main.cpp 4 + + The initial state is \c off; this is the state the state machine will + immediately transition to once the state machine is started. + + \snippet examples/statemachine/twowaybutton/main.cpp 5 + + Finally, the button is resized and made visible, and the application event + loop is entered. + +*/ diff --git a/examples/statemachine/twowaybutton/main.cpp b/examples/statemachine/twowaybutton/main.cpp index 61a0f32..a2c6e45 100644 --- a/examples/statemachine/twowaybutton/main.cpp +++ b/examples/statemachine/twowaybutton/main.cpp @@ -45,34 +45,42 @@ #include #endif +//! [0] int main(int argc, char **argv) { QApplication app(argc, argv); - QPushButton button; - QStateMachine machine; - QState *first = new QState(); - first->setObjectName("first"); +//! [0] +//! [1] QState *off = new QState(); off->assignProperty(&button, "text", "Off"); off->setObjectName("off"); - first->addTransition(off); QState *on = new QState(); on->setObjectName("on"); on->assignProperty(&button, "text", "On"); +//! [1] + +//! [2] off->addTransition(&button, SIGNAL(clicked()), on); on->addTransition(&button, SIGNAL(clicked()), off); +//! [2] - machine.addState(first); +//! [3] machine.addState(off); machine.addState(on); - machine.setInitialState(first); +//! [3] + +//! [4] + machine.setInitialState(off); machine.start(); +//! [4] +//! [5] button.resize(100, 50); button.show(); return app.exec(); } +//! [5] -- cgit v0.12 From d26a56e2d94bea2b5a1135b8336bbeefebf3ba1c Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 12 May 2009 16:40:44 +0200 Subject: Change name of "errorstate" example to "tankgame" The error state is not a big enough part of the example to justify naming it after it. --- examples/statemachine/errorstate/errorstate.pro | 13 - examples/statemachine/errorstate/gameitem.cpp | 88 ------- examples/statemachine/errorstate/gameitem.h | 23 -- examples/statemachine/errorstate/main.cpp | 12 - examples/statemachine/errorstate/mainwindow.cpp | 257 -------------------- examples/statemachine/errorstate/mainwindow.h | 46 ---- examples/statemachine/errorstate/plugin.h | 17 -- examples/statemachine/errorstate/rocketitem.cpp | 60 ----- examples/statemachine/errorstate/rocketitem.h | 26 -- examples/statemachine/errorstate/tankitem.cpp | 262 -------------------- examples/statemachine/errorstate/tankitem.h | 65 ----- .../errorstateplugins/errorstateplugins.pro | 11 - .../errorstateplugins/random_ai/random_ai.pro | 13 - .../random_ai/random_ai_plugin.cpp | 38 --- .../errorstateplugins/random_ai/random_ai_plugin.h | 64 ----- .../errorstateplugins/seek_ai/seek_ai.cpp | 48 ---- .../errorstateplugins/seek_ai/seek_ai.h | 204 ---------------- .../errorstateplugins/seek_ai/seek_ai.pro | 13 - .../errorstateplugins/spin_ai/spin_ai.cpp | 29 --- .../errorstateplugins/spin_ai/spin_ai.h | 46 ---- .../errorstateplugins/spin_ai/spin_ai.pro | 13 - .../spin_ai_with_error/spin_ai_with_error.cpp | 29 --- .../spin_ai_with_error/spin_ai_with_error.h | 46 ---- .../spin_ai_with_error/spin_ai_with_error.pro | 13 - examples/statemachine/statemachine.pro | 4 +- examples/statemachine/tankgame/gameitem.cpp | 88 +++++++ examples/statemachine/tankgame/gameitem.h | 23 ++ examples/statemachine/tankgame/main.cpp | 12 + examples/statemachine/tankgame/mainwindow.cpp | 264 +++++++++++++++++++++ examples/statemachine/tankgame/mainwindow.h | 46 ++++ examples/statemachine/tankgame/plugin.h | 17 ++ examples/statemachine/tankgame/rocketitem.cpp | 60 +++++ examples/statemachine/tankgame/rocketitem.h | 26 ++ examples/statemachine/tankgame/tankgame.pro | 13 + examples/statemachine/tankgame/tankitem.cpp | 262 ++++++++++++++++++++ examples/statemachine/tankgame/tankitem.h | 67 ++++++ .../tankgameplugins/random_ai/random_ai.pro | 13 + .../tankgameplugins/random_ai/random_ai_plugin.cpp | 38 +++ .../tankgameplugins/random_ai/random_ai_plugin.h | 64 +++++ .../tankgameplugins/seek_ai/seek_ai.cpp | 48 ++++ .../statemachine/tankgameplugins/seek_ai/seek_ai.h | 204 ++++++++++++++++ .../tankgameplugins/seek_ai/seek_ai.pro | 13 + .../tankgameplugins/spin_ai/spin_ai.cpp | 29 +++ .../statemachine/tankgameplugins/spin_ai/spin_ai.h | 46 ++++ .../tankgameplugins/spin_ai/spin_ai.pro | 13 + .../spin_ai_with_error/spin_ai_with_error.cpp | 29 +++ .../spin_ai_with_error/spin_ai_with_error.h | 46 ++++ .../spin_ai_with_error/spin_ai_with_error.pro | 13 + .../tankgameplugins/tankgameplugins.pro | 11 + 49 files changed, 1448 insertions(+), 1437 deletions(-) delete mode 100644 examples/statemachine/errorstate/errorstate.pro delete mode 100644 examples/statemachine/errorstate/gameitem.cpp delete mode 100644 examples/statemachine/errorstate/gameitem.h delete mode 100644 examples/statemachine/errorstate/main.cpp delete mode 100644 examples/statemachine/errorstate/mainwindow.cpp delete mode 100644 examples/statemachine/errorstate/mainwindow.h delete mode 100644 examples/statemachine/errorstate/plugin.h delete mode 100644 examples/statemachine/errorstate/rocketitem.cpp delete mode 100644 examples/statemachine/errorstate/rocketitem.h delete mode 100644 examples/statemachine/errorstate/tankitem.cpp delete mode 100644 examples/statemachine/errorstate/tankitem.h delete mode 100644 examples/statemachine/errorstateplugins/errorstateplugins.pro delete mode 100644 examples/statemachine/errorstateplugins/random_ai/random_ai.pro delete mode 100644 examples/statemachine/errorstateplugins/random_ai/random_ai_plugin.cpp delete mode 100644 examples/statemachine/errorstateplugins/random_ai/random_ai_plugin.h delete mode 100644 examples/statemachine/errorstateplugins/seek_ai/seek_ai.cpp delete mode 100644 examples/statemachine/errorstateplugins/seek_ai/seek_ai.h delete mode 100644 examples/statemachine/errorstateplugins/seek_ai/seek_ai.pro delete mode 100644 examples/statemachine/errorstateplugins/spin_ai/spin_ai.cpp delete mode 100644 examples/statemachine/errorstateplugins/spin_ai/spin_ai.h delete mode 100644 examples/statemachine/errorstateplugins/spin_ai/spin_ai.pro delete mode 100644 examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.cpp delete mode 100644 examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.h delete mode 100644 examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.pro create mode 100644 examples/statemachine/tankgame/gameitem.cpp create mode 100644 examples/statemachine/tankgame/gameitem.h create mode 100644 examples/statemachine/tankgame/main.cpp create mode 100644 examples/statemachine/tankgame/mainwindow.cpp create mode 100644 examples/statemachine/tankgame/mainwindow.h create mode 100644 examples/statemachine/tankgame/plugin.h create mode 100644 examples/statemachine/tankgame/rocketitem.cpp create mode 100644 examples/statemachine/tankgame/rocketitem.h create mode 100644 examples/statemachine/tankgame/tankgame.pro create mode 100644 examples/statemachine/tankgame/tankitem.cpp create mode 100644 examples/statemachine/tankgame/tankitem.h create mode 100644 examples/statemachine/tankgameplugins/random_ai/random_ai.pro create mode 100644 examples/statemachine/tankgameplugins/random_ai/random_ai_plugin.cpp create mode 100644 examples/statemachine/tankgameplugins/random_ai/random_ai_plugin.h create mode 100644 examples/statemachine/tankgameplugins/seek_ai/seek_ai.cpp create mode 100644 examples/statemachine/tankgameplugins/seek_ai/seek_ai.h create mode 100644 examples/statemachine/tankgameplugins/seek_ai/seek_ai.pro create mode 100644 examples/statemachine/tankgameplugins/spin_ai/spin_ai.cpp create mode 100644 examples/statemachine/tankgameplugins/spin_ai/spin_ai.h create mode 100644 examples/statemachine/tankgameplugins/spin_ai/spin_ai.pro create mode 100644 examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.cpp create mode 100644 examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.h create mode 100644 examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.pro create mode 100644 examples/statemachine/tankgameplugins/tankgameplugins.pro diff --git a/examples/statemachine/errorstate/errorstate.pro b/examples/statemachine/errorstate/errorstate.pro deleted file mode 100644 index b93a691..0000000 --- a/examples/statemachine/errorstate/errorstate.pro +++ /dev/null @@ -1,13 +0,0 @@ -###################################################################### -# Automatically generated by qmake (2.01a) on 22. apr 14:11:33 2009 -###################################################################### - -TEMPLATE = app -TARGET = -DEPENDPATH += . -INCLUDEPATH += C:/dev/kinetic/examples/statemachine/errorstate/. . - -# Input -HEADERS += mainwindow.h plugin.h tankitem.h rocketitem.h gameitem.h -SOURCES += main.cpp mainwindow.cpp tankitem.cpp rocketitem.cpp gameitem.cpp -CONFIG += console diff --git a/examples/statemachine/errorstate/gameitem.cpp b/examples/statemachine/errorstate/gameitem.cpp deleted file mode 100644 index 1a2af71..0000000 --- a/examples/statemachine/errorstate/gameitem.cpp +++ /dev/null @@ -1,88 +0,0 @@ -#include "gameitem.h" - -#include -#include - -GameItem::GameItem(QObject *parent) : QObject(parent) -{ -} - -QPointF GameItem::tryMove(const QPointF &requestedPosition, QLineF *collidedLine, - QGraphicsItem **collidedItem) const -{ - QLineF movementPath(pos(), requestedPosition); - - qreal cannonLength = 0.0; - { - QPointF p1 = boundingRect().center(); - QPointF p2 = QPointF(boundingRect().right() + 10.0, p1.y()); - - cannonLength = QLineF(mapToScene(p1), mapToScene(p2)).length(); - } - - movementPath.setLength(movementPath.length() + cannonLength); - - QRectF boundingRectPath(QPointF(qMin(movementPath.x1(), movementPath.x2()), qMin(movementPath.y1(), movementPath.y2())), - QPointF(qMax(movementPath.x1(), movementPath.x2()), qMax(movementPath.y1(), movementPath.y2()))); - - QList itemsInRect = scene()->items(boundingRectPath, Qt::IntersectsItemBoundingRect); - - QPointF nextPoint = requestedPosition; - QRectF sceneRect = scene()->sceneRect(); - - foreach (QGraphicsItem *item, itemsInRect) { - if (item == static_cast(this)) - continue; - - QPolygonF mappedBoundingRect = item->mapToScene(item->boundingRect()); - for (int i=0; isceneRect().topLeft(), scene()->sceneRect().bottomLeft()); - } - - if (nextPoint.x() > sceneRect.right()) { - nextPoint.rx() = sceneRect.right(); - if (collidedLine != 0) - *collidedLine = QLineF(scene()->sceneRect().topRight(), scene()->sceneRect().bottomRight()); - } - - if (nextPoint.y() < sceneRect.top()) { - nextPoint.ry() = sceneRect.top(); - if (collidedLine != 0) - *collidedLine = QLineF(scene()->sceneRect().topLeft(), scene()->sceneRect().topRight()); - } - - if (nextPoint.y() > sceneRect.bottom()) { - nextPoint.ry() = sceneRect.bottom(); - if (collidedLine != 0) - *collidedLine = QLineF(scene()->sceneRect().bottomLeft(), scene()->sceneRect().bottomRight()); - } - - return nextPoint; -} - diff --git a/examples/statemachine/errorstate/gameitem.h b/examples/statemachine/errorstate/gameitem.h deleted file mode 100644 index 43b8785..0000000 --- a/examples/statemachine/errorstate/gameitem.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef GAMEITEM_H -#define GAMEITEM_H - -#include - -class QLineF; -class GameItem: public QObject, public QGraphicsItem -{ - Q_OBJECT -public: - enum { Type = UserType + 1 }; - int type() const { return Type; } - - GameItem(QObject *parent = 0); - - virtual void idle(qreal elapsed) = 0; - -protected: - QPointF tryMove(const QPointF &requestedPosition, QLineF *collidedLine = 0, - QGraphicsItem **collidedItem = 0) const; -}; - -#endif diff --git a/examples/statemachine/errorstate/main.cpp b/examples/statemachine/errorstate/main.cpp deleted file mode 100644 index 26fc1bb..0000000 --- a/examples/statemachine/errorstate/main.cpp +++ /dev/null @@ -1,12 +0,0 @@ -#include -#include "mainwindow.h" - -int main(int argc, char **argv) -{ - QApplication app(argc, argv); - - MainWindow mainWindow; - mainWindow.show(); - - return app.exec(); -} diff --git a/examples/statemachine/errorstate/mainwindow.cpp b/examples/statemachine/errorstate/mainwindow.cpp deleted file mode 100644 index 07719bc..0000000 --- a/examples/statemachine/errorstate/mainwindow.cpp +++ /dev/null @@ -1,257 +0,0 @@ -#include "mainwindow.h" -#include "tankitem.h" -#include "rocketitem.h" -#include "plugin.h" - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -MainWindow::MainWindow(QWidget *parent) - : QMainWindow(parent), m_scene(0), m_machine(0), m_runningState(0), m_started(false) -{ - init(); -} - -MainWindow::~MainWindow() -{ -} - -void MainWindow::addWall(const QRectF &wall) -{ - QGraphicsRectItem *item = new QGraphicsRectItem; - item->setRect(wall); - item->setBrush(Qt::darkGreen); - item->setPen(QPen(Qt::black, 0)); - - m_scene->addItem(item); -} - -void MainWindow::init() -{ - setWindowTitle("Pluggable Tank Game"); - - QGraphicsView *view = new QGraphicsView(this); - view->setRenderHints(QPainter::Antialiasing); - setCentralWidget(view); - - m_scene = new QGraphicsScene(this); - view->setScene(m_scene); - - QRectF sceneRect = QRectF(-200.0, -200.0, 400.0, 400.0); - m_scene->setSceneRect(sceneRect); - - { - TankItem *item = new TankItem(this); - - item->setPos(m_scene->sceneRect().topLeft() + QPointF(15.0, 15.0)); - item->setDirection(45.0); - item->setColor(Qt::red); - - m_spawns.append(item); - } - - { - TankItem *item = new TankItem(this); - - item->setPos(m_scene->sceneRect().topRight() + QPointF(-15.0, 15.0)); - item->setDirection(135.0); - item->setColor(Qt::green); - - m_spawns.append(item); - } - - { - TankItem *item = new TankItem(this); - - item->setPos(m_scene->sceneRect().bottomRight() + QPointF(-15.0, -15.0)); - item->setDirection(225.0); - item->setColor(Qt::blue); - - m_spawns.append(item); - } - - { - TankItem *item = new TankItem(this); - - item->setPos(m_scene->sceneRect().bottomLeft() + QPointF(15.0, -15.0)); - item->setDirection(315.0); - item->setColor(Qt::yellow); - - m_spawns.append(item); - } - - QPointF centerOfMap = sceneRect.center(); - - addWall(QRectF(centerOfMap + QPointF(-50.0, -60.0), centerOfMap + QPointF(50.0, -50.0))); - addWall(QRectF(centerOfMap - QPointF(-50.0, -60.0), centerOfMap - QPointF(50.0, -50.0))); - addWall(QRectF(centerOfMap + QPointF(-50.0, -50.0), centerOfMap + QPointF(-40.0, 50.0))); - addWall(QRectF(centerOfMap - QPointF(-50.0, -50.0), centerOfMap - QPointF(-40.0, 50.0))); - - addWall(QRectF(sceneRect.topLeft() + QPointF(sceneRect.width() / 2.0 - 5.0, -10.0), - sceneRect.topLeft() + QPointF(sceneRect.width() / 2.0 + 5.0, 100.0))); - addWall(QRectF(sceneRect.bottomLeft() + QPointF(sceneRect.width() / 2.0 - 5.0, 10.0), - sceneRect.bottomLeft() + QPointF(sceneRect.width() / 2.0 + 5.0, -100.0))); - addWall(QRectF(sceneRect.topLeft() + QPointF(-10.0, sceneRect.height() / 2.0 - 5.0), - sceneRect.topLeft() + QPointF(100.0, sceneRect.height() / 2.0 + 5.0))); - addWall(QRectF(sceneRect.topRight() + QPointF(10.0, sceneRect.height() / 2.0 - 5.0), - sceneRect.topRight() + QPointF(-100.0, sceneRect.height() / 2.0 + 5.0))); - - - QAction *addTankAction = menuBar()->addAction("&Add tank"); - QAction *runGameAction = menuBar()->addAction("&Run game"); - runGameAction->setObjectName("runGameAction"); - QAction *stopGameAction = menuBar()->addAction("&Stop game"); - menuBar()->addSeparator(); - QAction *quitAction = menuBar()->addAction("&Quit"); - - connect(addTankAction, SIGNAL(triggered()), this, SLOT(addTank())); - connect(quitAction, SIGNAL(triggered()), this, SLOT(close())); - - m_machine = new QStateMachine(this); - QState *stoppedState = new QState(m_machine->rootState()); - stoppedState->setObjectName("stoppedState"); - stoppedState->assignProperty(runGameAction, "enabled", true); - stoppedState->assignProperty(stopGameAction, "enabled", false); - stoppedState->assignProperty(this, "started", false); - m_machine->setInitialState(stoppedState); - - QState *spawnsAvailable = new QState(stoppedState); - spawnsAvailable->setObjectName("spawnsAvailable"); - spawnsAvailable->assignProperty(addTankAction, "enabled", true); - - QState *noSpawnsAvailable = new QState(stoppedState); - noSpawnsAvailable->setObjectName("noSpawnsAvailable"); - noSpawnsAvailable->assignProperty(addTankAction, "enabled", false); - - spawnsAvailable->addTransition(this, SIGNAL(mapFull()), noSpawnsAvailable); - - QHistoryState *hs = new QHistoryState(stoppedState); - hs->setDefaultState(spawnsAvailable); - - stoppedState->setInitialState(hs); - - m_runningState = new QState(QState::ParallelStates, m_machine->rootState()); - m_runningState->setObjectName("runningState"); - m_runningState->assignProperty(addTankAction, "enabled", false); - m_runningState->assignProperty(runGameAction, "enabled", false); - m_runningState->assignProperty(stopGameAction, "enabled", true); - - stoppedState->addTransition(runGameAction, SIGNAL(triggered()), m_runningState); - m_runningState->addTransition(stopGameAction, SIGNAL(triggered()), stoppedState); - - QTimer *timer = new QTimer(this); - timer->setInterval(100); - connect(timer, SIGNAL(timeout()), this, SLOT(runStep())); - connect(m_runningState, SIGNAL(entered()), timer, SLOT(start())); - connect(m_runningState, SIGNAL(exited()), timer, SLOT(stop())); - - m_machine->start(); - m_time.start(); -} - -void MainWindow::runStep() -{ - if (!m_started) { - m_time.restart(); - m_started = true; - } else { - int elapsed = m_time.elapsed(); - if (elapsed > 0) { - m_time.restart(); - qreal elapsedSecs = elapsed / 1000.0; - QList items = m_scene->items(); - foreach (QGraphicsItem *item, items) { - if (GameItem *gameItem = qgraphicsitem_cast(item)) - gameItem->idle(elapsedSecs); - } - } - } -} - -void MainWindow::addRocket() -{ - TankItem *tankItem = qobject_cast(sender()); - if (tankItem != 0) { - RocketItem *rocketItem = new RocketItem; - - QPointF s = tankItem->mapToScene(QPointF(tankItem->boundingRect().right() + 10.0, - tankItem->boundingRect().center().y())); - rocketItem->setPos(s); - rocketItem->setDirection(tankItem->direction()); - m_scene->addItem(rocketItem); - } -} - -void MainWindow::addTank() -{ - Q_ASSERT(!m_spawns.isEmpty()); - - QDir pluginsDir(qApp->applicationDirPath()); -#if defined(Q_OS_WIN) - if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release") - pluginsDir.cdUp(); -#elif defined(Q_OS_MAC) - if (pluginsDir.dirName() == "MacOS") { - pluginsDir.cdUp(); - pluginsDir.cdUp(); - pluginsDir.cdUp(); - } -#endif - - pluginsDir.cd("plugins"); - - QStringList itemNames; - QList items; - foreach (QString fileName, pluginsDir.entryList(QDir::Files)) { - QPluginLoader loader(pluginsDir.absoluteFilePath(fileName)); - QObject *possiblePlugin = loader.instance(); - if (Plugin *plugin = qobject_cast(possiblePlugin)) { - QString objectName = possiblePlugin->objectName(); - if (objectName.isEmpty()) - objectName = fileName; - - itemNames.append(objectName); - items.append(plugin); - } - } - - if (items.isEmpty()) { - QMessageBox::information(this, "No tank types found", "Please build the errorstateplugins directory"); - return; - } - - bool ok; - QString selectedName = QInputDialog::getItem(this, "Select a tank type", "Tank types", - itemNames, 0, false, &ok); - - if (ok && !selectedName.isEmpty()) { - int idx = itemNames.indexOf(selectedName); - if (Plugin *plugin = idx >= 0 ? items.at(idx) : 0) { - TankItem *tankItem = m_spawns.takeLast(); - m_scene->addItem(tankItem); - connect(tankItem, SIGNAL(cannonFired()), this, SLOT(addRocket())); - if (m_spawns.isEmpty()) - emit mapFull(); - - QState *region = new QState(m_runningState); - QState *pluginState = plugin->create(region, tankItem); - region->setInitialState(pluginState); - - // If the plugin has an error it is disabled - QState *errorState = new QState(region); - errorState->assignProperty(tankItem, "enabled", false); - pluginState->setErrorState(errorState); - } - } -} - diff --git a/examples/statemachine/errorstate/mainwindow.h b/examples/statemachine/errorstate/mainwindow.h deleted file mode 100644 index 622dabe..0000000 --- a/examples/statemachine/errorstate/mainwindow.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef MAINWINDOW_H -#define MAINWINDOW_H - -#include -#include - -class QGraphicsScene; -class QStateMachine; -class QState; -class TankItem; -class MainWindow: public QMainWindow -{ - Q_OBJECT - Q_PROPERTY(bool started READ started WRITE setStarted) -public: - MainWindow(QWidget *parent = 0); - ~MainWindow(); - - void setStarted(bool b) { m_started = b; } - bool started() const { return m_started; } - -public slots: - void addTank(); - void addRocket(); - void runStep(); - -signals: - void mapFull(); - -private: - void init(); - void addWall(const QRectF &wall); - - QGraphicsScene *m_scene; - - QStateMachine *m_machine; - QState *m_runningState; - - QList m_spawns; - QTime m_time; - - bool m_started : 1; -}; - -#endif - diff --git a/examples/statemachine/errorstate/plugin.h b/examples/statemachine/errorstate/plugin.h deleted file mode 100644 index 2b48d43..0000000 --- a/examples/statemachine/errorstate/plugin.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef PLUGIN_H -#define PLUGIN_H - -#include - -class QState; -class Plugin -{ -public: - virtual ~Plugin() {} - - virtual QState *create(QState *parentState, QObject *tank) = 0; -}; - -Q_DECLARE_INTERFACE(Plugin, "TankPlugin") - -#endif diff --git a/examples/statemachine/errorstate/rocketitem.cpp b/examples/statemachine/errorstate/rocketitem.cpp deleted file mode 100644 index c324980..0000000 --- a/examples/statemachine/errorstate/rocketitem.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "rocketitem.h" -#include "tankitem.h" - -#include -#include - -#include - -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - -RocketItem::RocketItem(QObject *parent) - : GameItem(parent), m_direction(0.0), m_distance(300.0) -{ -} - -QRectF RocketItem::boundingRect() const -{ - return QRectF(-1.0, -1.0, 2.0, 2.0); -} - -void RocketItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) -{ - painter->setBrush(Qt::black); - painter->drawEllipse(boundingRect()); -} - -void RocketItem::idle(qreal elapsed) -{ - qreal dist = elapsed * speed(); - - m_distance -= dist; - if (m_distance < 0.0) { - scene()->removeItem(this); - delete this; - return; - } - - qreal a = m_direction * M_PI / 180.0; - - qreal yd = dist * sin(a); - qreal xd = dist * sin(M_PI / 2.0 - a); - - QPointF requestedPosition = pos() + QPointF(xd, yd); - QGraphicsItem *collidedItem = 0; - QPointF nextPosition = tryMove(requestedPosition, 0, &collidedItem); - if (requestedPosition == nextPosition) { - setPos(nextPosition); - } else { - if (GameItem *gameItem = qgraphicsitem_cast(collidedItem)) { - TankItem *tankItem = qobject_cast(gameItem); - if (tankItem != 0) - tankItem->hitByRocket(); - } - - scene()->removeItem(this); - delete this; - } -} diff --git a/examples/statemachine/errorstate/rocketitem.h b/examples/statemachine/errorstate/rocketitem.h deleted file mode 100644 index 189a1dd..0000000 --- a/examples/statemachine/errorstate/rocketitem.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef ROCKETITEM_H -#define ROCKETITEM_H - -#include "gameitem.h" - -class RocketItem: public GameItem -{ - Q_OBJECT -public: - RocketItem(QObject *parent = 0); - - virtual void idle(qreal elapsed); - qreal speed() const { return 100.0; } - void setDirection(qreal direction) { m_direction = direction; } - -protected: - virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); - QRectF boundingRect() const; - -private: - qreal m_direction; - qreal m_distance; -}; - - -#endif diff --git a/examples/statemachine/errorstate/tankitem.cpp b/examples/statemachine/errorstate/tankitem.cpp deleted file mode 100644 index 5506a7e..0000000 --- a/examples/statemachine/errorstate/tankitem.cpp +++ /dev/null @@ -1,262 +0,0 @@ -#include "tankitem.h" - -#include -#include -#include - -#include - -#ifndef M_PI -#define M_PI 3.14159265358979323846 -#endif - -class Action -{ -public: - Action(TankItem *item) : m_item(item) - { - } - - TankItem *item() const { return m_item; } - void setItem(TankItem *item) { m_item = item; } - - virtual bool apply(qreal timeDelta) = 0; - -private: - TankItem *m_item; -}; - -class MoveAction: public Action -{ -public: - MoveAction(TankItem *item, qreal distance) - : Action(item), m_distance(distance) - { - m_reverse = m_distance < 0.0; - } - - bool apply(qreal timeDelta) - { - qreal dist = timeDelta * item()->speed() * (m_reverse ? -1.0 : 1.0); - - bool done = false; - if (qAbs(m_distance) < qAbs(dist)) { - done = true; - dist = m_distance; - } - m_distance -= dist; - - qreal a = item()->direction() * M_PI / 180.0; - - qreal yd = dist * sin(a); - qreal xd = dist * sin(M_PI / 2.0 - a); - - item()->setPos(item()->pos() + QPointF(xd, yd)); - return !done; - } - -private: - qreal m_distance; - bool m_reverse; -}; - -class TurnAction: public Action -{ -public: - TurnAction(TankItem *item, qreal distance) - : Action(item), m_distance(distance) - { - m_reverse = m_distance < 0.0; - } - - bool apply(qreal timeDelta) - { - qreal dist = timeDelta * item()->angularSpeed() * (m_reverse ? -1.0 : 1.0); - bool done = false; - if (qAbs(m_distance) < qAbs(dist)) { - done = true; - dist = m_distance; - } - m_distance -= dist; - - item()->setDirection(item()->direction() + dist); - return !done; - } - -private: - qreal m_distance; - bool m_reverse; -}; - -TankItem::TankItem(QObject *parent) - : GameItem(parent), m_currentAction(0), m_currentDirection(0.0), m_enabled(true) -{ - connect(this, SIGNAL(cannonFired()), this, SIGNAL(actionCompleted())); -} - -void TankItem::idle(qreal elapsed) -{ - if (m_enabled) { - if (m_currentAction != 0) { - if (!m_currentAction->apply(elapsed)) { - setAction(0); - emit actionCompleted(); - } - - QGraphicsItem *item = 0; - qreal distance = distanceToObstacle(&item); - if (TankItem *tankItem = qgraphicsitem_cast(item)) - emit tankSpotted(tankItem->direction(), distance); - } - } -} - -void TankItem::hitByRocket() -{ - deleteLater(); -} - -void TankItem::setAction(Action *newAction) -{ - if (m_currentAction != 0) - delete m_currentAction; - - m_currentAction = newAction; -} - -void TankItem::fireCannon() -{ - emit cannonFired(); -} - -void TankItem::moveForwards(qreal length) -{ - setAction(new MoveAction(this, length)); -} - -void TankItem::moveBackwards(qreal length) -{ - setAction(new MoveAction(this, -length)); -} - -void TankItem::turn(qreal degrees) -{ - setAction(new TurnAction(this, degrees)); -} - -void TankItem::turnTo(qreal degrees) -{ - setAction(new TurnAction(this, degrees - direction())); -} - -void TankItem::stop() -{ - setAction(0); -} - -QVariant TankItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) -{ - if (change == ItemPositionChange && scene()) { - QPointF requestedPosition = value.toPointF(); - QLineF collidedLine; - QPointF nextPoint = tryMove(requestedPosition, &collidedLine); - if (nextPoint != requestedPosition) - emit collision(collidedLine); - return nextPoint; - } else { - return QGraphicsItem::itemChange(change, value); - } -} - - -void TankItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) -{ - QRectF brect = boundingRect(); - - painter->setBrush(m_color); - painter->setPen(Qt::black); - - // body - painter->drawRect(brect.adjusted(0.0, 4.0, -2.0, -4.0)); - - // cannon - QRectF cannonBase = brect.adjusted(10.0, 6.0, -12.0, -6.0); - painter->drawEllipse(cannonBase); - - painter->drawRect(QRectF(QPointF(cannonBase.center().x(), cannonBase.center().y() - 2.0), - QPointF(brect.right(), cannonBase.center().y() + 2.0))); - - // left track - painter->setBrush(QBrush(Qt::black, Qt::VerPattern)); - QRectF leftTrackRect = QRectF(brect.topLeft(), QPointF(brect.right() - 2.0, brect.top() + 4.0)); - painter->fillRect(leftTrackRect, Qt::darkYellow); - painter->drawRect(leftTrackRect); - - // right track - QRectF rightTrackRect = QRectF(QPointF(brect.left(), brect.bottom() - 4.0), - QPointF(brect.right() - 2.0, brect.bottom())); - painter->fillRect(rightTrackRect, Qt::darkYellow); - painter->drawRect(rightTrackRect); - - if (!m_enabled) { - painter->setPen(QPen(Qt::red, 5)); - - painter->drawEllipse(brect); - - QPainterPath path; - path.addEllipse(brect); - painter->setClipPath(path); - painter->drawLine(brect.topRight(), brect.bottomLeft()); - } -} - -QRectF TankItem::boundingRect() const -{ - return QRectF(-20.0, -10.0, 40.0, 20.0); -} - -qreal TankItem::direction() const -{ - return m_currentDirection; -} - -void TankItem::setDirection(qreal newDirection) -{ - int fullRotations = int(newDirection) / 360; - newDirection -= fullRotations * 360.0; - - qreal diff = newDirection - m_currentDirection; - m_currentDirection = newDirection; - rotate(diff); -} - -qreal TankItem::distanceToObstacle(QGraphicsItem **obstacle) const -{ - qreal dist = sqrt(pow(scene()->sceneRect().width(), 2) + pow(scene()->sceneRect().height(), 2)); - - qreal a = m_currentDirection * M_PI / 180.0; - - qreal yd = dist * sin(a); - qreal xd = dist * sin(M_PI / 2.0 - a); - - QPointF requestedPosition = pos() + QPointF(xd, yd); - QGraphicsItem *collidedItem = 0; - QPointF nextPosition = tryMove(requestedPosition, 0, &collidedItem); - if (collidedItem != 0) { - if (obstacle != 0) - *obstacle = collidedItem; - - QPointF d = nextPosition - pos(); - return sqrt(pow(d.x(), 2) + pow(d.y(), 2)); - } else { - return 0.0; - } -} - -qreal TankItem::distanceToObstacle() const -{ - return distanceToObstacle(0); -} - - - diff --git a/examples/statemachine/errorstate/tankitem.h b/examples/statemachine/errorstate/tankitem.h deleted file mode 100644 index cefed69..0000000 --- a/examples/statemachine/errorstate/tankitem.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef TANKITEM_H -#define TANKITEM_H - -#include "gameitem.h" - -#include - -class Action; -class TankItem: public GameItem -{ - Q_OBJECT - Q_PROPERTY(bool enabled READ enabled WRITE setEnabled) - Q_PROPERTY(qreal direction READ direction WRITE turnTo) - Q_PROPERTY(qreal distanceToObstacle READ distanceToObstacle) -public: - TankItem(QObject *parent = 0); - - void setColor(const QColor &color) { m_color = color; } - QColor color() const { return m_color; } - - void idle(qreal elapsed); - void setDirection(qreal newDirection); - - qreal speed() const { return 90.0; } - qreal angularSpeed() const { return 90.0; } - - QRectF boundingRect() const; - - void hitByRocket(); - - void setEnabled(bool b) { m_enabled = b; } - bool enabled() const { return m_enabled; } - - qreal direction() const; - qreal distanceToObstacle() const; - qreal distanceToObstacle(QGraphicsItem **item) const; - -signals: - void tankSpotted(qreal direction, qreal distance); - void collision(const QLineF &collidedLine); - void actionCompleted(); - void cannonFired(); - -public slots: - void moveForwards(qreal length = 10.0); - void moveBackwards(qreal length = 10.0); - void turn(qreal degrees = 30.0); - void turnTo(qreal degrees = 0.0); - void stop(); - void fireCannon(); - -protected: - virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); - QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value); - -private: - void setAction(Action *newAction); - - Action *m_currentAction; - qreal m_currentDirection; - QColor m_color; - bool m_enabled; -}; - -#endif diff --git a/examples/statemachine/errorstateplugins/errorstateplugins.pro b/examples/statemachine/errorstateplugins/errorstateplugins.pro deleted file mode 100644 index 5b6b758..0000000 --- a/examples/statemachine/errorstateplugins/errorstateplugins.pro +++ /dev/null @@ -1,11 +0,0 @@ -TEMPLATE = subdirs -SUBDIRS = random_ai \ - spin_ai_with_error \ - spin_ai \ - seek_ai - -# install -target.path = $$[QT_INSTALL_EXAMPLES]/statemachine/errorstateplugins -sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS errorstateplugins.pro -sources.path = $$[QT_INSTALL_EXAMPLES]/statemachine/errorstateplugins -INSTALLS += target sources diff --git a/examples/statemachine/errorstateplugins/random_ai/random_ai.pro b/examples/statemachine/errorstateplugins/random_ai/random_ai.pro deleted file mode 100644 index f290250..0000000 --- a/examples/statemachine/errorstateplugins/random_ai/random_ai.pro +++ /dev/null @@ -1,13 +0,0 @@ -TEMPLATE = lib -CONFIG += plugin -INCLUDEPATH += ../.. -HEADERS = random_ai_plugin.h -SOURCES = random_ai_plugin.cpp -TARGET = $$qtLibraryTarget(random_ai) -DESTDIR = ../../errorstate/plugins - -#! [0] -# install -target.path = $$[QT_INSTALL_EXAMPLES]/statemachine/errorstate/plugins -sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS random_ai.pro -sources.path = $$[QT_INSTALL_EXAMPLES]/statemachine/errorstateplugins/random_ai \ No newline at end of file diff --git a/examples/statemachine/errorstateplugins/random_ai/random_ai_plugin.cpp b/examples/statemachine/errorstateplugins/random_ai/random_ai_plugin.cpp deleted file mode 100644 index c196247..0000000 --- a/examples/statemachine/errorstateplugins/random_ai/random_ai_plugin.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "random_ai_plugin.h" - -#include -#include - -#include - -QState *RandomAiPlugin::create(QState *parentState, QObject *tank) -{ - qsrand(uint(time(NULL))); - - QState *topLevel = new QState(parentState); - - QState *selectNextActionState = new SelectActionState(topLevel); - topLevel->setInitialState(selectNextActionState); - - QState *fireState = new RandomDistanceState(topLevel); - connect(fireState, SIGNAL(distanceComputed(qreal)), tank, SLOT(fireCannon())); - selectNextActionState->addTransition(selectNextActionState, SIGNAL(fireSelected()), fireState); - - QState *moveForwardsState = new RandomDistanceState(topLevel); - connect(moveForwardsState, SIGNAL(distanceComputed(qreal)), tank, SLOT(moveForwards(qreal))); - selectNextActionState->addTransition(selectNextActionState, SIGNAL(moveForwardsSelected()), moveForwardsState); - - QState *moveBackwardsState = new RandomDistanceState(topLevel); - connect(moveBackwardsState, SIGNAL(distanceComputed(qreal)), tank, SLOT(moveBackwards(qreal))); - selectNextActionState->addTransition(selectNextActionState, SIGNAL(moveBackwardsSelected()), moveBackwardsState); - - QState *turnState = new RandomDistanceState(topLevel); - connect(turnState, SIGNAL(distanceComputed(qreal)), tank, SLOT(turn(qreal))); - selectNextActionState->addTransition(selectNextActionState, SIGNAL(turnSelected()), turnState); - - topLevel->addTransition(tank, SIGNAL(actionCompleted()), selectNextActionState); - - return topLevel; -} - -Q_EXPORT_PLUGIN2(random_ai, RandomAiPlugin) diff --git a/examples/statemachine/errorstateplugins/random_ai/random_ai_plugin.h b/examples/statemachine/errorstateplugins/random_ai/random_ai_plugin.h deleted file mode 100644 index 10e6f48..0000000 --- a/examples/statemachine/errorstateplugins/random_ai/random_ai_plugin.h +++ /dev/null @@ -1,64 +0,0 @@ -#ifndef RANDOM_AI_PLUGIN_H -#define RANDOM_AI_PLUGIN_H - -#include -#include - -#include - -class SelectActionState: public QState -{ - Q_OBJECT -public: - SelectActionState(QState *parent = 0) : QState(parent) - { - } - -signals: - void fireSelected(); - void moveForwardsSelected(); - void moveBackwardsSelected(); - void turnSelected(); - -protected: - void onEntry(QEvent *) - { - int rand = qrand() % 4; - switch (rand) { - case 0: emit fireSelected(); break; - case 1: emit moveForwardsSelected(); break; - case 2: emit moveBackwardsSelected(); break; - case 3: emit turnSelected(); break; - }; - } -}; - -class RandomDistanceState: public QState -{ - Q_OBJECT -public: - RandomDistanceState(QState *parent = 0) : QState(parent) - { - } - -signals: - void distanceComputed(qreal distance); - -protected: - void onEntry(QEvent *) - { - emit distanceComputed(qreal(qrand() % 180)); - } -}; - -class RandomAiPlugin: public QObject, public Plugin -{ - Q_OBJECT - Q_INTERFACES(Plugin) -public: - RandomAiPlugin() { setObjectName("Random"); } - - virtual QState *create(QState *parentState, QObject *tank); -}; - -#endif // RANDOM_AI_PLUGIN_H diff --git a/examples/statemachine/errorstateplugins/seek_ai/seek_ai.cpp b/examples/statemachine/errorstateplugins/seek_ai/seek_ai.cpp deleted file mode 100644 index 2fb05d4..0000000 --- a/examples/statemachine/errorstateplugins/seek_ai/seek_ai.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include "seek_ai.h" - -QState *SeekAi::create(QState *parentState, QObject *tank) -{ - QState *topLevel = new QState(parentState); - topLevel->setObjectName("topLevel"); - - QState *seek = new QState(topLevel); - seek->setObjectName("seek"); - topLevel->setInitialState(seek); - - QState *lookForNearestWall = new SearchState(tank, seek); - lookForNearestWall->setObjectName("lookForNearestWall"); - seek->setInitialState(lookForNearestWall); - - QState *driveToFirstObstacle = new QState(seek); - driveToFirstObstacle->setObjectName("driveToFirstObstacle"); - lookForNearestWall->addTransition(lookForNearestWall, SIGNAL(nearestObstacleStraightAhead()), - driveToFirstObstacle); - - QState *drive = new QState(driveToFirstObstacle); - drive->setObjectName("drive"); - driveToFirstObstacle->setInitialState(drive); - connect(drive, SIGNAL(entered()), tank, SLOT(moveForwards())); - connect(drive, SIGNAL(exited()), tank, SLOT(stop())); - - // Go in loop - QState *finishedDriving = new QState(driveToFirstObstacle); - finishedDriving->setObjectName("finishedDriving"); - drive->addTransition(tank, SIGNAL(actionCompleted()), finishedDriving); - finishedDriving->addTransition(drive); - - QState *turnTo = new QState(seek); - turnTo->setObjectName("turnTo"); - driveToFirstObstacle->addTransition(new CollisionTransition(tank, turnTo)); - - turnTo->addTransition(tank, SIGNAL(actionCompleted()), driveToFirstObstacle); - - ChaseState *chase = new ChaseState(tank, topLevel); - chase->setObjectName("chase"); - seek->addTransition(new TankSpottedTransition(tank, chase)); - chase->addTransition(chase, SIGNAL(finished()), driveToFirstObstacle); - chase->addTransition(new TankSpottedTransition(tank, chase)); - - return topLevel; -} - -Q_EXPORT_PLUGIN2(seek_ai, SeekAi) diff --git a/examples/statemachine/errorstateplugins/seek_ai/seek_ai.h b/examples/statemachine/errorstateplugins/seek_ai/seek_ai.h deleted file mode 100644 index a1b5749..0000000 --- a/examples/statemachine/errorstateplugins/seek_ai/seek_ai.h +++ /dev/null @@ -1,204 +0,0 @@ -#ifndef SEEK_AI_H -#define SEEK_AI_H - -#include - -#include -#include -#include -#include -#include -#include -#include - -class SearchState: public QState -{ - Q_OBJECT -public: - SearchState(QObject *tank, QState *parentState = 0) - : QState(parentState), - m_tank(tank), - m_distanceToTurn(360.0), - m_nearestDistance(-1.0), - m_directionOfNearestObstacle(0.0) - { - } - -public slots: - void turnAlittle() - { - qreal dist = m_tank->property("distanceToObstacle").toDouble(); - - if (m_nearestDistance < 0.0 || dist < m_nearestDistance) { - m_nearestDistance = dist; - m_directionOfNearestObstacle = m_tank->property("direction").toDouble(); - } - - m_distanceToTurn -= 10.0; - if (m_distanceToTurn < 0.0) { - disconnect(m_tank, SIGNAL(actionCompleted()), this, SLOT(turnAlittle())); - connect(m_tank, SIGNAL(actionCompleted()), this, SIGNAL(nearestObstacleStraightAhead())); - m_tank->setProperty("direction", m_directionOfNearestObstacle); - } - - qreal currentDirection = m_tank->property("direction").toDouble(); - m_tank->setProperty("direction", currentDirection + 10.0); - } - -signals: - void nearestObstacleStraightAhead(); - -protected: - void onEntry(QEvent *) - { - connect(m_tank, SIGNAL(actionCompleted()), this, SLOT(turnAlittle())); - turnAlittle(); - } - - void onExit(QEvent *) - { - disconnect(m_tank, SIGNAL(actionCompleted()), this, SLOT(turnAlittle())); - disconnect(m_tank, SIGNAL(actionCompleted()), this, SLOT(nearestObstacleStraightAhead())); - } - -private: - QObject *m_tank; - - qreal m_distanceToTurn; - qreal m_nearestDistance; - qreal m_directionOfNearestObstacle; -}; - -class CollisionTransition: public QSignalTransition -{ -public: - CollisionTransition(QObject *tank, QState *turnTo) - : QSignalTransition(tank, SIGNAL(collision(QLineF))), - m_tank(tank), - m_turnTo(turnTo) - { - setTargetState(turnTo); - } - -protected: - bool eventTest(QEvent *event) const - { - bool b = QSignalTransition::eventTest(event); - if (b) { - QSignalEvent *se = static_cast(event); - m_lastLine = se->arguments().at(0).toLineF(); - } - return b; - } - - void onTransition(QEvent *) - { - qreal angleOfWall = m_lastLine.angle(); - - qreal newDirection; - if (qrand() % 2 == 0) - newDirection = angleOfWall; - else - newDirection = angleOfWall - 180.0; - - m_turnTo->assignProperty(m_tank, "direction", newDirection); - } - -private: - mutable QLineF m_lastLine; - QObject *m_tank; - QState *m_turnTo; -}; - -class ChaseState: public QState -{ - class GoToLocationState: public QState - { - public: - GoToLocationState(QObject *tank, QState *parentState = 0) - : QState(parentState), m_tank(tank), m_distance(0.0) - { - } - - void setDistance(qreal distance) { m_distance = distance; } - - protected: - void onEntry() - { - QMetaObject::invokeMethod(m_tank, "moveForwards", Q_ARG(qreal, m_distance)); - } - - private: - QObject *m_tank; - qreal m_distance; - }; - -public: - ChaseState(QObject *tank, QState *parentState = 0) : QState(parentState), m_tank(tank) - { - QState *fireCannon = new QState(this); - connect(fireCannon, SIGNAL(entered()), tank, SLOT(fireCannon())); - setInitialState(fireCannon); - - m_goToLocation = new GoToLocationState(this); - fireCannon->addTransition(tank, SIGNAL(actionCompleted()), m_goToLocation); - - m_turnToDirection = new QState(this); - m_goToLocation->addTransition(tank, SIGNAL(actionCompleted()), m_turnToDirection); - - QFinalState *finalState = new QFinalState(this); - m_turnToDirection->addTransition(tank, SIGNAL(actionCompleted()), finalState); - } - - void setDirection(qreal direction) - { - m_turnToDirection->assignProperty(m_tank, "direction", direction); - } - - void setDistance(qreal distance) - { - m_goToLocation->setDistance(distance); - } - -private: - QObject *m_tank; - GoToLocationState *m_goToLocation; - QState *m_turnToDirection; - -}; - -class TankSpottedTransition: public QSignalTransition -{ -public: - TankSpottedTransition(QObject *tank, ChaseState *target) : QSignalTransition(tank, SIGNAL(tankSpotted(qreal,qreal))), m_chase(target) - { - setTargetState(target); - } - -protected: - bool eventTest(QEvent *event) const - { - bool b = QSignalTransition::eventTest(event); - if (b) { - QSignalEvent *se = static_cast(event); - m_chase->setDirection(se->arguments().at(0).toDouble()); - m_chase->setDistance(se->arguments().at(1).toDouble()); - } - return b; - } - -private: - ChaseState *m_chase; -}; - -class SeekAi: public QObject, public Plugin -{ - Q_OBJECT - Q_INTERFACES(Plugin) -public: - SeekAi() { setObjectName("Seek and destroy"); } - - virtual QState *create(QState *parentState, QObject *tank); -}; - -#endif diff --git a/examples/statemachine/errorstateplugins/seek_ai/seek_ai.pro b/examples/statemachine/errorstateplugins/seek_ai/seek_ai.pro deleted file mode 100644 index 11bd242..0000000 --- a/examples/statemachine/errorstateplugins/seek_ai/seek_ai.pro +++ /dev/null @@ -1,13 +0,0 @@ -TEMPLATE = lib -CONFIG += plugin -INCLUDEPATH += ../.. -HEADERS = seek_ai.h -SOURCES = seek_ai.cpp -TARGET = $$qtLibraryTarget(seek_ai) -DESTDIR = ../../errorstate/plugins - -#! [0] -# install -target.path = $$[QT_INSTALL_EXAMPLES]/statemachine/errorstate/plugins -sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS seek_ai.pro -sources.path = $$[QT_INSTALL_EXAMPLES]/statemachine/errorstateplugins/seek_ai \ No newline at end of file diff --git a/examples/statemachine/errorstateplugins/spin_ai/spin_ai.cpp b/examples/statemachine/errorstateplugins/spin_ai/spin_ai.cpp deleted file mode 100644 index de95f41..0000000 --- a/examples/statemachine/errorstateplugins/spin_ai/spin_ai.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "spin_ai.h" - -#include - -QState *SpinAi::create(QState *parentState, QObject *tank) -{ - QState *topLevel = new QState(parentState); - QState *spinState = new SpinState(tank, topLevel); - topLevel->setInitialState(spinState); - - // When tank is spotted, fire two times and go back to spin state - QState *fireState = new QState(topLevel); - - QState *fireOnce = new QState(fireState); - fireState->setInitialState(fireOnce); - connect(fireOnce, SIGNAL(entered()), tank, SLOT(fireCannon())); - - QState *fireTwice = new QState(fireState); - connect(fireTwice, SIGNAL(entered()), tank, SLOT(fireCannon())); - - fireOnce->addTransition(tank, SIGNAL(actionCompleted()), fireTwice); - fireTwice->addTransition(tank, SIGNAL(actionCompleted()), spinState); - - spinState->addTransition(tank, SIGNAL(tankSpotted(qreal,qreal)), fireState); - - return topLevel; -} - -Q_EXPORT_PLUGIN2(spin_ai, SpinAi) diff --git a/examples/statemachine/errorstateplugins/spin_ai/spin_ai.h b/examples/statemachine/errorstateplugins/spin_ai/spin_ai.h deleted file mode 100644 index 6e220ed..0000000 --- a/examples/statemachine/errorstateplugins/spin_ai/spin_ai.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef SPIN_AI_H -#define SPIN_AI_H - -#include - -#include -#include -#include - -class SpinState: public QState -{ - Q_OBJECT -public: - SpinState(QObject *tank, QState *parent) : QState(parent), m_tank(tank) - { - } - -public slots: - void spin() - { - m_tank->setProperty("direction", 90.0); - } - -protected: - void onEntry(QEvent *) - { - connect(m_tank, SIGNAL(actionCompleted()), this, SLOT(spin())); - spin(); - } - -private: - QObject *m_tank; - -}; - -class SpinAi: public QObject, public Plugin -{ - Q_OBJECT - Q_INTERFACES(Plugin) -public: - SpinAi() { setObjectName("Spin and destroy"); } - - virtual QState *create(QState *parentState, QObject *tank); -}; - -#endif diff --git a/examples/statemachine/errorstateplugins/spin_ai/spin_ai.pro b/examples/statemachine/errorstateplugins/spin_ai/spin_ai.pro deleted file mode 100644 index c2fd937..0000000 --- a/examples/statemachine/errorstateplugins/spin_ai/spin_ai.pro +++ /dev/null @@ -1,13 +0,0 @@ -TEMPLATE = lib -CONFIG += plugin -INCLUDEPATH += ../.. -HEADERS = spin_ai.h -SOURCES = spin_ai.cpp -TARGET = $$qtLibraryTarget(spin_ai) -DESTDIR = ../../errorstate/plugins - -#! [0] -# install -target.path = $$[QT_INSTALL_EXAMPLES]/statemachine/errorstate/plugins -sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS spin_ai.pro -sources.path = $$[QT_INSTALL_EXAMPLES]/statemachine/errorstateplugins/spin_ai \ No newline at end of file diff --git a/examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.cpp b/examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.cpp deleted file mode 100644 index 5499ba3..0000000 --- a/examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "spin_ai_with_error.h" - -#include - -QState *SpinAiWithError::create(QState *parentState, QObject *tank) -{ - QState *topLevel = new QState(parentState); - QState *spinState = new SpinState(tank, topLevel); - topLevel->setInitialState(spinState); - - // When tank is spotted, fire two times and go back to spin state - // (no initial state set for fireState will lead to run-time error in machine) - QState *fireState = new QState(topLevel); - - QState *fireOnce = new QState(fireState); - connect(fireOnce, SIGNAL(entered()), tank, SLOT(fireCannon())); - - QState *fireTwice = new QState(fireState); - connect(fireTwice, SIGNAL(entered()), tank, SLOT(fireCannon())); - - fireOnce->addTransition(tank, SIGNAL(actionCompleted()), fireTwice); - fireTwice->addTransition(tank, SIGNAL(actionCompleted()), spinState); - - spinState->addTransition(tank, SIGNAL(tankSpotted(qreal,qreal)), fireState); - - return topLevel; -} - -Q_EXPORT_PLUGIN2(spin_ai_with_error, SpinAiWithError) diff --git a/examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.h b/examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.h deleted file mode 100644 index d520455..0000000 --- a/examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.h +++ /dev/null @@ -1,46 +0,0 @@ -#ifndef SPIN_AI_WITH_ERROR_H -#define SPIN_AI_WITH_ERROR_H - -#include - -#include -#include -#include - -class SpinState: public QState -{ - Q_OBJECT -public: - SpinState(QObject *tank, QState *parent) : QState(parent), m_tank(tank) - { - } - -public slots: - void spin() - { - m_tank->setProperty("direction", 90.0); - } - -protected: - void onEntry(QEvent *) - { - connect(m_tank, SIGNAL(actionCompleted()), this, SLOT(spin())); - spin(); - } - -private: - QObject *m_tank; - -}; - -class SpinAiWithError: public QObject, public Plugin -{ - Q_OBJECT - Q_INTERFACES(Plugin) -public: - SpinAiWithError() { setObjectName("Spin and destroy with runtime error in state machine"); } - - virtual QState *create(QState *parentState, QObject *tank); -}; - -#endif diff --git a/examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.pro b/examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.pro deleted file mode 100644 index 31f4c7f..0000000 --- a/examples/statemachine/errorstateplugins/spin_ai_with_error/spin_ai_with_error.pro +++ /dev/null @@ -1,13 +0,0 @@ -TEMPLATE = lib -CONFIG += plugin -INCLUDEPATH += ../.. -HEADERS = spin_ai_with_error.h -SOURCES = spin_ai_with_error.cpp -TARGET = $$qtLibraryTarget(spin_ai_with_error) -DESTDIR = ../../errorstate/plugins - -#! [0] -# install -target.path = $$[QT_INSTALL_EXAMPLES]/statemachine/errorstate/plugins -sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS spin_ai_with_error.pro -sources.path = $$[QT_INSTALL_EXAMPLES]/statemachine/errorstateplugins/spin_ai_with_error \ No newline at end of file diff --git a/examples/statemachine/statemachine.pro b/examples/statemachine/statemachine.pro index ea3e7a8..5074a3c 100644 --- a/examples/statemachine/statemachine.pro +++ b/examples/statemachine/statemachine.pro @@ -4,7 +4,9 @@ SUBDIRS = \ factorial \ pingpong \ trafficlight \ - twowaybutton + twowaybutton \ + tankgame \ + tankgameplugins # install target.path = $$[QT_INSTALL_EXAMPLES]/statemachine diff --git a/examples/statemachine/tankgame/gameitem.cpp b/examples/statemachine/tankgame/gameitem.cpp new file mode 100644 index 0000000..1a2af71 --- /dev/null +++ b/examples/statemachine/tankgame/gameitem.cpp @@ -0,0 +1,88 @@ +#include "gameitem.h" + +#include +#include + +GameItem::GameItem(QObject *parent) : QObject(parent) +{ +} + +QPointF GameItem::tryMove(const QPointF &requestedPosition, QLineF *collidedLine, + QGraphicsItem **collidedItem) const +{ + QLineF movementPath(pos(), requestedPosition); + + qreal cannonLength = 0.0; + { + QPointF p1 = boundingRect().center(); + QPointF p2 = QPointF(boundingRect().right() + 10.0, p1.y()); + + cannonLength = QLineF(mapToScene(p1), mapToScene(p2)).length(); + } + + movementPath.setLength(movementPath.length() + cannonLength); + + QRectF boundingRectPath(QPointF(qMin(movementPath.x1(), movementPath.x2()), qMin(movementPath.y1(), movementPath.y2())), + QPointF(qMax(movementPath.x1(), movementPath.x2()), qMax(movementPath.y1(), movementPath.y2()))); + + QList itemsInRect = scene()->items(boundingRectPath, Qt::IntersectsItemBoundingRect); + + QPointF nextPoint = requestedPosition; + QRectF sceneRect = scene()->sceneRect(); + + foreach (QGraphicsItem *item, itemsInRect) { + if (item == static_cast(this)) + continue; + + QPolygonF mappedBoundingRect = item->mapToScene(item->boundingRect()); + for (int i=0; isceneRect().topLeft(), scene()->sceneRect().bottomLeft()); + } + + if (nextPoint.x() > sceneRect.right()) { + nextPoint.rx() = sceneRect.right(); + if (collidedLine != 0) + *collidedLine = QLineF(scene()->sceneRect().topRight(), scene()->sceneRect().bottomRight()); + } + + if (nextPoint.y() < sceneRect.top()) { + nextPoint.ry() = sceneRect.top(); + if (collidedLine != 0) + *collidedLine = QLineF(scene()->sceneRect().topLeft(), scene()->sceneRect().topRight()); + } + + if (nextPoint.y() > sceneRect.bottom()) { + nextPoint.ry() = sceneRect.bottom(); + if (collidedLine != 0) + *collidedLine = QLineF(scene()->sceneRect().bottomLeft(), scene()->sceneRect().bottomRight()); + } + + return nextPoint; +} + diff --git a/examples/statemachine/tankgame/gameitem.h b/examples/statemachine/tankgame/gameitem.h new file mode 100644 index 0000000..43b8785 --- /dev/null +++ b/examples/statemachine/tankgame/gameitem.h @@ -0,0 +1,23 @@ +#ifndef GAMEITEM_H +#define GAMEITEM_H + +#include + +class QLineF; +class GameItem: public QObject, public QGraphicsItem +{ + Q_OBJECT +public: + enum { Type = UserType + 1 }; + int type() const { return Type; } + + GameItem(QObject *parent = 0); + + virtual void idle(qreal elapsed) = 0; + +protected: + QPointF tryMove(const QPointF &requestedPosition, QLineF *collidedLine = 0, + QGraphicsItem **collidedItem = 0) const; +}; + +#endif diff --git a/examples/statemachine/tankgame/main.cpp b/examples/statemachine/tankgame/main.cpp new file mode 100644 index 0000000..26fc1bb --- /dev/null +++ b/examples/statemachine/tankgame/main.cpp @@ -0,0 +1,12 @@ +#include +#include "mainwindow.h" + +int main(int argc, char **argv) +{ + QApplication app(argc, argv); + + MainWindow mainWindow; + mainWindow.show(); + + return app.exec(); +} diff --git a/examples/statemachine/tankgame/mainwindow.cpp b/examples/statemachine/tankgame/mainwindow.cpp new file mode 100644 index 0000000..3bc9bbe --- /dev/null +++ b/examples/statemachine/tankgame/mainwindow.cpp @@ -0,0 +1,264 @@ +#include "mainwindow.h" +#include "tankitem.h" +#include "rocketitem.h" +#include "plugin.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +MainWindow::MainWindow(QWidget *parent) + : QMainWindow(parent), m_scene(0), m_machine(0), m_runningState(0), m_started(false) +{ + init(); +} + +MainWindow::~MainWindow() +{ +} + +void MainWindow::addWall(const QRectF &wall) +{ + QGraphicsRectItem *item = new QGraphicsRectItem; + item->setRect(wall); + item->setBrush(Qt::darkGreen); + item->setPen(QPen(Qt::black, 0)); + + m_scene->addItem(item); +} + +void MainWindow::init() +{ + setWindowTitle("Pluggable Tank Game"); + + QGraphicsView *view = new QGraphicsView(this); + view->setRenderHints(QPainter::Antialiasing); + setCentralWidget(view); + + m_scene = new QGraphicsScene(this); + view->setScene(m_scene); + + QRectF sceneRect = QRectF(-200.0, -200.0, 400.0, 400.0); + m_scene->setSceneRect(sceneRect); + + { + TankItem *item = new TankItem(this); + + item->setPos(m_scene->sceneRect().topLeft() + QPointF(15.0, 15.0)); + item->setDirection(45.0); + item->setColor(Qt::red); + + m_spawns.append(item); + } + + { + TankItem *item = new TankItem(this); + + item->setPos(m_scene->sceneRect().topRight() + QPointF(-15.0, 15.0)); + item->setDirection(135.0); + item->setColor(Qt::green); + + m_spawns.append(item); + } + + { + TankItem *item = new TankItem(this); + + item->setPos(m_scene->sceneRect().bottomRight() + QPointF(-15.0, -15.0)); + item->setDirection(225.0); + item->setColor(Qt::blue); + + m_spawns.append(item); + } + + { + TankItem *item = new TankItem(this); + + item->setPos(m_scene->sceneRect().bottomLeft() + QPointF(15.0, -15.0)); + item->setDirection(315.0); + item->setColor(Qt::yellow); + + m_spawns.append(item); + } + + QPointF centerOfMap = sceneRect.center(); + + addWall(QRectF(centerOfMap + QPointF(-50.0, -60.0), centerOfMap + QPointF(50.0, -50.0))); + addWall(QRectF(centerOfMap - QPointF(-50.0, -60.0), centerOfMap - QPointF(50.0, -50.0))); + addWall(QRectF(centerOfMap + QPointF(-50.0, -50.0), centerOfMap + QPointF(-40.0, 50.0))); + addWall(QRectF(centerOfMap - QPointF(-50.0, -50.0), centerOfMap - QPointF(-40.0, 50.0))); + + addWall(QRectF(sceneRect.topLeft() + QPointF(sceneRect.width() / 2.0 - 5.0, -10.0), + sceneRect.topLeft() + QPointF(sceneRect.width() / 2.0 + 5.0, 100.0))); + addWall(QRectF(sceneRect.bottomLeft() + QPointF(sceneRect.width() / 2.0 - 5.0, 10.0), + sceneRect.bottomLeft() + QPointF(sceneRect.width() / 2.0 + 5.0, -100.0))); + addWall(QRectF(sceneRect.topLeft() + QPointF(-10.0, sceneRect.height() / 2.0 - 5.0), + sceneRect.topLeft() + QPointF(100.0, sceneRect.height() / 2.0 + 5.0))); + addWall(QRectF(sceneRect.topRight() + QPointF(10.0, sceneRect.height() / 2.0 - 5.0), + sceneRect.topRight() + QPointF(-100.0, sceneRect.height() / 2.0 + 5.0))); + + + QAction *addTankAction = menuBar()->addAction("&Add tank"); + QAction *runGameAction = menuBar()->addAction("&Run game"); + runGameAction->setObjectName("runGameAction"); + QAction *stopGameAction = menuBar()->addAction("&Stop game"); + menuBar()->addSeparator(); + QAction *quitAction = menuBar()->addAction("&Quit"); + + connect(addTankAction, SIGNAL(triggered()), this, SLOT(addTank())); + connect(quitAction, SIGNAL(triggered()), this, SLOT(close())); + + m_machine = new QStateMachine(this); + QState *stoppedState = new QState(m_machine->rootState()); + stoppedState->setObjectName("stoppedState"); + stoppedState->assignProperty(runGameAction, "enabled", true); + stoppedState->assignProperty(stopGameAction, "enabled", false); + stoppedState->assignProperty(this, "started", false); + m_machine->setInitialState(stoppedState); + + QState *spawnsAvailable = new QState(stoppedState); + spawnsAvailable->setObjectName("spawnsAvailable"); + spawnsAvailable->assignProperty(addTankAction, "enabled", true); + + QState *noSpawnsAvailable = new QState(stoppedState); + noSpawnsAvailable->setObjectName("noSpawnsAvailable"); + noSpawnsAvailable->assignProperty(addTankAction, "enabled", false); + + spawnsAvailable->addTransition(this, SIGNAL(mapFull()), noSpawnsAvailable); + + QHistoryState *hs = new QHistoryState(stoppedState); + hs->setDefaultState(spawnsAvailable); + + stoppedState->setInitialState(hs); + +//! [0] + m_runningState = new QState(QState::ParallelStates, m_machine->rootState()); +//! [0] + m_runningState->setObjectName("runningState"); + m_runningState->assignProperty(addTankAction, "enabled", false); + m_runningState->assignProperty(runGameAction, "enabled", false); + m_runningState->assignProperty(stopGameAction, "enabled", true); + + stoppedState->addTransition(runGameAction, SIGNAL(triggered()), m_runningState); + m_runningState->addTransition(stopGameAction, SIGNAL(triggered()), stoppedState); + + QTimer *timer = new QTimer(this); + timer->setInterval(100); + connect(timer, SIGNAL(timeout()), this, SLOT(runStep())); + connect(m_runningState, SIGNAL(entered()), timer, SLOT(start())); + connect(m_runningState, SIGNAL(exited()), timer, SLOT(stop())); + + m_machine->start(); + m_time.start(); +} + +void MainWindow::runStep() +{ + if (!m_started) { + m_time.restart(); + m_started = true; + } else { + int elapsed = m_time.elapsed(); + if (elapsed > 0) { + m_time.restart(); + qreal elapsedSecs = elapsed / 1000.0; + QList items = m_scene->items(); + foreach (QGraphicsItem *item, items) { + if (GameItem *gameItem = qgraphicsitem_cast(item)) + gameItem->idle(elapsedSecs); + } + } + } +} + +void MainWindow::addRocket() +{ + TankItem *tankItem = qobject_cast(sender()); + if (tankItem != 0) { + RocketItem *rocketItem = new RocketItem; + + QPointF s = tankItem->mapToScene(QPointF(tankItem->boundingRect().right() + 10.0, + tankItem->boundingRect().center().y())); + rocketItem->setPos(s); + rocketItem->setDirection(tankItem->direction()); + m_scene->addItem(rocketItem); + } +} + +void MainWindow::addTank() +{ + Q_ASSERT(!m_spawns.isEmpty()); + + QDir pluginsDir(qApp->applicationDirPath()); +#if defined(Q_OS_WIN) + if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release") + pluginsDir.cdUp(); +#elif defined(Q_OS_MAC) + if (pluginsDir.dirName() == "MacOS") { + pluginsDir.cdUp(); + pluginsDir.cdUp(); + pluginsDir.cdUp(); + } +#endif + + pluginsDir.cd("plugins"); + + QStringList itemNames; + QList items; + foreach (QString fileName, pluginsDir.entryList(QDir::Files)) { + QPluginLoader loader(pluginsDir.absoluteFilePath(fileName)); + QObject *possiblePlugin = loader.instance(); + if (Plugin *plugin = qobject_cast(possiblePlugin)) { + QString objectName = possiblePlugin->objectName(); + if (objectName.isEmpty()) + objectName = fileName; + + itemNames.append(objectName); + items.append(plugin); + } + } + + if (items.isEmpty()) { + QMessageBox::information(this, "No tank types found", "Please build the errorstateplugins directory"); + return; + } + + bool ok; +//! [1] + QString selectedName = QInputDialog::getItem(this, "Select a tank type", "Tank types", + itemNames, 0, false, &ok); +//! [1] + + if (ok && !selectedName.isEmpty()) { + int idx = itemNames.indexOf(selectedName); + if (Plugin *plugin = idx >= 0 ? items.at(idx) : 0) { + TankItem *tankItem = m_spawns.takeLast(); + m_scene->addItem(tankItem); + connect(tankItem, SIGNAL(cannonFired()), this, SLOT(addRocket())); + if (m_spawns.isEmpty()) + emit mapFull(); + + QState *region = new QState(m_runningState); +//! [2] + QState *pluginState = plugin->create(region, tankItem); +//! [2] + region->setInitialState(pluginState); + + + // If the plugin has an error it is disabled + QState *errorState = new QState(region); + errorState->assignProperty(tankItem, "enabled", false); + pluginState->setErrorState(errorState); + } + } +} + diff --git a/examples/statemachine/tankgame/mainwindow.h b/examples/statemachine/tankgame/mainwindow.h new file mode 100644 index 0000000..622dabe --- /dev/null +++ b/examples/statemachine/tankgame/mainwindow.h @@ -0,0 +1,46 @@ +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include +#include + +class QGraphicsScene; +class QStateMachine; +class QState; +class TankItem; +class MainWindow: public QMainWindow +{ + Q_OBJECT + Q_PROPERTY(bool started READ started WRITE setStarted) +public: + MainWindow(QWidget *parent = 0); + ~MainWindow(); + + void setStarted(bool b) { m_started = b; } + bool started() const { return m_started; } + +public slots: + void addTank(); + void addRocket(); + void runStep(); + +signals: + void mapFull(); + +private: + void init(); + void addWall(const QRectF &wall); + + QGraphicsScene *m_scene; + + QStateMachine *m_machine; + QState *m_runningState; + + QList m_spawns; + QTime m_time; + + bool m_started : 1; +}; + +#endif + diff --git a/examples/statemachine/tankgame/plugin.h b/examples/statemachine/tankgame/plugin.h new file mode 100644 index 0000000..2b48d43 --- /dev/null +++ b/examples/statemachine/tankgame/plugin.h @@ -0,0 +1,17 @@ +#ifndef PLUGIN_H +#define PLUGIN_H + +#include + +class QState; +class Plugin +{ +public: + virtual ~Plugin() {} + + virtual QState *create(QState *parentState, QObject *tank) = 0; +}; + +Q_DECLARE_INTERFACE(Plugin, "TankPlugin") + +#endif diff --git a/examples/statemachine/tankgame/rocketitem.cpp b/examples/statemachine/tankgame/rocketitem.cpp new file mode 100644 index 0000000..c324980 --- /dev/null +++ b/examples/statemachine/tankgame/rocketitem.cpp @@ -0,0 +1,60 @@ +#include "rocketitem.h" +#include "tankitem.h" + +#include +#include + +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +RocketItem::RocketItem(QObject *parent) + : GameItem(parent), m_direction(0.0), m_distance(300.0) +{ +} + +QRectF RocketItem::boundingRect() const +{ + return QRectF(-1.0, -1.0, 2.0, 2.0); +} + +void RocketItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + painter->setBrush(Qt::black); + painter->drawEllipse(boundingRect()); +} + +void RocketItem::idle(qreal elapsed) +{ + qreal dist = elapsed * speed(); + + m_distance -= dist; + if (m_distance < 0.0) { + scene()->removeItem(this); + delete this; + return; + } + + qreal a = m_direction * M_PI / 180.0; + + qreal yd = dist * sin(a); + qreal xd = dist * sin(M_PI / 2.0 - a); + + QPointF requestedPosition = pos() + QPointF(xd, yd); + QGraphicsItem *collidedItem = 0; + QPointF nextPosition = tryMove(requestedPosition, 0, &collidedItem); + if (requestedPosition == nextPosition) { + setPos(nextPosition); + } else { + if (GameItem *gameItem = qgraphicsitem_cast(collidedItem)) { + TankItem *tankItem = qobject_cast(gameItem); + if (tankItem != 0) + tankItem->hitByRocket(); + } + + scene()->removeItem(this); + delete this; + } +} diff --git a/examples/statemachine/tankgame/rocketitem.h b/examples/statemachine/tankgame/rocketitem.h new file mode 100644 index 0000000..189a1dd --- /dev/null +++ b/examples/statemachine/tankgame/rocketitem.h @@ -0,0 +1,26 @@ +#ifndef ROCKETITEM_H +#define ROCKETITEM_H + +#include "gameitem.h" + +class RocketItem: public GameItem +{ + Q_OBJECT +public: + RocketItem(QObject *parent = 0); + + virtual void idle(qreal elapsed); + qreal speed() const { return 100.0; } + void setDirection(qreal direction) { m_direction = direction; } + +protected: + virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + QRectF boundingRect() const; + +private: + qreal m_direction; + qreal m_distance; +}; + + +#endif diff --git a/examples/statemachine/tankgame/tankgame.pro b/examples/statemachine/tankgame/tankgame.pro new file mode 100644 index 0000000..f7b0760 --- /dev/null +++ b/examples/statemachine/tankgame/tankgame.pro @@ -0,0 +1,13 @@ +###################################################################### +# Automatically generated by qmake (2.01a) on 22. apr 14:11:33 2009 +###################################################################### + +TEMPLATE = app +TARGET = +DEPENDPATH += . +INCLUDEPATH += C:/dev/kinetic/examples/statemachine/tankgame/. . + +# Input +HEADERS += mainwindow.h plugin.h tankitem.h rocketitem.h gameitem.h +SOURCES += main.cpp mainwindow.cpp tankitem.cpp rocketitem.cpp gameitem.cpp +CONFIG += console diff --git a/examples/statemachine/tankgame/tankitem.cpp b/examples/statemachine/tankgame/tankitem.cpp new file mode 100644 index 0000000..5506a7e --- /dev/null +++ b/examples/statemachine/tankgame/tankitem.cpp @@ -0,0 +1,262 @@ +#include "tankitem.h" + +#include +#include +#include + +#include + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +class Action +{ +public: + Action(TankItem *item) : m_item(item) + { + } + + TankItem *item() const { return m_item; } + void setItem(TankItem *item) { m_item = item; } + + virtual bool apply(qreal timeDelta) = 0; + +private: + TankItem *m_item; +}; + +class MoveAction: public Action +{ +public: + MoveAction(TankItem *item, qreal distance) + : Action(item), m_distance(distance) + { + m_reverse = m_distance < 0.0; + } + + bool apply(qreal timeDelta) + { + qreal dist = timeDelta * item()->speed() * (m_reverse ? -1.0 : 1.0); + + bool done = false; + if (qAbs(m_distance) < qAbs(dist)) { + done = true; + dist = m_distance; + } + m_distance -= dist; + + qreal a = item()->direction() * M_PI / 180.0; + + qreal yd = dist * sin(a); + qreal xd = dist * sin(M_PI / 2.0 - a); + + item()->setPos(item()->pos() + QPointF(xd, yd)); + return !done; + } + +private: + qreal m_distance; + bool m_reverse; +}; + +class TurnAction: public Action +{ +public: + TurnAction(TankItem *item, qreal distance) + : Action(item), m_distance(distance) + { + m_reverse = m_distance < 0.0; + } + + bool apply(qreal timeDelta) + { + qreal dist = timeDelta * item()->angularSpeed() * (m_reverse ? -1.0 : 1.0); + bool done = false; + if (qAbs(m_distance) < qAbs(dist)) { + done = true; + dist = m_distance; + } + m_distance -= dist; + + item()->setDirection(item()->direction() + dist); + return !done; + } + +private: + qreal m_distance; + bool m_reverse; +}; + +TankItem::TankItem(QObject *parent) + : GameItem(parent), m_currentAction(0), m_currentDirection(0.0), m_enabled(true) +{ + connect(this, SIGNAL(cannonFired()), this, SIGNAL(actionCompleted())); +} + +void TankItem::idle(qreal elapsed) +{ + if (m_enabled) { + if (m_currentAction != 0) { + if (!m_currentAction->apply(elapsed)) { + setAction(0); + emit actionCompleted(); + } + + QGraphicsItem *item = 0; + qreal distance = distanceToObstacle(&item); + if (TankItem *tankItem = qgraphicsitem_cast(item)) + emit tankSpotted(tankItem->direction(), distance); + } + } +} + +void TankItem::hitByRocket() +{ + deleteLater(); +} + +void TankItem::setAction(Action *newAction) +{ + if (m_currentAction != 0) + delete m_currentAction; + + m_currentAction = newAction; +} + +void TankItem::fireCannon() +{ + emit cannonFired(); +} + +void TankItem::moveForwards(qreal length) +{ + setAction(new MoveAction(this, length)); +} + +void TankItem::moveBackwards(qreal length) +{ + setAction(new MoveAction(this, -length)); +} + +void TankItem::turn(qreal degrees) +{ + setAction(new TurnAction(this, degrees)); +} + +void TankItem::turnTo(qreal degrees) +{ + setAction(new TurnAction(this, degrees - direction())); +} + +void TankItem::stop() +{ + setAction(0); +} + +QVariant TankItem::itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value) +{ + if (change == ItemPositionChange && scene()) { + QPointF requestedPosition = value.toPointF(); + QLineF collidedLine; + QPointF nextPoint = tryMove(requestedPosition, &collidedLine); + if (nextPoint != requestedPosition) + emit collision(collidedLine); + return nextPoint; + } else { + return QGraphicsItem::itemChange(change, value); + } +} + + +void TankItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) +{ + QRectF brect = boundingRect(); + + painter->setBrush(m_color); + painter->setPen(Qt::black); + + // body + painter->drawRect(brect.adjusted(0.0, 4.0, -2.0, -4.0)); + + // cannon + QRectF cannonBase = brect.adjusted(10.0, 6.0, -12.0, -6.0); + painter->drawEllipse(cannonBase); + + painter->drawRect(QRectF(QPointF(cannonBase.center().x(), cannonBase.center().y() - 2.0), + QPointF(brect.right(), cannonBase.center().y() + 2.0))); + + // left track + painter->setBrush(QBrush(Qt::black, Qt::VerPattern)); + QRectF leftTrackRect = QRectF(brect.topLeft(), QPointF(brect.right() - 2.0, brect.top() + 4.0)); + painter->fillRect(leftTrackRect, Qt::darkYellow); + painter->drawRect(leftTrackRect); + + // right track + QRectF rightTrackRect = QRectF(QPointF(brect.left(), brect.bottom() - 4.0), + QPointF(brect.right() - 2.0, brect.bottom())); + painter->fillRect(rightTrackRect, Qt::darkYellow); + painter->drawRect(rightTrackRect); + + if (!m_enabled) { + painter->setPen(QPen(Qt::red, 5)); + + painter->drawEllipse(brect); + + QPainterPath path; + path.addEllipse(brect); + painter->setClipPath(path); + painter->drawLine(brect.topRight(), brect.bottomLeft()); + } +} + +QRectF TankItem::boundingRect() const +{ + return QRectF(-20.0, -10.0, 40.0, 20.0); +} + +qreal TankItem::direction() const +{ + return m_currentDirection; +} + +void TankItem::setDirection(qreal newDirection) +{ + int fullRotations = int(newDirection) / 360; + newDirection -= fullRotations * 360.0; + + qreal diff = newDirection - m_currentDirection; + m_currentDirection = newDirection; + rotate(diff); +} + +qreal TankItem::distanceToObstacle(QGraphicsItem **obstacle) const +{ + qreal dist = sqrt(pow(scene()->sceneRect().width(), 2) + pow(scene()->sceneRect().height(), 2)); + + qreal a = m_currentDirection * M_PI / 180.0; + + qreal yd = dist * sin(a); + qreal xd = dist * sin(M_PI / 2.0 - a); + + QPointF requestedPosition = pos() + QPointF(xd, yd); + QGraphicsItem *collidedItem = 0; + QPointF nextPosition = tryMove(requestedPosition, 0, &collidedItem); + if (collidedItem != 0) { + if (obstacle != 0) + *obstacle = collidedItem; + + QPointF d = nextPosition - pos(); + return sqrt(pow(d.x(), 2) + pow(d.y(), 2)); + } else { + return 0.0; + } +} + +qreal TankItem::distanceToObstacle() const +{ + return distanceToObstacle(0); +} + + + diff --git a/examples/statemachine/tankgame/tankitem.h b/examples/statemachine/tankgame/tankitem.h new file mode 100644 index 0000000..66f05aa --- /dev/null +++ b/examples/statemachine/tankgame/tankitem.h @@ -0,0 +1,67 @@ +#ifndef TANKITEM_H +#define TANKITEM_H + +#include "gameitem.h" + +#include + +class Action; +class TankItem: public GameItem +{ + Q_OBJECT + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled) + Q_PROPERTY(qreal direction READ direction WRITE turnTo) + Q_PROPERTY(qreal distanceToObstacle READ distanceToObstacle) +public: + TankItem(QObject *parent = 0); + + void setColor(const QColor &color) { m_color = color; } + QColor color() const { return m_color; } + + void idle(qreal elapsed); + void setDirection(qreal newDirection); + + qreal speed() const { return 90.0; } + qreal angularSpeed() const { return 90.0; } + + QRectF boundingRect() const; + + void hitByRocket(); + + void setEnabled(bool b) { m_enabled = b; } + bool enabled() const { return m_enabled; } + + qreal direction() const; + qreal distanceToObstacle() const; + qreal distanceToObstacle(QGraphicsItem **item) const; + +//! [0] +signals: + void tankSpotted(qreal direction, qreal distance); + void collision(const QLineF &collidedLine); + void actionCompleted(); + void cannonFired(); + +public slots: + void moveForwards(qreal length = 10.0); + void moveBackwards(qreal length = 10.0); + void turn(qreal degrees = 30.0); + void turnTo(qreal degrees = 0.0); + void stop(); + void fireCannon(); +//! [0] + +protected: + virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); + QVariant itemChange(QGraphicsItem::GraphicsItemChange change, const QVariant &value); + +private: + void setAction(Action *newAction); + + Action *m_currentAction; + qreal m_currentDirection; + QColor m_color; + bool m_enabled; +}; + +#endif diff --git a/examples/statemachine/tankgameplugins/random_ai/random_ai.pro b/examples/statemachine/tankgameplugins/random_ai/random_ai.pro new file mode 100644 index 0000000..5bc0b26 --- /dev/null +++ b/examples/statemachine/tankgameplugins/random_ai/random_ai.pro @@ -0,0 +1,13 @@ +TEMPLATE = lib +CONFIG += plugin +INCLUDEPATH += ../.. +HEADERS = random_ai_plugin.h +SOURCES = random_ai_plugin.cpp +TARGET = $$qtLibraryTarget(random_ai) +DESTDIR = ../../tankgame/plugins + +#! [0] +# install +target.path = $$[QT_INSTALL_EXAMPLES]/statemachine/tankgame/plugins +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS random_ai.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/statemachine/tankgameplugins/random_ai \ No newline at end of file diff --git a/examples/statemachine/tankgameplugins/random_ai/random_ai_plugin.cpp b/examples/statemachine/tankgameplugins/random_ai/random_ai_plugin.cpp new file mode 100644 index 0000000..c196247 --- /dev/null +++ b/examples/statemachine/tankgameplugins/random_ai/random_ai_plugin.cpp @@ -0,0 +1,38 @@ +#include "random_ai_plugin.h" + +#include +#include + +#include + +QState *RandomAiPlugin::create(QState *parentState, QObject *tank) +{ + qsrand(uint(time(NULL))); + + QState *topLevel = new QState(parentState); + + QState *selectNextActionState = new SelectActionState(topLevel); + topLevel->setInitialState(selectNextActionState); + + QState *fireState = new RandomDistanceState(topLevel); + connect(fireState, SIGNAL(distanceComputed(qreal)), tank, SLOT(fireCannon())); + selectNextActionState->addTransition(selectNextActionState, SIGNAL(fireSelected()), fireState); + + QState *moveForwardsState = new RandomDistanceState(topLevel); + connect(moveForwardsState, SIGNAL(distanceComputed(qreal)), tank, SLOT(moveForwards(qreal))); + selectNextActionState->addTransition(selectNextActionState, SIGNAL(moveForwardsSelected()), moveForwardsState); + + QState *moveBackwardsState = new RandomDistanceState(topLevel); + connect(moveBackwardsState, SIGNAL(distanceComputed(qreal)), tank, SLOT(moveBackwards(qreal))); + selectNextActionState->addTransition(selectNextActionState, SIGNAL(moveBackwardsSelected()), moveBackwardsState); + + QState *turnState = new RandomDistanceState(topLevel); + connect(turnState, SIGNAL(distanceComputed(qreal)), tank, SLOT(turn(qreal))); + selectNextActionState->addTransition(selectNextActionState, SIGNAL(turnSelected()), turnState); + + topLevel->addTransition(tank, SIGNAL(actionCompleted()), selectNextActionState); + + return topLevel; +} + +Q_EXPORT_PLUGIN2(random_ai, RandomAiPlugin) diff --git a/examples/statemachine/tankgameplugins/random_ai/random_ai_plugin.h b/examples/statemachine/tankgameplugins/random_ai/random_ai_plugin.h new file mode 100644 index 0000000..f5e3b6f --- /dev/null +++ b/examples/statemachine/tankgameplugins/random_ai/random_ai_plugin.h @@ -0,0 +1,64 @@ +#ifndef RANDOM_AI_PLUGIN_H +#define RANDOM_AI_PLUGIN_H + +#include +#include + +#include + +class SelectActionState: public QState +{ + Q_OBJECT +public: + SelectActionState(QState *parent = 0) : QState(parent) + { + } + +signals: + void fireSelected(); + void moveForwardsSelected(); + void moveBackwardsSelected(); + void turnSelected(); + +protected: + void onEntry(QEvent *) + { + int rand = qrand() % 4; + switch (rand) { + case 0: emit fireSelected(); break; + case 1: emit moveForwardsSelected(); break; + case 2: emit moveBackwardsSelected(); break; + case 3: emit turnSelected(); break; + }; + } +}; + +class RandomDistanceState: public QState +{ + Q_OBJECT +public: + RandomDistanceState(QState *parent = 0) : QState(parent) + { + } + +signals: + void distanceComputed(qreal distance); + +protected: + void onEntry(QEvent *) + { + emit distanceComputed(qreal(qrand() % 180)); + } +}; + +class RandomAiPlugin: public QObject, public Plugin +{ + Q_OBJECT + Q_INTERFACES(Plugin) +public: + RandomAiPlugin() { setObjectName("Random"); } + + virtual QState *create(QState *parentState, QObject *tank); +}; + +#endif // RANDOM_AI_PLUGIN_H diff --git a/examples/statemachine/tankgameplugins/seek_ai/seek_ai.cpp b/examples/statemachine/tankgameplugins/seek_ai/seek_ai.cpp new file mode 100644 index 0000000..2fb05d4 --- /dev/null +++ b/examples/statemachine/tankgameplugins/seek_ai/seek_ai.cpp @@ -0,0 +1,48 @@ +#include "seek_ai.h" + +QState *SeekAi::create(QState *parentState, QObject *tank) +{ + QState *topLevel = new QState(parentState); + topLevel->setObjectName("topLevel"); + + QState *seek = new QState(topLevel); + seek->setObjectName("seek"); + topLevel->setInitialState(seek); + + QState *lookForNearestWall = new SearchState(tank, seek); + lookForNearestWall->setObjectName("lookForNearestWall"); + seek->setInitialState(lookForNearestWall); + + QState *driveToFirstObstacle = new QState(seek); + driveToFirstObstacle->setObjectName("driveToFirstObstacle"); + lookForNearestWall->addTransition(lookForNearestWall, SIGNAL(nearestObstacleStraightAhead()), + driveToFirstObstacle); + + QState *drive = new QState(driveToFirstObstacle); + drive->setObjectName("drive"); + driveToFirstObstacle->setInitialState(drive); + connect(drive, SIGNAL(entered()), tank, SLOT(moveForwards())); + connect(drive, SIGNAL(exited()), tank, SLOT(stop())); + + // Go in loop + QState *finishedDriving = new QState(driveToFirstObstacle); + finishedDriving->setObjectName("finishedDriving"); + drive->addTransition(tank, SIGNAL(actionCompleted()), finishedDriving); + finishedDriving->addTransition(drive); + + QState *turnTo = new QState(seek); + turnTo->setObjectName("turnTo"); + driveToFirstObstacle->addTransition(new CollisionTransition(tank, turnTo)); + + turnTo->addTransition(tank, SIGNAL(actionCompleted()), driveToFirstObstacle); + + ChaseState *chase = new ChaseState(tank, topLevel); + chase->setObjectName("chase"); + seek->addTransition(new TankSpottedTransition(tank, chase)); + chase->addTransition(chase, SIGNAL(finished()), driveToFirstObstacle); + chase->addTransition(new TankSpottedTransition(tank, chase)); + + return topLevel; +} + +Q_EXPORT_PLUGIN2(seek_ai, SeekAi) diff --git a/examples/statemachine/tankgameplugins/seek_ai/seek_ai.h b/examples/statemachine/tankgameplugins/seek_ai/seek_ai.h new file mode 100644 index 0000000..2835988 --- /dev/null +++ b/examples/statemachine/tankgameplugins/seek_ai/seek_ai.h @@ -0,0 +1,204 @@ +#ifndef SEEK_AI_H +#define SEEK_AI_H + +#include + +#include +#include +#include +#include +#include +#include +#include + +class SearchState: public QState +{ + Q_OBJECT +public: + SearchState(QObject *tank, QState *parentState = 0) + : QState(parentState), + m_tank(tank), + m_distanceToTurn(360.0), + m_nearestDistance(-1.0), + m_directionOfNearestObstacle(0.0) + { + } + +public slots: + void turnAlittle() + { + qreal dist = m_tank->property("distanceToObstacle").toDouble(); + + if (m_nearestDistance < 0.0 || dist < m_nearestDistance) { + m_nearestDistance = dist; + m_directionOfNearestObstacle = m_tank->property("direction").toDouble(); + } + + m_distanceToTurn -= 10.0; + if (m_distanceToTurn < 0.0) { + disconnect(m_tank, SIGNAL(actionCompleted()), this, SLOT(turnAlittle())); + connect(m_tank, SIGNAL(actionCompleted()), this, SIGNAL(nearestObstacleStraightAhead())); + m_tank->setProperty("direction", m_directionOfNearestObstacle); + } + + qreal currentDirection = m_tank->property("direction").toDouble(); + m_tank->setProperty("direction", currentDirection + 10.0); + } + +signals: + void nearestObstacleStraightAhead(); + +protected: + void onEntry(QEvent *) + { + connect(m_tank, SIGNAL(actionCompleted()), this, SLOT(turnAlittle())); + turnAlittle(); + } + + void onExit(QEvent *) + { + disconnect(m_tank, SIGNAL(actionCompleted()), this, SLOT(turnAlittle())); + disconnect(m_tank, SIGNAL(actionCompleted()), this, SLOT(nearestObstacleStraightAhead())); + } + +private: + QObject *m_tank; + + qreal m_distanceToTurn; + qreal m_nearestDistance; + qreal m_directionOfNearestObstacle; +}; + +class CollisionTransition: public QSignalTransition +{ +public: + CollisionTransition(QObject *tank, QState *turnTo) + : QSignalTransition(tank, SIGNAL(collision(QLineF))), + m_tank(tank), + m_turnTo(turnTo) + { + setTargetState(turnTo); + } + +protected: + bool eventTest(QEvent *event) const + { + bool b = QSignalTransition::eventTest(event); + if (b) { + QSignalEvent *se = static_cast(event); + m_lastLine = se->arguments().at(0).toLineF(); + } + return b; + } + + void onTransition(QEvent *) + { + qreal angleOfWall = m_lastLine.angle(); + + qreal newDirection; + if (qrand() % 2 == 0) + newDirection = angleOfWall; + else + newDirection = angleOfWall - 180.0; + + m_turnTo->assignProperty(m_tank, "direction", newDirection); + } + +private: + mutable QLineF m_lastLine; + QObject *m_tank; + QState *m_turnTo; +}; + +class ChaseState: public QState +{ + class GoToLocationState: public QState + { + public: + GoToLocationState(QObject *tank, QState *parentState = 0) + : QState(parentState), m_tank(tank), m_distance(0.0) + { + } + + void setDistance(qreal distance) { m_distance = distance; } + + protected: + void onEntry() + { + QMetaObject::invokeMethod(m_tank, "moveForwards", Q_ARG(qreal, m_distance)); + } + + private: + QObject *m_tank; + qreal m_distance; + }; + +public: + ChaseState(QObject *tank, QState *parentState = 0) : QState(parentState), m_tank(tank) + { + QState *fireCannon = new QState(this); + connect(fireCannon, SIGNAL(entered()), tank, SLOT(fireCannon())); + setInitialState(fireCannon); + + m_goToLocation = new GoToLocationState(this); + fireCannon->addTransition(tank, SIGNAL(actionCompleted()), m_goToLocation); + + m_turnToDirection = new QState(this); + m_goToLocation->addTransition(tank, SIGNAL(actionCompleted()), m_turnToDirection); + + QFinalState *finalState = new QFinalState(this); + m_turnToDirection->addTransition(tank, SIGNAL(actionCompleted()), finalState); + } + + void setDirection(qreal direction) + { + m_turnToDirection->assignProperty(m_tank, "direction", direction); + } + + void setDistance(qreal distance) + { + m_goToLocation->setDistance(distance); + } + +private: + QObject *m_tank; + GoToLocationState *m_goToLocation; + QState *m_turnToDirection; + +}; + +class TankSpottedTransition: public QSignalTransition +{ +public: + TankSpottedTransition(QObject *tank, ChaseState *target) : QSignalTransition(tank, SIGNAL(tankSpotted(qreal,qreal))), m_chase(target) + { + setTargetState(target); + } + +protected: + bool eventTest(QEvent *event) const + { + bool b = QSignalTransition::eventTest(event); + if (b) { + QSignalEvent *se = static_cast(event); + m_chase->setDirection(se->arguments().at(0).toDouble()); + m_chase->setDistance(se->arguments().at(1).toDouble()); + } + return b; + } + +private: + ChaseState *m_chase; +}; + +class SeekAi: public QObject, public Plugin +{ + Q_OBJECT + Q_INTERFACES(Plugin) +public: + SeekAi() { setObjectName("Seek and destroy"); } + + virtual QState *create(QState *parentState, QObject *tank); +}; + +#endif diff --git a/examples/statemachine/tankgameplugins/seek_ai/seek_ai.pro b/examples/statemachine/tankgameplugins/seek_ai/seek_ai.pro new file mode 100644 index 0000000..0d8bf2e --- /dev/null +++ b/examples/statemachine/tankgameplugins/seek_ai/seek_ai.pro @@ -0,0 +1,13 @@ +TEMPLATE = lib +CONFIG += plugin +INCLUDEPATH += ../.. +HEADERS = seek_ai.h +SOURCES = seek_ai.cpp +TARGET = $$qtLibraryTarget(seek_ai) +DESTDIR = ../../tankgame/plugins + +#! [0] +# install +target.path = $$[QT_INSTALL_EXAMPLES]/statemachine/tankgame/plugins +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS seek_ai.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/statemachine/tankgameplugins/seek_ai \ No newline at end of file diff --git a/examples/statemachine/tankgameplugins/spin_ai/spin_ai.cpp b/examples/statemachine/tankgameplugins/spin_ai/spin_ai.cpp new file mode 100644 index 0000000..de95f41 --- /dev/null +++ b/examples/statemachine/tankgameplugins/spin_ai/spin_ai.cpp @@ -0,0 +1,29 @@ +#include "spin_ai.h" + +#include + +QState *SpinAi::create(QState *parentState, QObject *tank) +{ + QState *topLevel = new QState(parentState); + QState *spinState = new SpinState(tank, topLevel); + topLevel->setInitialState(spinState); + + // When tank is spotted, fire two times and go back to spin state + QState *fireState = new QState(topLevel); + + QState *fireOnce = new QState(fireState); + fireState->setInitialState(fireOnce); + connect(fireOnce, SIGNAL(entered()), tank, SLOT(fireCannon())); + + QState *fireTwice = new QState(fireState); + connect(fireTwice, SIGNAL(entered()), tank, SLOT(fireCannon())); + + fireOnce->addTransition(tank, SIGNAL(actionCompleted()), fireTwice); + fireTwice->addTransition(tank, SIGNAL(actionCompleted()), spinState); + + spinState->addTransition(tank, SIGNAL(tankSpotted(qreal,qreal)), fireState); + + return topLevel; +} + +Q_EXPORT_PLUGIN2(spin_ai, SpinAi) diff --git a/examples/statemachine/tankgameplugins/spin_ai/spin_ai.h b/examples/statemachine/tankgameplugins/spin_ai/spin_ai.h new file mode 100644 index 0000000..a331a3e --- /dev/null +++ b/examples/statemachine/tankgameplugins/spin_ai/spin_ai.h @@ -0,0 +1,46 @@ +#ifndef SPIN_AI_H +#define SPIN_AI_H + +#include + +#include +#include +#include + +class SpinState: public QState +{ + Q_OBJECT +public: + SpinState(QObject *tank, QState *parent) : QState(parent), m_tank(tank) + { + } + +public slots: + void spin() + { + m_tank->setProperty("direction", 90.0); + } + +protected: + void onEntry(QEvent *) + { + connect(m_tank, SIGNAL(actionCompleted()), this, SLOT(spin())); + spin(); + } + +private: + QObject *m_tank; + +}; + +class SpinAi: public QObject, public Plugin +{ + Q_OBJECT + Q_INTERFACES(Plugin) +public: + SpinAi() { setObjectName("Spin and destroy"); } + + virtual QState *create(QState *parentState, QObject *tank); +}; + +#endif diff --git a/examples/statemachine/tankgameplugins/spin_ai/spin_ai.pro b/examples/statemachine/tankgameplugins/spin_ai/spin_ai.pro new file mode 100644 index 0000000..8ab4da0 --- /dev/null +++ b/examples/statemachine/tankgameplugins/spin_ai/spin_ai.pro @@ -0,0 +1,13 @@ +TEMPLATE = lib +CONFIG += plugin +INCLUDEPATH += ../.. +HEADERS = spin_ai.h +SOURCES = spin_ai.cpp +TARGET = $$qtLibraryTarget(spin_ai) +DESTDIR = ../../tankgame/plugins + +#! [0] +# install +target.path = $$[QT_INSTALL_EXAMPLES]/statemachine/tankgame/plugins +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS spin_ai.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/statemachine/tankgameplugins/spin_ai \ No newline at end of file diff --git a/examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.cpp b/examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.cpp new file mode 100644 index 0000000..5499ba3 --- /dev/null +++ b/examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.cpp @@ -0,0 +1,29 @@ +#include "spin_ai_with_error.h" + +#include + +QState *SpinAiWithError::create(QState *parentState, QObject *tank) +{ + QState *topLevel = new QState(parentState); + QState *spinState = new SpinState(tank, topLevel); + topLevel->setInitialState(spinState); + + // When tank is spotted, fire two times and go back to spin state + // (no initial state set for fireState will lead to run-time error in machine) + QState *fireState = new QState(topLevel); + + QState *fireOnce = new QState(fireState); + connect(fireOnce, SIGNAL(entered()), tank, SLOT(fireCannon())); + + QState *fireTwice = new QState(fireState); + connect(fireTwice, SIGNAL(entered()), tank, SLOT(fireCannon())); + + fireOnce->addTransition(tank, SIGNAL(actionCompleted()), fireTwice); + fireTwice->addTransition(tank, SIGNAL(actionCompleted()), spinState); + + spinState->addTransition(tank, SIGNAL(tankSpotted(qreal,qreal)), fireState); + + return topLevel; +} + +Q_EXPORT_PLUGIN2(spin_ai_with_error, SpinAiWithError) diff --git a/examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.h b/examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.h new file mode 100644 index 0000000..5cfb364 --- /dev/null +++ b/examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.h @@ -0,0 +1,46 @@ +#ifndef SPIN_AI_WITH_ERROR_H +#define SPIN_AI_WITH_ERROR_H + +#include + +#include +#include +#include + +class SpinState: public QState +{ + Q_OBJECT +public: + SpinState(QObject *tank, QState *parent) : QState(parent), m_tank(tank) + { + } + +public slots: + void spin() + { + m_tank->setProperty("direction", 90.0); + } + +protected: + void onEntry(QEvent *) + { + connect(m_tank, SIGNAL(actionCompleted()), this, SLOT(spin())); + spin(); + } + +private: + QObject *m_tank; + +}; + +class SpinAiWithError: public QObject, public Plugin +{ + Q_OBJECT + Q_INTERFACES(Plugin) +public: + SpinAiWithError() { setObjectName("Spin and destroy with runtime error in state machine"); } + + virtual QState *create(QState *parentState, QObject *tank); +}; + +#endif diff --git a/examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.pro b/examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.pro new file mode 100644 index 0000000..124cf98 --- /dev/null +++ b/examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.pro @@ -0,0 +1,13 @@ +TEMPLATE = lib +CONFIG += plugin +INCLUDEPATH += ../.. +HEADERS = spin_ai_with_error.h +SOURCES = spin_ai_with_error.cpp +TARGET = $$qtLibraryTarget(spin_ai_with_error) +DESTDIR = ../../tankgame/plugins + +#! [0] +# install +target.path = $$[QT_INSTALL_EXAMPLES]/statemachine/tankgame/plugins +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS spin_ai_with_error.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/statemachine/tankgameplugins/spin_ai_with_error \ No newline at end of file diff --git a/examples/statemachine/tankgameplugins/tankgameplugins.pro b/examples/statemachine/tankgameplugins/tankgameplugins.pro new file mode 100644 index 0000000..a098e03 --- /dev/null +++ b/examples/statemachine/tankgameplugins/tankgameplugins.pro @@ -0,0 +1,11 @@ +TEMPLATE = subdirs +SUBDIRS = random_ai \ + spin_ai_with_error \ + spin_ai \ + seek_ai + +# install +target.path = $$[QT_INSTALL_EXAMPLES]/statemachine/tankgameplugins +sources.files = $$SOURCES $$HEADERS $$RESOURCES $$FORMS tankgameplugins.pro +sources.path = $$[QT_INSTALL_EXAMPLES]/statemachine/tankgameplugins +INSTALLS += target sources -- cgit v0.12 From b6c082925a6fd51d05fcba6276d1cb9bd5886374 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Tue, 12 May 2009 16:41:50 +0200 Subject: add State Machine to examples overview --- doc/src/examples-overview.qdoc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/doc/src/examples-overview.qdoc b/doc/src/examples-overview.qdoc index 549574d..92ccd4e 100644 --- a/doc/src/examples-overview.qdoc +++ b/doc/src/examples-overview.qdoc @@ -319,6 +319,14 @@ from displaying Web pages within a Qt user interface to an implementation of a basic function Web browser. + \section1 \l{Qt Examples#State Machine}{State Machine} + + Qt provides a powerful hierchical finite state machine through the Qt State + Machine classes. + + These examples demonstrate the fundamental aspects of implementing + Statecharts with Qt. + \section1 \l{Qt Examples#Qt for Embedded Linux}{Qt for Embedded Linux} \l{Qt Examples#Qt for Embedded Linux}{\inlineimage qt-embedded-examples.png -- cgit v0.12 From 3db6f6234eb36ec4b3d6e14dc48917762653cbd7 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Tue, 12 May 2009 17:02:27 +0200 Subject: document the statemachine/eventtransitions example --- doc/src/examples.qdoc | 5 +- doc/src/examples/eventtransitions.qdoc | 86 +++++++++++++++++++++++++ examples/statemachine/eventtransitions/main.cpp | 16 ++++- 3 files changed, 104 insertions(+), 3 deletions(-) create mode 100644 doc/src/examples/eventtransitions.qdoc diff --git a/doc/src/examples.qdoc b/doc/src/examples.qdoc index bf169e3..0153252 100644 --- a/doc/src/examples.qdoc +++ b/doc/src/examples.qdoc @@ -311,9 +311,10 @@ \section1 State Machine \list - \o \l{statemachine/twowaybutton}{Two-way Button}\raisedaster - \o \l{statemachine/trafficlight}{Traffic Light}\raisedaster + \o \l{statemachine/eventtransitions}{Event Transitions}\raisedaster \o \l{statemachine/pingpong}{Ping Pong States}\raisedaster + \o \l{statemachine/trafficlight}{Traffic Light}\raisedaster + \o \l{statemachine/twowaybutton}{Two-way Button}\raisedaster \endlist \section1 Threads diff --git a/doc/src/examples/eventtransitions.qdoc b/doc/src/examples/eventtransitions.qdoc new file mode 100644 index 0000000..3b956bb --- /dev/null +++ b/doc/src/examples/eventtransitions.qdoc @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example statemachine/eventtransitions + \title Event Transitions Example + + The Event Transitions example shows how to use event transitions, a + feature of \l{The State Machine Framework}. + + \snippet examples/statemachine/eventtransitions/main.cpp 0 + + The \c Window class's constructors begins by creating a button. + + \snippet examples/statemachine/eventtransitions/main.cpp 1 + + Two states, \c s1 and \c s2, are created; upon entry they will assign + "Outside" and "Inside" to the button's text, respectively. + + \snippet examples/statemachine/eventtransitions/main.cpp 2 + + When the button receives an event of type QEvent::Enter and the state + machine is in state \c s1, the machine will transition to state \c s2. + + \snippet examples/statemachine/eventtransitions/main.cpp 3 + + When the button receives an event of type QEvent::Leave and the state + machine is in state \c s2, the machine will transition back to state \c + s1. + + \snippet examples/statemachine/eventtransitions/main.cpp 4 + + Next, the state \c s3 is created. \c s3 will be entered when the button + receives an event of type QEvent::MouseButtonPress and the state machine + is in state \c s2. When the button receives an event of type + QEvent::MouseButtonRelease and the state machine is in state \c s3, the + machine will transition back to state \c s2. + + \snippet examples/statemachine/eventtransitions/main.cpp 5 + + Finally, the states are added to the machine as top-level states, the + initial state is set to be \c s1 ("Outside"), and the machine is started. + + \snippet examples/statemachine/eventtransitions/main.cpp 6 + + The main() function constructs a Window object and shows it. + +*/ diff --git a/examples/statemachine/eventtransitions/main.cpp b/examples/statemachine/eventtransitions/main.cpp index f564b7e..aba0c73 100644 --- a/examples/statemachine/eventtransitions/main.cpp +++ b/examples/statemachine/eventtransitions/main.cpp @@ -46,6 +46,7 @@ #include #endif +//! [0] class Window : public QWidget { public: @@ -54,7 +55,9 @@ public: { QPushButton *button = new QPushButton(this); button->setGeometry(QRect(100, 100, 100, 100)); +//! [0] +//! [1] QStateMachine *machine = new QStateMachine(this); QState *s1 = new QState(); @@ -62,15 +65,21 @@ public: QState *s2 = new QState(); s2->assignProperty(button, "text", "Inside"); +//! [1] +//! [2] QEventTransition *enterTransition = new QEventTransition(button, QEvent::Enter); enterTransition->setTargetState(s2); s1->addTransition(enterTransition); +//! [2] +//! [3] QEventTransition *leaveTransition = new QEventTransition(button, QEvent::Leave); leaveTransition->setTargetState(s1); s2->addTransition(leaveTransition); +//! [3] +//! [4] QState *s3 = new QState(); s3->assignProperty(button, "text", "Pressing..."); @@ -81,16 +90,20 @@ public: QEventTransition *releaseTransition = new QEventTransition(button, QEvent::MouseButtonRelease); releaseTransition->setTargetState(s2); s3->addTransition(releaseTransition); +//! [4] +//! [5] machine->addState(s1); machine->addState(s2); machine->addState(s3); + machine->setInitialState(s1); - QObject::connect(machine, SIGNAL(finished()), qApp, SLOT(quit())); machine->start(); } }; +//! [5] +//! [6] int main(int argc, char **argv) { QApplication app(argc, argv); @@ -100,3 +113,4 @@ int main(int argc, char **argv) return app.exec(); } +//! [6] -- cgit v0.12 From 09d1e6ee7d93c9fb658b2be5fe49698bf3faa0d6 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Tue, 12 May 2009 18:38:32 +0200 Subject: correctly handle multiple signal transitions for same (object,signal) The signal was not disconnected at the right time. We now store the number of active signal transitions for a particular (object,signal) and only disconnect when the count drops to zero. --- src/corelib/statemachine/qstate.cpp | 2 ++ src/corelib/statemachine/qstatemachine.cpp | 33 +++++++++++-------- src/corelib/statemachine/qstatemachine_p.h | 3 +- tests/auto/qstatemachine/tst_qstatemachine.cpp | 45 ++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 15 deletions(-) diff --git a/src/corelib/statemachine/qstate.cpp b/src/corelib/statemachine/qstate.cpp index 3a3bfc3..f1528b8 100644 --- a/src/corelib/statemachine/qstate.cpp +++ b/src/corelib/statemachine/qstate.cpp @@ -327,6 +327,8 @@ QAbstractTransition *QState::addTransition(QAbstractTransition *transition) } } transition->setParent(this); + if (machine() != 0 && machine()->configuration().contains(this)) + QStateMachinePrivate::get(machine())->registerTransitions(this); return transition; } diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp index 24af8e4..40a465a 100644 --- a/src/corelib/statemachine/qstatemachine.cpp +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -1311,8 +1311,10 @@ void QStateMachinePrivate::registerSignalTransition(QSignalTransition *transitio sender->metaObject()->className(), signal.constData()); return; } - QList &connectedSignalIndexes = connections[sender]; - if (!connectedSignalIndexes.contains(signalIndex)) { + QVector &connectedSignalIndexes = connections[sender]; + if (connectedSignalIndexes.size() <= signalIndex) + connectedSignalIndexes.resize(signalIndex+1); + if (connectedSignalIndexes.at(signalIndex) == 0) { #ifndef QT_STATEMACHINE_SOLUTION if (!signalEventGenerator) signalEventGenerator = new QSignalEventGenerator(q); @@ -1329,8 +1331,8 @@ void QStateMachinePrivate::registerSignalTransition(QSignalTransition *transitio #endif return; } - connectedSignalIndexes.append(signalIndex); } + ++connectedSignalIndexes[signalIndex]; QSignalTransitionPrivate::get(transition)->signalIndex = signalIndex; #ifdef QSTATEMACHINE_DEBUG qDebug() << q << ": added signal transition from" << transition->sourceState() @@ -1345,17 +1347,20 @@ void QStateMachinePrivate::unregisterSignalTransition(QSignalTransition *transit if (signalIndex == -1) return; // not registered #ifndef QT_STATEMACHINE_SOLUTION + QSignalTransitionPrivate::get(transition)->signalIndex = -1; const QObject *sender = QSignalTransitionPrivate::get(transition)->sender; - QList &connectedSignalIndexes = connections[sender]; - Q_ASSERT(connectedSignalIndexes.contains(signalIndex)); - Q_ASSERT(signalEventGenerator != 0); - bool ok = QMetaObject::disconnect(sender, signalIndex, signalEventGenerator, - signalEventGenerator->metaObject()->methodOffset()); - if (ok) { - connectedSignalIndexes.removeOne(signalIndex); - if (connectedSignalIndexes.isEmpty()) + QVector &connectedSignalIndexes = connections[sender]; + Q_ASSERT(connectedSignalIndexes.size() > signalIndex); + Q_ASSERT(connectedSignalIndexes.at(signalIndex) != 0); + if (--connectedSignalIndexes[signalIndex] == 0) { + Q_ASSERT(signalEventGenerator != 0); + QMetaObject::disconnect(sender, signalIndex, signalEventGenerator, + signalEventGenerator->metaObject()->methodOffset()); + int sum = 0; + for (int i = 0; i < connectedSignalIndexes.size(); ++i) + sum += connectedSignalIndexes.at(i); + if (sum == 0) connections.remove(sender); - QSignalTransitionPrivate::get(transition)->signalIndex = -1; } #endif } @@ -1420,8 +1425,8 @@ void QStateMachinePrivate::unregisterEventTransition(QEventTransition *transitio void QStateMachinePrivate::handleTransitionSignal(const QObject *sender, int signalIndex, void **argv) { - const QList &connectedSignalIndexes = connections[sender]; - Q_ASSERT(connectedSignalIndexes.contains(signalIndex)); + const QVector &connectedSignalIndexes = connections[sender]; + Q_ASSERT(connectedSignalIndexes.at(signalIndex) != 0); const QMetaObject *meta = sender->metaObject(); QMetaMethod method = meta->method(signalIndex); QList parameterTypes = method.parameterTypes(); diff --git a/src/corelib/statemachine/qstatemachine_p.h b/src/corelib/statemachine/qstatemachine_p.h index 47b139c..4bf9ce2 100644 --- a/src/corelib/statemachine/qstatemachine_p.h +++ b/src/corelib/statemachine/qstatemachine_p.h @@ -61,6 +61,7 @@ #include #include #include +#include #include "qstate.h" #include "qstate_p.h" @@ -202,7 +203,7 @@ public: #ifndef QT_STATEMACHINE_SOLUTION QSignalEventGenerator *signalEventGenerator; #endif - QHash > connections; + QHash > connections; #ifndef QT_NO_STATEMACHINE_EVENTFILTER QHash > qobjectEvents; #endif diff --git a/tests/auto/qstatemachine/tst_qstatemachine.cpp b/tests/auto/qstatemachine/tst_qstatemachine.cpp index 9058cb6..3f94ad9 100644 --- a/tests/auto/qstatemachine/tst_qstatemachine.cpp +++ b/tests/auto/qstatemachine/tst_qstatemachine.cpp @@ -1711,6 +1711,51 @@ void tst_QStateMachine::signalTransitions() QTRY_COMPARE(finishedSpy.count(), 1); } + // Multiple transitions for same (object,signal) + { + QStateMachine machine; + SignalEmitter emitter; + QState *s0 = new QState(machine.rootState()); + QState *s1 = new QState(machine.rootState()); + QSignalTransition *t0 = s0->addTransition(&emitter, SIGNAL(signalWithNoArg()), s1); + QSignalTransition *t1 = s1->addTransition(&emitter, SIGNAL(signalWithNoArg()), s0); + + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + machine.setInitialState(s0); + machine.start(); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s0)); + + emitter.emitSignalWithNoArg(); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s1)); + + s0->removeTransition(t0); + emitter.emitSignalWithNoArg(); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s0)); + + emitter.emitSignalWithNoArg(); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s0)); + + s1->removeTransition(t1); + emitter.emitSignalWithNoArg(); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s0)); + + s0->addTransition(t0); + s1->addTransition(t1); + emitter.emitSignalWithNoArg(); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s1)); + } } void tst_QStateMachine::eventTransitions() -- cgit v0.12 From 8589b7c526f8c7d436c6a7c3bfb39dc505eff78e Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Tue, 12 May 2009 18:52:58 +0200 Subject: doc: Add documentation for Tank Game example --- doc/src/examples.qdoc | 1 + doc/src/examples/tankgame.qdoc | 115 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 116 insertions(+) create mode 100644 doc/src/examples/tankgame.qdoc diff --git a/doc/src/examples.qdoc b/doc/src/examples.qdoc index 0153252..6fb903c 100644 --- a/doc/src/examples.qdoc +++ b/doc/src/examples.qdoc @@ -315,6 +315,7 @@ \o \l{statemachine/pingpong}{Ping Pong States}\raisedaster \o \l{statemachine/trafficlight}{Traffic Light}\raisedaster \o \l{statemachine/twowaybutton}{Two-way Button}\raisedaster + \o \l{statemachine/tankgame}{Tank Game}\raisedaster \endlist \section1 Threads diff --git a/doc/src/examples/tankgame.qdoc b/doc/src/examples/tankgame.qdoc new file mode 100644 index 0000000..e10161c --- /dev/null +++ b/doc/src/examples/tankgame.qdoc @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example statemachine/tankgame + \title Tank Game Example + + The Tank Game example is part of the in \l{The State Machine Framework}. It shows how to use + parallel states to implement artificial intelligence controllers that run in parallel, and error + states to handle run-time errors in parts of the state graph created by external plugins. + + \image tankgame-example.png + + In this example we write a simple game. The application runs a state machine with two main + states: A "stopped" state and a "running" state. The user can load plugins from the disk by + selecting the "Add tank" menu item. + + When the "Add tank" menu item is selected, the "plugins" subdirectory in the example's + directory is searched for compatible plugins. If any are found, they will be listed in a + dialog box created using QInputDialog::getItem(). + + \snippet examples/statemachine/tankgame/mainwindow.cpp 1 + + If the user selects a plugin, the application will construct a TankItem object, which inherits + from QGraphicsItem and QObject, and which implements an agreed-upon interface using the + meta-object mechanism. + + \snippet examples/statemachine/tankgame/tankitem.h 0 + + The tank item will be passed to the plugin's create() function. This will in turn return a + QState object which is expected to implement an artificial intelligence which controls the + tank and attempts to destroy other tanks it detects. + + \snippet examples/statemachine/tankgame/mainwindow.cpp 2 + + Each returned QState object becomes a descendant of a \c region in the "running" state, which is + defined as a parallel state. This means that entering the "running" state will cause each of the + plugged-in QState objects to be entered simultaneously, allowing the tanks to run independently + of each other. + + \snippet examples/statemachine/tankgame/mainwindow.cpp 0 + + The maximum number of tanks on the map is four, and when this number is reached, the + "Add tank" menu item should be disabled. This is implemented by giving the "stopped" state two + children which define whether the map is full or not. + + To make sure that we go into the correct child state when returning from the "running" state + (if the "Stop game" menu item is selected while the game is running) we also give the "stopped" + state a history state which we make the initial state of "stopped" state. + + \snippet examples/statemachine/tankgame/mainwindow.cpp 3 + + Since part of the state graph is defined by external plugins, we have no way of controlling + whether they contain errors. By default, run-time errors are handled in the state machine by + entering a top level state which prints out an error message and never exits. If we were to + use this default behavior, a run-time error in any of the plugins would cause the "running" + state to exit, and thus all the other tanks to stop running as well. A better solution would + be if the broken plugin was disabled and the rest of the tanks allowed to continue as before. + + This is done by setting the error state of the plugin's top-most state to a special error state + defined specifically for the plugin in question. + + \snippet examples/statemachine/tankgame/mainwindow.cpp 3 + + If a run-time error occurs in \c pluginState or any of its descendants, the state machine will + search the hierarchy of ancestors until it finds a state whose error state is different from + \c null. (Note that if we are worried that a plugin could inadvertedly be overriding our + error state, we could search the descendants of \c pluginState and verify that their error + states are set to \c null before accepting the plugin.) + + The specialized \c errorState sets the "enabled" property of the tank item in question to false, + causing it to be painted with a red cross over it to indicate that it is no longer running. + Since the error state is a child of the same region in the parallel "running" state as + \c pluginState, it will not exit the "running" state, and the other tanks will continue running + without disruption. + +*/ -- cgit v0.12 From 288de11968d4b71c97fc5d1a175d4c4ac7023552 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Wed, 13 May 2009 11:01:32 +0200 Subject: doc: Correct names of snippets in docs for Tank Game example --- doc/src/examples/tankgame.qdoc | 2 +- examples/statemachine/tankgame/mainwindow.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/src/examples/tankgame.qdoc b/doc/src/examples/tankgame.qdoc index e10161c..1501a99 100644 --- a/doc/src/examples/tankgame.qdoc +++ b/doc/src/examples/tankgame.qdoc @@ -98,7 +98,7 @@ This is done by setting the error state of the plugin's top-most state to a special error state defined specifically for the plugin in question. - \snippet examples/statemachine/tankgame/mainwindow.cpp 3 + \snippet examples/statemachine/tankgame/mainwindow.cpp 4 If a run-time error occurs in \c pluginState or any of its descendants, the state machine will search the hierarchy of ancestors until it finds a state whose error state is different from diff --git a/examples/statemachine/tankgame/mainwindow.cpp b/examples/statemachine/tankgame/mainwindow.cpp index 3bc9bbe..870bc9c 100644 --- a/examples/statemachine/tankgame/mainwindow.cpp +++ b/examples/statemachine/tankgame/mainwindow.cpp @@ -135,8 +135,10 @@ void MainWindow::init() spawnsAvailable->addTransition(this, SIGNAL(mapFull()), noSpawnsAvailable); +//! [3] QHistoryState *hs = new QHistoryState(stoppedState); hs->setDefaultState(spawnsAvailable); +//! [3] stoppedState->setInitialState(hs); @@ -255,9 +257,11 @@ void MainWindow::addTank() // If the plugin has an error it is disabled +//! [4] QState *errorState = new QState(region); errorState->assignProperty(tankItem, "enabled", false); pluginState->setErrorState(errorState); +//! [4] } } } -- cgit v0.12 From 5c7c8208c559d82e96aa7aa8c753c224d89022a2 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Wed, 13 May 2009 11:20:14 +0200 Subject: document statemachine/factorial example --- doc/src/examples.qdoc | 1 + doc/src/examples/factorial.qdoc | 102 +++++++++++++++++++++++++++++++ doc/src/images/factorial-example.png | Bin 0 -> 4032 bytes examples/statemachine/factorial/main.cpp | 64 +++++++++++-------- 4 files changed, 141 insertions(+), 26 deletions(-) create mode 100644 doc/src/examples/factorial.qdoc create mode 100644 doc/src/images/factorial-example.png diff --git a/doc/src/examples.qdoc b/doc/src/examples.qdoc index 6fb903c..6603390 100644 --- a/doc/src/examples.qdoc +++ b/doc/src/examples.qdoc @@ -312,6 +312,7 @@ \list \o \l{statemachine/eventtransitions}{Event Transitions}\raisedaster + \o \l{statemachine/factorial}{Factorial States}\raisedaster \o \l{statemachine/pingpong}{Ping Pong States}\raisedaster \o \l{statemachine/trafficlight}{Traffic Light}\raisedaster \o \l{statemachine/twowaybutton}{Two-way Button}\raisedaster diff --git a/doc/src/examples/factorial.qdoc b/doc/src/examples/factorial.qdoc new file mode 100644 index 0000000..2a72e0a --- /dev/null +++ b/doc/src/examples/factorial.qdoc @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** Contact: Qt Software Information (qt-info@nokia.com) +** +** This file is part of the documentation of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the either Technology Preview License Agreement or the +** Beta Release License Agreement. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain +** additional rights. These rights are described in the Nokia Qt LGPL +** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this +** package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU General Public License version 3.0 requirements will be +** met: http://www.gnu.org/copyleft/gpl.html. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at qt-sales@nokia.com. +** $QT_END_LICENSE$ +** +****************************************************************************/ + +/*! + \example statemachine/factorial + \title Factorial States Example + + The Factorial States example shows how to use \l{The State Machine + Framework} to calculate the factorial of an integer. + + The statechart for calculating the factorial looks as follows: + + \img factorial-example.png + \omit + \caption This is a caption + \endomit + + In other words, the state machine calculates the factorial of 6 and prints + the result. + + \snippet examples/statemachine/factorial/main.cpp 0 + + The Factorial class is used to hold the data of the computation, \c x and + \c fac. It also provides a signal that's emitted whenever the value of \c + x changes. + + \snippet examples/statemachine/factorial/main.cpp 1 + + The FactorialLoopTransition class implements the guard (\c x > 1) and + calculations (\c fac = \c x * \c fac; \c x = \c x - 1) of the factorial + loop. + + \snippet examples/statemachine/factorial/main.cpp 2 + + The FactorialDoneTransition class implements the guard (\c x <= 1) that + terminates the factorial computation. It also prints the final result to + standard output. + + \snippet examples/statemachine/factorial/main.cpp 3 + + The application's main() function first creates the application object, a + Factorial object and a state machine. + + \snippet examples/statemachine/factorial/main.cpp 4 + + The \c compute state is created, and the initial values of \c x and \c fac + are defined. A FactorialLoopTransition object is created and added to the + state. + + \snippet examples/statemachine/factorial/main.cpp 5 + + A final state, \c done, is created, and a FactorialDoneTransition object + is created with \c done as its target state. The transition is then added + to the \c compute state. + + \snippet examples/statemachine/factorial/main.cpp 6 + + The machine's initial state is set to be the \c compute state. We connect + the QStateMachine::finished() signal to the QCoreApplication::quit() slot, + so the application will quit when the state machine's work is + done. Finally, the state machine is started, and the application's event + loop is entered. + + */ diff --git a/doc/src/images/factorial-example.png b/doc/src/images/factorial-example.png new file mode 100644 index 0000000..8fb1cc6 Binary files /dev/null and b/doc/src/images/factorial-example.png differ diff --git a/examples/statemachine/factorial/main.cpp b/examples/statemachine/factorial/main.cpp index 2b63690..ae5928f 100644 --- a/examples/statemachine/factorial/main.cpp +++ b/examples/statemachine/factorial/main.cpp @@ -48,6 +48,7 @@ #include #endif +//! [0] class Factorial : public QObject { Q_OBJECT @@ -55,10 +56,8 @@ class Factorial : public QObject Q_PROPERTY(int fac READ fac WRITE setFac) public: Factorial(QObject *parent = 0) - : QObject(parent) + : QObject(parent), m_x(-1), m_fac(1) { - m_fac = 1; - m_x = -1; } int x() const @@ -71,7 +70,7 @@ public: if (x == m_x) return; m_x = x; - emit xChanged(); + emit xChanged(x); } int fac() const @@ -85,28 +84,34 @@ public: } Q_SIGNALS: - void xChanged(); + void xChanged(int value); private: int m_x; int m_fac; }; +//! [0] +//! [1] class FactorialLoopTransition : public QSignalTransition { public: FactorialLoopTransition(Factorial *fact) - : QSignalTransition(fact, SIGNAL(xChanged())), m_fact(fact) + : QSignalTransition(fact, SIGNAL(xChanged(int))), m_fact(fact) {} - virtual bool eventTest(QEvent *) const + virtual bool eventTest(QEvent *e) const { - return m_fact->property("x").toInt() > 1; + if (!QSignalTransition::eventTest(e)) + return false; + QSignalEvent *se = static_cast(e); + return se->arguments().at(0).toInt() > 1; } - virtual void onTransition(QEvent *) + virtual void onTransition(QEvent *e) { - int x = m_fact->property("x").toInt(); + QSignalEvent *se = static_cast(e); + int x = se->arguments().at(0).toInt(); int fac = m_fact->property("fac").toInt(); m_fact->setProperty("fac", x * fac); m_fact->setProperty("x", x - 1); @@ -115,17 +120,22 @@ public: private: Factorial *m_fact; }; +//! [1] +//! [2] class FactorialDoneTransition : public QSignalTransition { public: FactorialDoneTransition(Factorial *fact) - : QSignalTransition(fact, SIGNAL(xChanged())), m_fact(fact) + : QSignalTransition(fact, SIGNAL(xChanged(int))), m_fact(fact) {} - virtual bool eventTest(QEvent *) const + virtual bool eventTest(QEvent *e) const { - return m_fact->property("x").toInt() <= 1; + if (!QSignalTransition::eventTest(e)) + return false; + QSignalEvent *se = static_cast(e); + return se->arguments().at(0).toInt() <= 1; } virtual void onTransition(QEvent *) @@ -136,35 +146,37 @@ public: private: Factorial *m_fact; }; +//! [2] +//! [3] int main(int argc, char **argv) { QCoreApplication app(argc, argv); - Factorial factorial; - QStateMachine machine; +//! [3] - QState *computing = new QState(machine.rootState()); - computing->addTransition(new FactorialLoopTransition(&factorial)); +//! [4] + QState *compute = new QState(machine.rootState()); + compute->assignProperty(&factorial, "fac", 1); + compute->assignProperty(&factorial, "x", 6); + compute->addTransition(new FactorialLoopTransition(&factorial)); +//! [4] +//! [5] QFinalState *done = new QFinalState(machine.rootState()); FactorialDoneTransition *doneTransition = new FactorialDoneTransition(&factorial); doneTransition->setTargetState(done); - computing->addTransition(doneTransition); - - QState *initialize = new QState(machine.rootState()); - initialize->assignProperty(&factorial, "x", 6); - FactorialLoopTransition *enterLoopTransition = new FactorialLoopTransition(&factorial); - enterLoopTransition->setTargetState(computing); - initialize->addTransition(enterLoopTransition); + compute->addTransition(doneTransition); +//! [5] +//! [6] + machine.setInitialState(compute); QObject::connect(&machine, SIGNAL(finished()), &app, SLOT(quit())); - - machine.setInitialState(initialize); machine.start(); return app.exec(); } +//! [6] #include "main.moc" -- cgit v0.12 From 6b2d3e437bf1632191f62c603f754f895d4122eb Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Wed, 13 May 2009 12:39:11 +0200 Subject: kill the QT_STATEMACHINE_SOLUTION define We won't release another Qt Solution, so the define is no longer needed. --- src/corelib/statemachine/qabstractstate.cpp | 27 +------- src/corelib/statemachine/qabstractstate.h | 3 - src/corelib/statemachine/qabstractstate_p.h | 8 --- src/corelib/statemachine/qabstracttransition.cpp | 51 ++------------- src/corelib/statemachine/qabstracttransition.h | 3 - src/corelib/statemachine/qabstracttransition_p.h | 8 --- src/corelib/statemachine/qeventtransition.cpp | 4 -- src/corelib/statemachine/qeventtransition.h | 6 -- src/corelib/statemachine/qfinalstate.h | 4 -- src/corelib/statemachine/qhistorystate.h | 4 -- src/corelib/statemachine/qsignaleventgenerator_p.h | 9 +-- src/corelib/statemachine/qsignaltransition.cpp | 4 -- src/corelib/statemachine/qsignaltransition.h | 4 -- src/corelib/statemachine/qstate.cpp | 9 --- src/corelib/statemachine/qstate.h | 4 -- src/corelib/statemachine/qstatemachine.cpp | 76 ++-------------------- src/corelib/statemachine/qstatemachine.h | 9 +-- src/corelib/statemachine/qstatemachine_p.h | 11 +--- src/gui/statemachine/qguistatemachine.cpp | 5 -- 19 files changed, 16 insertions(+), 233 deletions(-) diff --git a/src/corelib/statemachine/qabstractstate.cpp b/src/corelib/statemachine/qabstractstate.cpp index 3f84314..7e59a7d 100644 --- a/src/corelib/statemachine/qabstractstate.cpp +++ b/src/corelib/statemachine/qabstractstate.cpp @@ -129,36 +129,16 @@ void QAbstractStatePrivate::emitExited() Constructs a new state with the given \a parent state. */ QAbstractState::QAbstractState(QState *parent) - : QObject( -#ifndef QT_STATEMACHINE_SOLUTION - *new QAbstractStatePrivate, -#endif - parent) -#ifdef QT_STATEMACHINE_SOLUTION - , d_ptr(new QAbstractStatePrivate) -#endif + : QObject(*new QAbstractStatePrivate, parent) { -#ifdef QT_STATEMACHINE_SOLUTION - d_ptr->q_ptr = this; -#endif } /*! \internal */ QAbstractState::QAbstractState(QAbstractStatePrivate &dd, QState *parent) - : QObject( -#ifndef QT_STATEMACHINE_SOLUTION - dd, -#endif - parent) -#ifdef QT_STATEMACHINE_SOLUTION - , d_ptr(&dd) -#endif + : QObject(dd, parent) { -#ifdef QT_STATEMACHINE_SOLUTION - d_ptr->q_ptr = this; -#endif } /*! @@ -166,9 +146,6 @@ QAbstractState::QAbstractState(QAbstractStatePrivate &dd, QState *parent) */ QAbstractState::~QAbstractState() { -#ifdef QT_STATEMACHINE_SOLUTION - delete d_ptr; -#endif } /*! diff --git a/src/corelib/statemachine/qabstractstate.h b/src/corelib/statemachine/qabstractstate.h index f6b4b21..d0ebb52 100644 --- a/src/corelib/statemachine/qabstractstate.h +++ b/src/corelib/statemachine/qabstractstate.h @@ -76,9 +76,6 @@ protected: bool event(QEvent *e); protected: -#ifdef QT_STATEMACHINE_SOLUTION - QAbstractStatePrivate *d_ptr; -#endif QAbstractState(QAbstractStatePrivate &dd, QState *parent); private: diff --git a/src/corelib/statemachine/qabstractstate_p.h b/src/corelib/statemachine/qabstractstate_p.h index 6c09696..1a99d6c 100644 --- a/src/corelib/statemachine/qabstractstate_p.h +++ b/src/corelib/statemachine/qabstractstate_p.h @@ -53,9 +53,7 @@ // We mean it. // -#ifndef QT_STATEMACHINE_SOLUTION #include -#endif QT_BEGIN_NAMESPACE @@ -63,9 +61,7 @@ class QStateMachine; class QAbstractState; class Q_CORE_EXPORT QAbstractStatePrivate -#ifndef QT_STATEMACHINE_SOLUTION : public QObjectPrivate -#endif { Q_DECLARE_PUBLIC(QAbstractState) @@ -82,10 +78,6 @@ public: void emitEntered(); void emitExited(); - -#ifdef QT_STATEMACHINE_SOLUTION - QAbstractState *q_ptr; -#endif }; QT_END_NAMESPACE diff --git a/src/corelib/statemachine/qabstracttransition.cpp b/src/corelib/statemachine/qabstracttransition.cpp index 6ddb416..f7a7d34 100644 --- a/src/corelib/statemachine/qabstracttransition.cpp +++ b/src/corelib/statemachine/qabstracttransition.cpp @@ -147,18 +147,8 @@ QState *QAbstractTransitionPrivate::sourceState() const Constructs a new QAbstractTransition object with the given \a sourceState. */ QAbstractTransition::QAbstractTransition(QState *sourceState) - : QObject( -#ifndef QT_STATEMACHINE_SOLUTION - *new QAbstractTransitionPrivate, -#endif - sourceState) -#ifdef QT_STATEMACHINE_SOLUTION - , d_ptr(new QAbstractTransitionPrivate) -#endif + : QObject(*new QAbstractTransitionPrivate, sourceState) { -#ifdef QT_STATEMACHINE_SOLUTION - d_ptr->q_ptr = this; -#endif } /*! @@ -167,18 +157,8 @@ QAbstractTransition::QAbstractTransition(QState *sourceState) */ QAbstractTransition::QAbstractTransition(const QList &targets, QState *sourceState) - : QObject( -#ifndef QT_STATEMACHINE_SOLUTION - *new QAbstractTransitionPrivate, -#endif - sourceState) -#ifdef QT_STATEMACHINE_SOLUTION - , d_ptr(new QAbstractTransitionPrivate) -#endif + : QObject(*new QAbstractTransitionPrivate, sourceState) { -#ifdef QT_STATEMACHINE_SOLUTION - d_ptr->q_ptr = this; -#endif setTargetStates(targets); } @@ -187,18 +167,8 @@ QAbstractTransition::QAbstractTransition(const QList &targets, */ QAbstractTransition::QAbstractTransition(QAbstractTransitionPrivate &dd, QState *parent) - : QObject( -#ifndef QT_STATEMACHINE_SOLUTION - dd, -#endif - parent) -#ifdef QT_STATEMACHINE_SOLUTION - , d_ptr(&dd) -#endif + : QObject(dd, parent) { -#ifdef QT_STATEMACHINE_SOLUTION - d_ptr->q_ptr = this; -#endif } /*! @@ -207,18 +177,8 @@ QAbstractTransition::QAbstractTransition(QAbstractTransitionPrivate &dd, QAbstractTransition::QAbstractTransition(QAbstractTransitionPrivate &dd, const QList &targets, QState *parent) - : QObject( -#ifndef QT_STATEMACHINE_SOLUTION - dd, -#endif - parent) -#ifdef QT_STATEMACHINE_SOLUTION - , d_ptr(&dd) -#endif + : QObject(dd, parent) { -#ifdef QT_STATEMACHINE_SOLUTION - d_ptr->q_ptr = this; -#endif setTargetStates(targets); } @@ -227,9 +187,6 @@ QAbstractTransition::QAbstractTransition(QAbstractTransitionPrivate &dd, */ QAbstractTransition::~QAbstractTransition() { -#ifdef QT_STATEMACHINE_SOLUTION - delete d_ptr; -#endif } /*! diff --git a/src/corelib/statemachine/qabstracttransition.h b/src/corelib/statemachine/qabstracttransition.h index e207944..8f0cefa 100644 --- a/src/corelib/statemachine/qabstracttransition.h +++ b/src/corelib/statemachine/qabstracttransition.h @@ -95,9 +95,6 @@ protected: bool event(QEvent *e); protected: -#ifdef QT_STATEMACHINE_SOLUTION - QAbstractTransitionPrivate *d_ptr; -#endif QAbstractTransition(QAbstractTransitionPrivate &dd, QState *parent); QAbstractTransition(QAbstractTransitionPrivate &dd, const QList &targets, QState *parent); diff --git a/src/corelib/statemachine/qabstracttransition_p.h b/src/corelib/statemachine/qabstracttransition_p.h index eb0ec21..156e70e 100644 --- a/src/corelib/statemachine/qabstracttransition_p.h +++ b/src/corelib/statemachine/qabstracttransition_p.h @@ -53,9 +53,7 @@ // We mean it. // -#ifndef QT_STATEMACHINE_SOLUTION #include -#endif #include #include @@ -68,9 +66,7 @@ class QStateMachine; class QAbstractTransition; class Q_CORE_EXPORT QAbstractTransitionPrivate -#ifndef QT_STATEMACHINE_SOLUTION : public QObjectPrivate -#endif { Q_DECLARE_PUBLIC(QAbstractTransition) public: @@ -89,10 +85,6 @@ public: #ifndef QT_NO_ANIMATION QList animations; #endif - -#ifdef QT_STATEMACHINE_SOLUTION - QAbstractTransition *q_ptr; -#endif }; QT_END_NAMESPACE diff --git a/src/corelib/statemachine/qeventtransition.cpp b/src/corelib/statemachine/qeventtransition.cpp index 86259e4..3ae3ed9 100644 --- a/src/corelib/statemachine/qeventtransition.cpp +++ b/src/corelib/statemachine/qeventtransition.cpp @@ -255,11 +255,7 @@ void QEventTransition::setEventObject(QObject *object) bool QEventTransition::eventTest(QEvent *event) const { Q_D(const QEventTransition); -#ifdef QT_STATEMACHINE_SOLUTION - if (event->type() == QEvent::Type(QEvent::User-3)) { -#else if (event->type() == QEvent::Wrapped) { -#endif QWrappedEvent *we = static_cast(event); return (we->object() == d->object) && (we->event()->type() == d->eventType); diff --git a/src/corelib/statemachine/qeventtransition.h b/src/corelib/statemachine/qeventtransition.h index a128cee..68ee4fc 100644 --- a/src/corelib/statemachine/qeventtransition.h +++ b/src/corelib/statemachine/qeventtransition.h @@ -42,11 +42,7 @@ #ifndef QEVENTTRANSITION_H #define QEVENTTRANSITION_H -#ifndef QT_STATEMACHINE_SOLUTION #include -#else -#include "qabstracttransition.h" -#endif #include QT_BEGIN_HEADER @@ -60,9 +56,7 @@ class Q_CORE_EXPORT QEventTransition : public QAbstractTransition { Q_OBJECT Q_PROPERTY(QObject* eventObject READ eventObject WRITE setEventObject) -#ifndef QT_STATEMACHINE_SOLUTION Q_PROPERTY(QEvent::Type eventType READ eventType WRITE setEventType) -#endif public: QEventTransition(QState *sourceState = 0); QEventTransition(QObject *object, QEvent::Type type, QState *sourceState = 0); diff --git a/src/corelib/statemachine/qfinalstate.h b/src/corelib/statemachine/qfinalstate.h index eb8aa0f..fa68394 100644 --- a/src/corelib/statemachine/qfinalstate.h +++ b/src/corelib/statemachine/qfinalstate.h @@ -42,11 +42,7 @@ #ifndef QFINALSTATE_H #define QFINALSTATE_H -#ifndef QT_STATEMACHINE_SOLUTION #include -#else -#include "qabstractstate.h" -#endif QT_BEGIN_HEADER diff --git a/src/corelib/statemachine/qhistorystate.h b/src/corelib/statemachine/qhistorystate.h index d0f75de..a0682bd 100644 --- a/src/corelib/statemachine/qhistorystate.h +++ b/src/corelib/statemachine/qhistorystate.h @@ -42,11 +42,7 @@ #ifndef QHISTORYSTATE_H #define QHISTORYSTATE_H -#ifndef QT_STATEMACHINE_SOLUTION #include -#else -#include "qabstractstate.h" -#endif QT_BEGIN_HEADER diff --git a/src/corelib/statemachine/qsignaleventgenerator_p.h b/src/corelib/statemachine/qsignaleventgenerator_p.h index d18def8..cf0ea1e 100644 --- a/src/corelib/statemachine/qsignaleventgenerator_p.h +++ b/src/corelib/statemachine/qsignaleventgenerator_p.h @@ -62,11 +62,7 @@ class QStateMachine; class QSignalEventGenerator : public QObject { public: - QSignalEventGenerator( -#ifdef QT_STATEMACHINE_SOLUTION - int signalIndex, -#endif - QStateMachine *parent); + QSignalEventGenerator(QStateMachine *parent); static const QMetaObject staticMetaObject; virtual const QMetaObject *metaObject() const; @@ -74,9 +70,6 @@ public: virtual int qt_metacall(QMetaObject::Call, int, void **argv); private: -#ifdef QT_STATEMACHINE_SOLUTION - int signalIndex; -#endif Q_DISABLE_COPY(QSignalEventGenerator) }; diff --git a/src/corelib/statemachine/qsignaltransition.cpp b/src/corelib/statemachine/qsignaltransition.cpp index d5833bd..fd17292 100644 --- a/src/corelib/statemachine/qsignaltransition.cpp +++ b/src/corelib/statemachine/qsignaltransition.cpp @@ -228,11 +228,7 @@ void QSignalTransition::setSignal(const QByteArray &signal) bool QSignalTransition::eventTest(QEvent *event) const { Q_D(const QSignalTransition); -#ifndef QT_STATEMACHINE_SOLUTION if (event->type() == QEvent::Signal) { -#else - if (event->type() == QEvent::Type(QEvent::User-1)) { -#endif if (d->signalIndex == -1) return false; QSignalEvent *se = static_cast(event); diff --git a/src/corelib/statemachine/qsignaltransition.h b/src/corelib/statemachine/qsignaltransition.h index 98a9ae7..7f457b8 100644 --- a/src/corelib/statemachine/qsignaltransition.h +++ b/src/corelib/statemachine/qsignaltransition.h @@ -42,11 +42,7 @@ #ifndef QSIGNALTRANSITION_H #define QSIGNALTRANSITION_H -#ifndef QT_STATEMACHINE_SOLUTION #include -#else -#include "qabstracttransition.h" -#endif QT_BEGIN_HEADER diff --git a/src/corelib/statemachine/qstate.cpp b/src/corelib/statemachine/qstate.cpp index f1528b8..8893bfe 100644 --- a/src/corelib/statemachine/qstate.cpp +++ b/src/corelib/statemachine/qstate.cpp @@ -193,9 +193,6 @@ QList QStatePrivate::childStates() const { QList result; QList::const_iterator it; -#ifdef QT_STATEMACHINE_SOLUTION - const QObjectList &children = q_func()->children(); -#endif for (it = children.constBegin(); it != children.constEnd(); ++it) { QAbstractState *s = qobject_cast(*it); if (!s || qobject_cast(s)) @@ -209,9 +206,6 @@ QList QStatePrivate::historyStates() const { QList result; QList::const_iterator it; -#ifdef QT_STATEMACHINE_SOLUTION - const QObjectList &children = q_func()->children(); -#endif for (it = children.constBegin(); it != children.constEnd(); ++it) { QHistoryState *h = qobject_cast(*it); if (h) @@ -224,9 +218,6 @@ QList QStatePrivate::transitions() const { QList result; QList::const_iterator it; -#ifdef QT_STATEMACHINE_SOLUTION - const QObjectList &children = q_func()->children(); -#endif for (it = children.constBegin(); it != children.constEnd(); ++it) { QAbstractTransition *t = qobject_cast(*it); if (t) diff --git a/src/corelib/statemachine/qstate.h b/src/corelib/statemachine/qstate.h index 73955d7..6729c69 100644 --- a/src/corelib/statemachine/qstate.h +++ b/src/corelib/statemachine/qstate.h @@ -42,11 +42,7 @@ #ifndef QSTATE_H #define QSTATE_H -#ifndef QT_STATEMACHINE_SOLUTION #include -#else -#include "qabstractstate.h" -#endif QT_BEGIN_HEADER diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp index 40a465a..c2b2696 100644 --- a/src/corelib/statemachine/qstatemachine.cpp +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -54,10 +54,8 @@ #include "qfinalstate.h" #include "qhistorystate.h" #include "qhistorystate_p.h" -#ifndef QT_STATEMACHINE_SOLUTION #include "private/qobject_p.h" #include "private/qthread_p.h" -#endif #ifndef QT_NO_STATEMACHINE_EVENTFILTER #include "qeventtransition.h" @@ -68,11 +66,7 @@ #ifndef QT_NO_ANIMATION #include "qpropertyanimation.h" #include "qanimationgroup.h" -# ifndef QT_STATEMACHINE_SOLUTION -# include -# else -# include "qvariantanimation_p.h" -# endif +#include #endif #include @@ -212,9 +206,7 @@ QStateMachinePrivate::QStateMachinePrivate() globalRestorePolicy = QStateMachine::DoNotRestoreProperties; rootState = 0; initialErrorStateForRoot = 0; -#ifndef QT_STATEMACHINE_SOLUTION signalEventGenerator = 0; -#endif #ifndef QT_NO_ANIMATION animationsEnabled = true; #endif @@ -1274,8 +1266,6 @@ void QStateMachinePrivate::unregisterTransition(QAbstractTransition *transition) #endif } -#ifndef QT_STATEMACHINE_SOLUTION - static int senderSignalIndex(const QObject *sender) { QObjectPrivate *d = QObjectPrivate::get(const_cast(sender)); @@ -1292,8 +1282,6 @@ static int senderSignalIndex(const QObject *sender) return d->currentSender->signal; } -#endif - void QStateMachinePrivate::registerSignalTransition(QSignalTransition *transition) { Q_Q(QStateMachine); @@ -1315,12 +1303,8 @@ void QStateMachinePrivate::registerSignalTransition(QSignalTransition *transitio if (connectedSignalIndexes.size() <= signalIndex) connectedSignalIndexes.resize(signalIndex+1); if (connectedSignalIndexes.at(signalIndex) == 0) { -#ifndef QT_STATEMACHINE_SOLUTION if (!signalEventGenerator) signalEventGenerator = new QSignalEventGenerator(q); -#else - QSignalEventGenerator *signalEventGenerator = new QSignalEventGenerator(signalIndex, q); -#endif bool ok = QMetaObject::connect(sender, signalIndex, signalEventGenerator, signalEventGenerator->metaObject()->methodOffset()); if (!ok) { @@ -1346,7 +1330,6 @@ void QStateMachinePrivate::unregisterSignalTransition(QSignalTransition *transit int signalIndex = QSignalTransitionPrivate::get(transition)->signalIndex; if (signalIndex == -1) return; // not registered -#ifndef QT_STATEMACHINE_SOLUTION QSignalTransitionPrivate::get(transition)->signalIndex = -1; const QObject *sender = QSignalTransitionPrivate::get(transition)->sender; QVector &connectedSignalIndexes = connections[sender]; @@ -1362,7 +1345,6 @@ void QStateMachinePrivate::unregisterSignalTransition(QSignalTransition *transit if (sum == 0) connections.remove(sender); } -#endif } void QStateMachinePrivate::unregisterAllTransitions() @@ -1392,10 +1374,8 @@ void QStateMachinePrivate::registerEventTransition(QEventTransition *transition) QObject *object = QEventTransitionPrivate::get(transition)->object; if (!object) return; -#ifndef QT_STATEMACHINE_SOLUTION QObjectPrivate *od = QObjectPrivate::get(object); if (!od->eventFilters.contains(q)) -#endif object->installEventFilter(q); qobjectEvents[object].insert(transition->eventType()); QEventTransitionPrivate::get(transition)->registered = true; @@ -1449,36 +1429,16 @@ void QStateMachinePrivate::handleTransitionSignal(const QObject *sender, int sig Constructs a new state machine with the given \a parent. */ QStateMachine::QStateMachine(QObject *parent) - : QObject( -#ifndef QT_STATEMACHINE_SOLUTION - *new QStateMachinePrivate, -#endif - parent) -#ifdef QT_STATEMACHINE_SOLUTION - , d_ptr(new QStateMachinePrivate) -#endif + : QObject(*new QStateMachinePrivate, parent) { -#ifdef QT_STATEMACHINE_SOLUTION - d_ptr->q_ptr = this; -#endif } /*! \internal */ QStateMachine::QStateMachine(QStateMachinePrivate &dd, QObject *parent) - : QObject( -#ifndef QT_STATEMACHINE_SOLUTION - dd, -#endif - parent) -#ifdef QT_STATEMACHINE_SOLUTION - , d_ptr(&dd) -#endif + : QObject(dd, parent) { -#ifdef QT_STATEMACHINE_SOLUTION - d_ptr->q_ptr = this; -#endif } /*! @@ -1486,9 +1446,6 @@ QStateMachine::QStateMachine(QStateMachinePrivate &dd, QObject *parent) */ QStateMachine::~QStateMachine() { -#ifdef QT_STATEMACHINE_SOLUTION - delete d_ptr; -#endif } namespace { @@ -2088,11 +2045,9 @@ int QSignalEventGenerator::qt_metacall(QMetaObject::Call _c, int _id, void **_a) if (_c == QMetaObject::InvokeMetaMethod) { switch (_id) { case 0: { -#ifndef QT_STATEMACHINE_SOLUTION // ### in Qt 4.6 we can use QObject::senderSignalIndex() int signalIndex = senderSignalIndex(this); Q_ASSERT(signalIndex != -1); -#endif QStateMachine *machine = qobject_cast(parent()); QStateMachinePrivate::get(machine)->handleTransitionSignal(sender(), signalIndex, _a); break; @@ -2104,15 +2059,8 @@ int QSignalEventGenerator::qt_metacall(QMetaObject::Call _c, int _id, void **_a) return _id; } -QSignalEventGenerator::QSignalEventGenerator( -#ifdef QT_STATEMACHINE_SOLUTION - int sigIdx, -#endif - QStateMachine *parent) +QSignalEventGenerator::QSignalEventGenerator(QStateMachine *parent) : QObject(parent) -#ifdef QT_STATEMACHINE_SOLUTION - , signalIndex(sigIdx) -#endif { } @@ -2143,13 +2091,8 @@ QSignalEventGenerator::QSignalEventGenerator( */ QSignalEvent::QSignalEvent(const QObject *sender, int signalIndex, const QList &arguments) - : -#ifndef QT_STATEMACHINE_SOLUTION - QEvent(QEvent::Signal) -#else - QEvent(QEvent::Type(QEvent::User-1)) -#endif - , m_sender(sender), m_signalIndex(signalIndex), m_arguments(arguments) + : QEvent(QEvent::Signal), m_sender(sender), + m_signalIndex(signalIndex), m_arguments(arguments) { } @@ -2208,12 +2151,7 @@ QSignalEvent::~QSignalEvent() and \a event. */ QWrappedEvent::QWrappedEvent(QObject *object, QEvent *event) -#ifdef QT_STATEMACHINE_SOLUTION - : QEvent(QEvent::Type(QEvent::User-3)) -#else - : QEvent(QEvent::Wrapped) -#endif - , m_object(object), m_event(event) + : QEvent(QEvent::Wrapped), m_object(object), m_event(event) { } diff --git a/src/corelib/statemachine/qstatemachine.h b/src/corelib/statemachine/qstatemachine.h index 5dc6c0b..2a98a9a 100644 --- a/src/corelib/statemachine/qstatemachine.h +++ b/src/corelib/statemachine/qstatemachine.h @@ -42,11 +42,7 @@ #ifndef QSTATEMACHINE_H #define QSTATEMACHINE_H -#ifndef QT_STATEMACHINE_SOLUTION -# include -#else -# include "qabstractstate.h" -#endif +#include #include #include @@ -151,9 +147,6 @@ protected: bool event(QEvent *e); protected: -#ifdef QT_STATEMACHINE_SOLUTION - QStateMachinePrivate *d_ptr; -#endif QStateMachine(QStateMachinePrivate &dd, QObject *parent); private: diff --git a/src/corelib/statemachine/qstatemachine_p.h b/src/corelib/statemachine/qstatemachine_p.h index 4bf9ce2..9b4b861 100644 --- a/src/corelib/statemachine/qstatemachine_p.h +++ b/src/corelib/statemachine/qstatemachine_p.h @@ -53,9 +53,7 @@ // We mean it. // -#ifndef QT_STATEMACHINE_SOLUTION #include -#endif #include #include #include @@ -84,9 +82,7 @@ class QAbstractAnimation; class QStateMachine; class Q_CORE_EXPORT QStateMachinePrivate -#ifndef QT_STATEMACHINE_SOLUTION : public QObjectPrivate -#endif { Q_DECLARE_PUBLIC(QStateMachine) public: @@ -200,9 +196,8 @@ public: #endif // QT_NO_ANIMATION -#ifndef QT_STATEMACHINE_SOLUTION QSignalEventGenerator *signalEventGenerator; -#endif + QHash > connections; #ifndef QT_NO_STATEMACHINE_EVENTFILTER QHash > qobjectEvents; @@ -215,10 +210,6 @@ public: }; static const Handler *handler; - -#ifdef QT_STATEMACHINE_SOLUTION - QStateMachine *q_ptr; -#endif }; QT_END_NAMESPACE diff --git a/src/gui/statemachine/qguistatemachine.cpp b/src/gui/statemachine/qguistatemachine.cpp index d30265a..b7563d7 100644 --- a/src/gui/statemachine/qguistatemachine.cpp +++ b/src/gui/statemachine/qguistatemachine.cpp @@ -9,13 +9,8 @@ ** ****************************************************************************/ -#ifdef QT_STATEMACHINE_SOLUTION -#include "qstatemachine.h" -#include "qstatemachine_p.h" -#else #include #include -#endif #include #include -- cgit v0.12 From c89557db5b643eb8018de8ca87e5e56140ed6d2d Mon Sep 17 00:00:00 2001 From: Geir Vattekar Date: Wed, 13 May 2009 12:40:27 +0200 Subject: Doc: Work on QStateMachine class description Reviewed-by: Kent Hansen --- src/corelib/statemachine/qstatemachine.cpp | 140 +++++++++++++++++------------ 1 file changed, 85 insertions(+), 55 deletions(-) diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp index 24af8e4..42e965a 100644 --- a/src/corelib/statemachine/qstatemachine.cpp +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -89,68 +89,98 @@ QT_BEGIN_NAMESPACE \since 4.6 \ingroup statemachine - The QStateMachine class provides a hierarchical finite state machine based - on \l{Statecharts: A visual formalism for complex systems}{Statecharts} - concepts and notation. QStateMachine is part of \l{The State Machine - Framework}. - - A state machine manages a set of states (QAbstractState objects) and - transitions (QAbstractTransition objects) between those states; the states - and the transitions collectively define a state graph. Once a state graph - has been defined, the state machine can execute it. QStateMachine's - execution algorithm is based on the \l{State Chart XML: State Machine - Notation for Control Abstraction}{State Chart XML (SCXML)} algorithm. - - The QState class provides a state that you can use to set properties and - invoke methods on QObjects when the state is entered or exited. This is + QStateMachine is based on the concepts and notation of + \l{Statecharts: A visual formalism for complex + systems}{Statecharts}. QStateMachine is part of \l{The State + Machine Framework}. + + A state machine manages a set of states (classes that inherit from + QAbstractState) and transitions (descendants of + QAbstractTransition) between those states; these states and + transitions define a state graph. Once a state graph has been + built, the state machine can execute it. \l{QStateMachine}'s + execution algorithm is based on the \l{State Chart XML: State + Machine Notation for Control Abstraction}{State Chart XML (SCXML)} + algorithm. The framework's \l{The State Machine + Framework}{overview} gives several state graphs and the code to + build them. + + The rootState() is the parent of all top-level states in the + machine; it is used, for instance, when the state graph is + deleted. It is created by the machine. + + Use the addState() function to add a state to the state machine. + All top-level states are added to the root state. States are + removed with the removeState() function. Removing states while the + machine is running is discouraged. + + Before the machine can be started, the \l{initialState}{initial + state} must be set. The initial state is the state that the + machine enters when started. You can then start() the state + machine. The started() signal is emitted when the initial state is + entered. + + The machine is event driven and keeps its own event loop. Events + are posted to the machine through postEvent(). Note that this + means that it executes asynchronously, and that it will not + progress without a running event loop. You will normally not have + to post events to the machine directly as Qt's transitions, e.g., + QEventTransition and its subclasses, handle this. But for custom + transitions triggered by events, postEvent() is useful. + + The state machine processes events and takes transitions until a + top-level final state is entered; the state machine then emits the + finished() signal. You can also stop() the state machine + explicitly. The stopped() signal is emitted in this case. + + The following snippet shows a state machine that will finish when a button + is clicked: + + \code + QPushButton button; + + QStateMachine machine; + QState *s1 = new QState(); + s1->assignProperty(&button, "text", "Click me"); + + QFinalState *s2 = new QFinalState(); + s1->addTransition(&button, SIGNAL(clicked()), s2); + + machine.addState(s1); + machine.addState(s2); + machine.setInitialState(s1); + machine.start(); + \endcode + + This code example uses QState, which inherits QAbstractState. The + QState class provides a state that you can use to set properties + and invoke methods on \l{QObject}s when the state is entered or + exited. It also contains convenience functions for adding + transitions, e.g., \l{QSignalTransition}s as in this example. See + the QState class description for further details. + + If an error is encountered, the machine will enter the + \l{errorState}{error state}, which is a special state created by + the machine. The types of errors possible are described by the + \l{QStateMachine::}{Error} enum. After the error state is entered, + the type of the error can be retrieved with error(). The execution + of the state graph will not stop when the error state is entered. + So it is possible to handle the error, for instance, by connecting + to the \l{QAbstractState::}{entered()} signal of the error state. + It is also possible to set a custom error state with + setErrorState(). + + \omit This stuff will be moved elsewhere +This is typically used in conjunction with \l{Signals and Slots}{signals}; the signals determine the flow of the state graph, whereas the states' property assignments and method invocations are the actions. - Use the addState() function to add a state to the state machine; - alternatively, pass the machine's rootState() to the state constructor. Use - the removeState() function to remove a state from the state machine. - - The following snippet shows a state machine that will finish when a button - is clicked: - - \code - QPushButton button; - - QStateMachine machine; - QState *s1 = new QState(); - s1->assignProperty(&button, "text", "Click me"); - - QFinalState *s2 = new QFinalState(); - s1->addTransition(&button, SIGNAL(clicked()), s2); - - machine.addState(s1); - machine.addState(s2); - machine.setInitialState(s1); - machine.start(); - \endcode - - The setInitialState() function sets the state machine's initial state; this - state is entered when the state machine is started. - - The start() function starts the state machine. The state machine executes - asynchronously, i.e. you need to run an event loop in order for it to make - progress. The started() signal is emitted when the state machine has entered - the initial state. - - The state machine processes events and takes transitions until a top-level - final state is entered; the state machine then emits the finished() signal. - - The stop() function stops the state machine. The stopped() signal is emitted - when the state machine has stopped. - The postEvent() function posts an event to the state machine. This is useful when you are using custom events to trigger transitions. + \endomit - The rootState() function returns the state machine's root state. All - top-level states have the root state as their parent. - - \sa QAbstractState, QAbstractTransition + \sa QAbstractState, QAbstractTransition, QState, {The State Machine Framework} */ /*! -- cgit v0.12 From db5c26f49d86d195306273038a84a48b3ea62719 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Wed, 13 May 2009 13:18:00 +0200 Subject: doc: Add screenshot for Tank Game Example --- doc/src/images/tankgame-example.png | Bin 0 -> 16089 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 doc/src/images/tankgame-example.png diff --git a/doc/src/images/tankgame-example.png b/doc/src/images/tankgame-example.png new file mode 100644 index 0000000..9e17e30 Binary files /dev/null and b/doc/src/images/tankgame-example.png differ -- cgit v0.12 From d1f79472826577406a675de61122cd4fc9413c52 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Wed, 13 May 2009 13:31:34 +0200 Subject: more statemachine docs --- doc/src/images/statemachine-customevents2.png | Bin 0 -> 6713 bytes doc/src/statemachine.qdoc | 45 +++++++++++++++++++++++--- 2 files changed, 40 insertions(+), 5 deletions(-) create mode 100644 doc/src/images/statemachine-customevents2.png diff --git a/doc/src/images/statemachine-customevents2.png b/doc/src/images/statemachine-customevents2.png new file mode 100644 index 0000000..57b37ef Binary files /dev/null and b/doc/src/images/statemachine-customevents2.png differ diff --git a/doc/src/statemachine.qdoc b/doc/src/statemachine.qdoc index 27bd4f8..5a89f4d 100644 --- a/doc/src/statemachine.qdoc +++ b/doc/src/statemachine.qdoc @@ -147,6 +147,9 @@ QObject::connect(s3, SIGNAL(exited()), button, SLOT(showMinimized())); \endcode + Custom states can reimplement QAbstractState::onEntry() and + QAbstractState::onExit(). + \section1 State Machines That Finish The state machine defined in the previous section never finishes. In order @@ -155,6 +158,9 @@ final state, the machine will emit the QStateMachine::finished() signal and halt. + All you need to do to introduce a final state in the graph is create a + QFinalState object and use it as the target of one or more transitions. + \section1 Sharing Transitions By Grouping States Assume we wanted the user to be able to quit the application at any time by @@ -315,16 +321,32 @@ \section1 Detecting that a Composite State has Finished A child state can be final (a QFinalState object); when a final child state - is entered, the parent state emits the QState::finished() signal. + is entered, the parent state emits the QState::finished() signal. The + following diagram shows a composite state \c s1 which does some processing + before entering a final state: \img statemachine-finished.png \omit \caption This is a caption \endomit - This is useful when you want to hide the internal details of a state; - i.e. the only thing the outside world should be able to do is enter the - state, and get a notification when the state has completed its work. + When \c s1 's final state is entered, \c s1 will automatically emit + finished(). We use a signal transition to cause this event to trigger a + state change: + + \code + s1->addTransition(s1, SIGNAL(finished()), s2); + \endcode + + Using final states in composite states is useful when you want to hide the + internal details of a composite state; i.e. the only thing the outside world + should be able to do is enter the state, and get a notification when the + state has completed its work. This is a very powerful abstraction and + encapsulation mechanism when building complex (deeply nested) state + machines. (In the above example, you could of course create a transition + directly from \c s1 's \c done state rather than relying on \c s1 's + finished() signal, but with the consequence that implementation details of + \c s1 are exposed and depended on). For parallel state groups, the QState::finished() signal is emitted when \e all the child states have entered final states. @@ -425,7 +447,20 @@ machine.postEvent(new StringEvent("Hello")); machine.postEvent(new StringEvent("world")); \endcode - + + An event that is not handled by any relevant transition will be silently + consumed by the state machine. It can be useful to group states and provide + a default handling of such events; for example, as illustrated in the + following statechart: + + \img statemachine-customevents2.png + \omit + \caption This is a caption + \endomit + + For deeply nested statecharts, you can add such "fallback" transitions at + the level of granularity that's most appropriate. + \section1 Using Restore Policy To Automatically Restore Properties In some state machines it can be useful to focus the attention on assigning properties in states, -- cgit v0.12 From 3cd4b6e00e3448d880ae2335bd50f70d9a5d0fca Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Wed, 13 May 2009 13:56:44 +0200 Subject: correctly handle multiple event transitions for same (object,event) The event filter was not removed at the right time. We now store the number of active event transitions for a particular (object,event) and only remove the filtering when the count drops to zero. --- src/corelib/statemachine/qstatemachine.cpp | 19 ++++++---- src/corelib/statemachine/qstatemachine_p.h | 2 +- tests/auto/qstatemachine/tst_qstatemachine.cpp | 48 ++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 7 deletions(-) diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp index 2f620c7..046fbb4 100644 --- a/src/corelib/statemachine/qstatemachine.cpp +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -1407,7 +1407,7 @@ void QStateMachinePrivate::registerEventTransition(QEventTransition *transition) QObjectPrivate *od = QObjectPrivate::get(object); if (!od->eventFilters.contains(q)) object->installEventFilter(q); - qobjectEvents[object].insert(transition->eventType()); + ++qobjectEvents[object][transition->eventType()]; QEventTransitionPrivate::get(transition)->registered = true; #ifdef QSTATEMACHINE_DEBUG qDebug() << q << ": added event transition from" << transition->sourceState() @@ -1422,11 +1422,18 @@ void QStateMachinePrivate::unregisterEventTransition(QEventTransition *transitio if (!QEventTransitionPrivate::get(transition)->registered) return; QObject *object = QEventTransitionPrivate::get(transition)->object; - QSet &events = qobjectEvents[object]; - events.remove(transition->eventType()); - if (events.isEmpty()) { - qobjectEvents.remove(object); - object->removeEventFilter(q); + QHash &events = qobjectEvents[object]; + Q_ASSERT(events.value(transition->eventType()) > 0); + if (--events[transition->eventType()] == 0) { + events.remove(transition->eventType()); + int sum = 0; + QHash::const_iterator it; + for (it = events.constBegin(); it != events.constEnd(); ++it) + sum += it.value(); + if (sum == 0) { + qobjectEvents.remove(object); + object->removeEventFilter(q); + } } QEventTransitionPrivate::get(transition)->registered = false; } diff --git a/src/corelib/statemachine/qstatemachine_p.h b/src/corelib/statemachine/qstatemachine_p.h index 9b4b861..dfa5575 100644 --- a/src/corelib/statemachine/qstatemachine_p.h +++ b/src/corelib/statemachine/qstatemachine_p.h @@ -200,7 +200,7 @@ public: QHash > connections; #ifndef QT_NO_STATEMACHINE_EVENTFILTER - QHash > qobjectEvents; + QHash > qobjectEvents; #endif QHash delayedEvents; diff --git a/tests/auto/qstatemachine/tst_qstatemachine.cpp b/tests/auto/qstatemachine/tst_qstatemachine.cpp index 3f94ad9..f8a778a 100644 --- a/tests/auto/qstatemachine/tst_qstatemachine.cpp +++ b/tests/auto/qstatemachine/tst_qstatemachine.cpp @@ -1881,6 +1881,54 @@ void tst_QStateMachine::eventTransitions() QTRY_COMPARE(finishedSpy.count(), 1); } + // Multiple transitions for same (object,event) + { + QStateMachine machine; + QState *s0 = new QState(machine.rootState()); + QState *s1 = new QState(machine.rootState()); + QEventTransition *t0 = new QEventTransition(&button, QEvent::MouseButtonPress); + t0->setTargetState(s1); + s0->addTransition(t0); + QEventTransition *t1 = new QEventTransition(&button, QEvent::MouseButtonPress); + t1->setTargetState(s0); + s1->addTransition(t1); + + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + machine.setInitialState(s0); + machine.start(); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s0)); + + QTest::mousePress(&button, Qt::LeftButton); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s1)); + + s0->removeTransition(t0); + QTest::mousePress(&button, Qt::LeftButton); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s0)); + + QTest::mousePress(&button, Qt::LeftButton); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s0)); + + s1->removeTransition(t1); + QTest::mousePress(&button, Qt::LeftButton); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s0)); + + s0->addTransition(t0); + s1->addTransition(t1); + QTest::mousePress(&button, Qt::LeftButton); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s1)); + } } void tst_QStateMachine::historyStates() -- cgit v0.12 From a82714c6c82f682e02969d9afa551f37f8132653 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Wed, 13 May 2009 13:56:44 +0200 Subject: correctly handle multiple event transitions for same (object,event) The event filter was not removed at the right time. We now store the number of active event transitions for a particular (object,event) and only remove the filtering when the count drops to zero. --- src/corelib/statemachine/qstatemachine.cpp | 19 ++++++---- src/corelib/statemachine/qstatemachine_p.h | 2 +- tests/auto/qstatemachine/tst_qstatemachine.cpp | 48 ++++++++++++++++++++++++++ 3 files changed, 62 insertions(+), 7 deletions(-) diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp index 2f620c7..046fbb4 100644 --- a/src/corelib/statemachine/qstatemachine.cpp +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -1407,7 +1407,7 @@ void QStateMachinePrivate::registerEventTransition(QEventTransition *transition) QObjectPrivate *od = QObjectPrivate::get(object); if (!od->eventFilters.contains(q)) object->installEventFilter(q); - qobjectEvents[object].insert(transition->eventType()); + ++qobjectEvents[object][transition->eventType()]; QEventTransitionPrivate::get(transition)->registered = true; #ifdef QSTATEMACHINE_DEBUG qDebug() << q << ": added event transition from" << transition->sourceState() @@ -1422,11 +1422,18 @@ void QStateMachinePrivate::unregisterEventTransition(QEventTransition *transitio if (!QEventTransitionPrivate::get(transition)->registered) return; QObject *object = QEventTransitionPrivate::get(transition)->object; - QSet &events = qobjectEvents[object]; - events.remove(transition->eventType()); - if (events.isEmpty()) { - qobjectEvents.remove(object); - object->removeEventFilter(q); + QHash &events = qobjectEvents[object]; + Q_ASSERT(events.value(transition->eventType()) > 0); + if (--events[transition->eventType()] == 0) { + events.remove(transition->eventType()); + int sum = 0; + QHash::const_iterator it; + for (it = events.constBegin(); it != events.constEnd(); ++it) + sum += it.value(); + if (sum == 0) { + qobjectEvents.remove(object); + object->removeEventFilter(q); + } } QEventTransitionPrivate::get(transition)->registered = false; } diff --git a/src/corelib/statemachine/qstatemachine_p.h b/src/corelib/statemachine/qstatemachine_p.h index 9b4b861..dfa5575 100644 --- a/src/corelib/statemachine/qstatemachine_p.h +++ b/src/corelib/statemachine/qstatemachine_p.h @@ -200,7 +200,7 @@ public: QHash > connections; #ifndef QT_NO_STATEMACHINE_EVENTFILTER - QHash > qobjectEvents; + QHash > qobjectEvents; #endif QHash delayedEvents; diff --git a/tests/auto/qstatemachine/tst_qstatemachine.cpp b/tests/auto/qstatemachine/tst_qstatemachine.cpp index 3f94ad9..f8a778a 100644 --- a/tests/auto/qstatemachine/tst_qstatemachine.cpp +++ b/tests/auto/qstatemachine/tst_qstatemachine.cpp @@ -1881,6 +1881,54 @@ void tst_QStateMachine::eventTransitions() QTRY_COMPARE(finishedSpy.count(), 1); } + // Multiple transitions for same (object,event) + { + QStateMachine machine; + QState *s0 = new QState(machine.rootState()); + QState *s1 = new QState(machine.rootState()); + QEventTransition *t0 = new QEventTransition(&button, QEvent::MouseButtonPress); + t0->setTargetState(s1); + s0->addTransition(t0); + QEventTransition *t1 = new QEventTransition(&button, QEvent::MouseButtonPress); + t1->setTargetState(s0); + s1->addTransition(t1); + + QSignalSpy finishedSpy(&machine, SIGNAL(finished())); + machine.setInitialState(s0); + machine.start(); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s0)); + + QTest::mousePress(&button, Qt::LeftButton); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s1)); + + s0->removeTransition(t0); + QTest::mousePress(&button, Qt::LeftButton); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s0)); + + QTest::mousePress(&button, Qt::LeftButton); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s0)); + + s1->removeTransition(t1); + QTest::mousePress(&button, Qt::LeftButton); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s0)); + + s0->addTransition(t0); + s1->addTransition(t1); + QTest::mousePress(&button, Qt::LeftButton); + QCoreApplication::processEvents(); + QCOMPARE(machine.configuration().size(), 1); + QVERIFY(machine.configuration().contains(s1)); + } } void tst_QStateMachine::historyStates() -- cgit v0.12 From 66d8ad206cca48d8193785bdf685f5d1fba80f02 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Thu, 14 May 2009 10:33:05 +0200 Subject: Fix SpinState in Tank Game Example to spin more than 180 degrees This was a leftover from when the setDirection() semantics were broken. --- examples/statemachine/tankgameplugins/spin_ai/spin_ai.h | 7 ++++++- .../tankgameplugins/spin_ai_with_error/spin_ai_with_error.h | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/examples/statemachine/tankgameplugins/spin_ai/spin_ai.h b/examples/statemachine/tankgameplugins/spin_ai/spin_ai.h index a331a3e..d8d3d73 100644 --- a/examples/statemachine/tankgameplugins/spin_ai/spin_ai.h +++ b/examples/statemachine/tankgameplugins/spin_ai/spin_ai.h @@ -18,7 +18,7 @@ public: public slots: void spin() { - m_tank->setProperty("direction", 90.0); + m_tank->setProperty("direction", m_tank->property("direction").toDouble() + 90.0); } protected: @@ -28,6 +28,11 @@ protected: spin(); } + void onExit(QEvent *) + { + disconnect(m_tank, SIGNAL(actionCompleted()), this, SLOT(spin())); + } + private: QObject *m_tank; diff --git a/examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.h b/examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.h index 5cfb364..456ba01 100644 --- a/examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.h +++ b/examples/statemachine/tankgameplugins/spin_ai_with_error/spin_ai_with_error.h @@ -18,7 +18,7 @@ public: public slots: void spin() { - m_tank->setProperty("direction", 90.0); + m_tank->setProperty("direction", m_tank->property("direction").toDouble() + 90.0); } protected: @@ -28,6 +28,11 @@ protected: spin(); } + void onExit(QEvent *) + { + disconnect(m_tank, SIGNAL(actionCompleted()), this, SLOT(spin())); + } + private: QObject *m_tank; -- cgit v0.12 From fa99a2ab12f646d73498b69fae0020da1abf9685 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Thu, 14 May 2009 10:59:00 +0200 Subject: Fix run-time error in Seek AI's state machine Passed parent as 'tank', thus getting a null parent in the GoToLocation state, which breaks the machine when you transition into the state. --- examples/statemachine/tankgameplugins/seek_ai/seek_ai.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/examples/statemachine/tankgameplugins/seek_ai/seek_ai.h b/examples/statemachine/tankgameplugins/seek_ai/seek_ai.h index 2835988..597f8595 100644 --- a/examples/statemachine/tankgameplugins/seek_ai/seek_ai.h +++ b/examples/statemachine/tankgameplugins/seek_ai/seek_ai.h @@ -137,16 +137,20 @@ public: ChaseState(QObject *tank, QState *parentState = 0) : QState(parentState), m_tank(tank) { QState *fireCannon = new QState(this); + fireCannon->setObjectName("fireCannon"); connect(fireCannon, SIGNAL(entered()), tank, SLOT(fireCannon())); setInitialState(fireCannon); - m_goToLocation = new GoToLocationState(this); + m_goToLocation = new GoToLocationState(tank, this); + m_goToLocation->setObjectName("goToLocation"); fireCannon->addTransition(tank, SIGNAL(actionCompleted()), m_goToLocation); m_turnToDirection = new QState(this); + m_turnToDirection->setObjectName("turnToDirection"); m_goToLocation->addTransition(tank, SIGNAL(actionCompleted()), m_turnToDirection); QFinalState *finalState = new QFinalState(this); + finalState->setObjectName("finalState"); m_turnToDirection->addTransition(tank, SIGNAL(actionCompleted()), finalState); } -- cgit v0.12 From ededfad305258f9f450b314d9e2d53a6905bc6d0 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Thu, 14 May 2009 13:12:12 +0200 Subject: Pop up message when a tank wins the game Also fixed: Added some docs and moved the tanks a little so they don't start partly outside the scene rect. --- doc/src/examples/tankgame.qdoc | 2 + .../statemachine/tankgame/gameovertransition.cpp | 39 ++++++++++++++++ .../statemachine/tankgame/gameovertransition.h | 22 +++++++++ examples/statemachine/tankgame/mainwindow.cpp | 53 ++++++++++++++++++---- examples/statemachine/tankgame/mainwindow.h | 3 ++ examples/statemachine/tankgame/tankgame.pro | 4 +- examples/statemachine/tankgame/tankitem.cpp | 1 + examples/statemachine/tankgame/tankitem.h | 1 + 8 files changed, 113 insertions(+), 12 deletions(-) create mode 100644 examples/statemachine/tankgame/gameovertransition.cpp create mode 100644 examples/statemachine/tankgame/gameovertransition.h diff --git a/doc/src/examples/tankgame.qdoc b/doc/src/examples/tankgame.qdoc index 1501a99..ab3e0f4 100644 --- a/doc/src/examples/tankgame.qdoc +++ b/doc/src/examples/tankgame.qdoc @@ -82,6 +82,8 @@ "Add tank" menu item should be disabled. This is implemented by giving the "stopped" state two children which define whether the map is full or not. + \snippet examples/statemachine/tankgame/mainwindow.cpp 5 + To make sure that we go into the correct child state when returning from the "running" state (if the "Stop game" menu item is selected while the game is running) we also give the "stopped" state a history state which we make the initial state of "stopped" state. diff --git a/examples/statemachine/tankgame/gameovertransition.cpp b/examples/statemachine/tankgame/gameovertransition.cpp new file mode 100644 index 0000000..21c3510 --- /dev/null +++ b/examples/statemachine/tankgame/gameovertransition.cpp @@ -0,0 +1,39 @@ +#include "gameovertransition.h" +#include "tankitem.h" + +#include +#include + +GameOverTransition::GameOverTransition(QAbstractState *targetState) + : QSignalTransition(new QSignalMapper(), SIGNAL(mapped(QObject*))) +{ + setTargetState(targetState); + + QSignalMapper *mapper = qobject_cast(senderObject()); + mapper->setParent(this); +} + +void GameOverTransition::addTankItem(TankItem *tankItem) +{ + m_tankItems.append(tankItem); + + QSignalMapper *mapper = qobject_cast(senderObject()); + mapper->setMapping(tankItem, tankItem); + connect(tankItem, SIGNAL(aboutToBeDestroyed()), mapper, SLOT(map())); +} + +bool GameOverTransition::eventTest(QEvent *e) const +{ + bool ret = QSignalTransition::eventTest(e); + + if (ret) { + QSignalEvent *signalEvent = static_cast(e); + QObject *sender = qvariant_cast(signalEvent->arguments().at(0)); + TankItem *tankItem = qobject_cast(sender); + m_tankItems.removeAll(tankItem); + + return m_tankItems.size() <= 1; + } else { + return false; + } +} \ No newline at end of file diff --git a/examples/statemachine/tankgame/gameovertransition.h b/examples/statemachine/tankgame/gameovertransition.h new file mode 100644 index 0000000..53c9b86 --- /dev/null +++ b/examples/statemachine/tankgame/gameovertransition.h @@ -0,0 +1,22 @@ +#ifndef GAMEOVERTRANSITION_H +#define GAMEOVERTRANSITION_H + +#include + +class TankItem; +class GameOverTransition: public QSignalTransition +{ + Q_OBJECT +public: + GameOverTransition(QAbstractState *targetState); + + void addTankItem(TankItem *tankItem); + +protected: + bool eventTest(QEvent *event) const; + +private: + mutable QList m_tankItems; +}; + +#endif diff --git a/examples/statemachine/tankgame/mainwindow.cpp b/examples/statemachine/tankgame/mainwindow.cpp index 870bc9c..fcc0325 100644 --- a/examples/statemachine/tankgame/mainwindow.cpp +++ b/examples/statemachine/tankgame/mainwindow.cpp @@ -2,6 +2,7 @@ #include "tankitem.h" #include "rocketitem.h" #include "plugin.h" +#include "gameovertransition.h" #include #include @@ -53,7 +54,7 @@ void MainWindow::init() { TankItem *item = new TankItem(this); - item->setPos(m_scene->sceneRect().topLeft() + QPointF(15.0, 15.0)); + item->setPos(m_scene->sceneRect().topLeft() + QPointF(30.0, 30.0)); item->setDirection(45.0); item->setColor(Qt::red); @@ -63,7 +64,7 @@ void MainWindow::init() { TankItem *item = new TankItem(this); - item->setPos(m_scene->sceneRect().topRight() + QPointF(-15.0, 15.0)); + item->setPos(m_scene->sceneRect().topRight() + QPointF(-30.0, 30.0)); item->setDirection(135.0); item->setColor(Qt::green); @@ -73,7 +74,7 @@ void MainWindow::init() { TankItem *item = new TankItem(this); - item->setPos(m_scene->sceneRect().bottomRight() + QPointF(-15.0, -15.0)); + item->setPos(m_scene->sceneRect().bottomRight() + QPointF(-30.0, -30.0)); item->setDirection(225.0); item->setColor(Qt::blue); @@ -83,7 +84,7 @@ void MainWindow::init() { TankItem *item = new TankItem(this); - item->setPos(m_scene->sceneRect().bottomLeft() + QPointF(15.0, -15.0)); + item->setPos(m_scene->sceneRect().bottomLeft() + QPointF(30.0, -30.0)); item->setDirection(315.0); item->setColor(Qt::yellow); @@ -125,20 +126,23 @@ void MainWindow::init() stoppedState->assignProperty(this, "started", false); m_machine->setInitialState(stoppedState); - QState *spawnsAvailable = new QState(stoppedState); - spawnsAvailable->setObjectName("spawnsAvailable"); +//! [5] + QState *spawnsAvailable = new QState(stoppedState); spawnsAvailable->assignProperty(addTankAction, "enabled", true); - QState *noSpawnsAvailable = new QState(stoppedState); - noSpawnsAvailable->setObjectName("noSpawnsAvailable"); + QState *noSpawnsAvailable = new QState(stoppedState); noSpawnsAvailable->assignProperty(addTankAction, "enabled", false); +//! [5] + spawnsAvailable->setObjectName("spawnsAvailable"); + noSpawnsAvailable->setObjectName("noSpawnsAvailable"); spawnsAvailable->addTransition(this, SIGNAL(mapFull()), noSpawnsAvailable); //! [3] - QHistoryState *hs = new QHistoryState(stoppedState); + QHistoryState *hs = new QHistoryState(stoppedState); hs->setDefaultState(spawnsAvailable); //! [3] + hs->setObjectName("hs"); stoppedState->setInitialState(hs); @@ -149,10 +153,18 @@ void MainWindow::init() m_runningState->assignProperty(addTankAction, "enabled", false); m_runningState->assignProperty(runGameAction, "enabled", false); m_runningState->assignProperty(stopGameAction, "enabled", true); + + QState *gameOverState = new QState(m_machine->rootState()); + gameOverState->setObjectName("gameOverState"); + gameOverState->assignProperty(stopGameAction, "enabled", false); + connect(gameOverState, SIGNAL(entered()), this, SLOT(gameOver())); stoppedState->addTransition(runGameAction, SIGNAL(triggered()), m_runningState); m_runningState->addTransition(stopGameAction, SIGNAL(triggered()), stoppedState); + m_gameOverTransition = new GameOverTransition(gameOverState); + m_runningState->addTransition(m_gameOverTransition); + QTimer *timer = new QTimer(this); timer->setInterval(100); connect(timer, SIGNAL(timeout()), this, SLOT(runStep())); @@ -182,6 +194,22 @@ void MainWindow::runStep() } } +void MainWindow::gameOver() +{ + QList items = m_scene->items(); + + TankItem *lastTankStanding = 0; + foreach (QGraphicsItem *item, items) { + if (GameItem *gameItem = qgraphicsitem_cast(item)) { + if (lastTankStanding = qobject_cast(gameItem)) + break; + } + } + + QMessageBox::information(this, "Game over!", + QString::fromLatin1("The tank played by '%1' has won!").arg(lastTankStanding->objectName())); +} + void MainWindow::addRocket() { TankItem *tankItem = qobject_cast(sender()); @@ -244,21 +272,26 @@ void MainWindow::addTank() int idx = itemNames.indexOf(selectedName); if (Plugin *plugin = idx >= 0 ? items.at(idx) : 0) { TankItem *tankItem = m_spawns.takeLast(); + tankItem->setObjectName(selectedName); + tankItem->setToolTip(selectedName); m_scene->addItem(tankItem); connect(tankItem, SIGNAL(cannonFired()), this, SLOT(addRocket())); if (m_spawns.isEmpty()) emit mapFull(); + + m_gameOverTransition->addTankItem(tankItem); QState *region = new QState(m_runningState); + region->setObjectName(QString::fromLatin1("region%1").arg(m_spawns.size())); //! [2] QState *pluginState = plugin->create(region, tankItem); //! [2] region->setInitialState(pluginState); - // If the plugin has an error it is disabled //! [4] QState *errorState = new QState(region); + errorState->setObjectName(QString::fromLatin1("errorState%1").arg(m_spawns.size())); errorState->assignProperty(tankItem, "enabled", false); pluginState->setErrorState(errorState); //! [4] diff --git a/examples/statemachine/tankgame/mainwindow.h b/examples/statemachine/tankgame/mainwindow.h index 622dabe..40e1595 100644 --- a/examples/statemachine/tankgame/mainwindow.h +++ b/examples/statemachine/tankgame/mainwindow.h @@ -7,6 +7,7 @@ class QGraphicsScene; class QStateMachine; class QState; +class GameOverTransition; class TankItem; class MainWindow: public QMainWindow { @@ -23,6 +24,7 @@ public slots: void addTank(); void addRocket(); void runStep(); + void gameOver(); signals: void mapFull(); @@ -35,6 +37,7 @@ private: QStateMachine *m_machine; QState *m_runningState; + GameOverTransition *m_gameOverTransition; QList m_spawns; QTime m_time; diff --git a/examples/statemachine/tankgame/tankgame.pro b/examples/statemachine/tankgame/tankgame.pro index f7b0760..46cfe2e 100644 --- a/examples/statemachine/tankgame/tankgame.pro +++ b/examples/statemachine/tankgame/tankgame.pro @@ -8,6 +8,6 @@ DEPENDPATH += . INCLUDEPATH += C:/dev/kinetic/examples/statemachine/tankgame/. . # Input -HEADERS += mainwindow.h plugin.h tankitem.h rocketitem.h gameitem.h -SOURCES += main.cpp mainwindow.cpp tankitem.cpp rocketitem.cpp gameitem.cpp +HEADERS += mainwindow.h plugin.h tankitem.h rocketitem.h gameitem.h gameovertransition.h +SOURCES += main.cpp mainwindow.cpp tankitem.cpp rocketitem.cpp gameitem.cpp gameovertransition.cpp CONFIG += console diff --git a/examples/statemachine/tankgame/tankitem.cpp b/examples/statemachine/tankgame/tankitem.cpp index 5506a7e..c322d21 100644 --- a/examples/statemachine/tankgame/tankitem.cpp +++ b/examples/statemachine/tankgame/tankitem.cpp @@ -113,6 +113,7 @@ void TankItem::idle(qreal elapsed) void TankItem::hitByRocket() { + emit aboutToBeDestroyed(); deleteLater(); } diff --git a/examples/statemachine/tankgame/tankitem.h b/examples/statemachine/tankgame/tankitem.h index 66f05aa..9475397 100644 --- a/examples/statemachine/tankgame/tankitem.h +++ b/examples/statemachine/tankgame/tankitem.h @@ -41,6 +41,7 @@ signals: void collision(const QLineF &collidedLine); void actionCompleted(); void cannonFired(); + void aboutToBeDestroyed(); public slots: void moveForwards(qreal length = 10.0); -- cgit v0.12 From 2a4f5a1b4e6e9632ab18c81c3e06ebf2061cd765 Mon Sep 17 00:00:00 2001 From: David Boddie Date: Thu, 14 May 2009 14:17:18 +0200 Subject: Doc: Marked up a link correctly. Reviewed-by: David Boddie --- doc/src/groups.qdoc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/src/groups.qdoc b/doc/src/groups.qdoc index fda54dc..3c4da53 100644 --- a/doc/src/groups.qdoc +++ b/doc/src/groups.qdoc @@ -76,7 +76,7 @@ \brief Classes for animations, states and transitions. These classes provide a framework for creating both simple and complex - animations. The Animation Framework also provides states and animated + animations. \l{The Animation Framework} also provides states and animated transitions, making it easy to create animated stateful forms. */ -- cgit v0.12 From 17d8f734c038705f7f8196dfe5e8902c808a4945 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Thu, 14 May 2009 15:08:03 +0200 Subject: Make QAbstractTransition::eventTest() non-const We decided to remove the const of the eventTest() since some transitions have dynamic conditions and need to update when eventTest() is called. --- examples/animation/moveblocks/main.cpp | 2 +- examples/animation/stickman/lifecycle.cpp | 4 ++-- examples/animation/sub-attaq/boat_p.h | 6 +++--- examples/animation/sub-attaq/states.cpp | 6 +++--- examples/animation/sub-attaq/states.h | 6 +++--- examples/statemachine/factorial/main.cpp | 4 ++-- examples/statemachine/pingpong/main.cpp | 4 ++-- examples/statemachine/tankgame/gameovertransition.cpp | 2 +- examples/statemachine/tankgame/gameovertransition.h | 4 ++-- examples/statemachine/tankgameplugins/seek_ai/seek_ai.h | 6 +++--- src/corelib/statemachine/qabstracttransition.cpp | 4 ++-- src/corelib/statemachine/qabstracttransition.h | 2 +- src/corelib/statemachine/qabstracttransition_p.h | 2 +- src/corelib/statemachine/qeventtransition.cpp | 2 +- src/corelib/statemachine/qeventtransition.h | 2 +- src/corelib/statemachine/qsignaltransition.cpp | 2 +- src/corelib/statemachine/qsignaltransition.h | 2 +- src/corelib/statemachine/qstate.cpp | 2 +- src/corelib/statemachine/qstatemachine.cpp | 2 +- src/gui/statemachine/qbasickeyeventtransition.cpp | 2 +- src/gui/statemachine/qbasickeyeventtransition_p.h | 2 +- src/gui/statemachine/qbasicmouseeventtransition.cpp | 2 +- src/gui/statemachine/qbasicmouseeventtransition_p.h | 2 +- src/gui/statemachine/qkeyeventtransition.cpp | 2 +- src/gui/statemachine/qkeyeventtransition.h | 2 +- src/gui/statemachine/qmouseeventtransition.cpp | 2 +- src/gui/statemachine/qmouseeventtransition.h | 2 +- tests/auto/qstate/tst_qstate.cpp | 2 +- tests/auto/qstatemachine/tst_qstatemachine.cpp | 8 ++++---- 29 files changed, 45 insertions(+), 45 deletions(-) diff --git a/examples/animation/moveblocks/main.cpp b/examples/animation/moveblocks/main.cpp index 06ed3dd..0ce07fc 100644 --- a/examples/animation/moveblocks/main.cpp +++ b/examples/animation/moveblocks/main.cpp @@ -95,7 +95,7 @@ public: } protected: - virtual bool eventTest(QEvent *event) const + virtual bool eventTest(QEvent *event) { return (event->type() == QEvent::Type(StateSwitchEvent::StateSwitchType)) && (static_cast(event)->rand() == m_rand); diff --git a/examples/animation/stickman/lifecycle.cpp b/examples/animation/stickman/lifecycle.cpp index b22af55..423d7ad 100644 --- a/examples/animation/stickman/lifecycle.cpp +++ b/examples/animation/stickman/lifecycle.cpp @@ -69,7 +69,7 @@ public: { } - virtual bool eventTest(QEvent *e) const + virtual bool eventTest(QEvent *e) { if (QSignalTransition::eventTest(e)) { QVariant key = static_cast(e)->arguments().at(0); @@ -92,7 +92,7 @@ public: startTimer(1000); } - virtual bool eventTest(QEvent *e) const + virtual bool eventTest(QEvent *e) { return QEventTransition::eventTest(e) && ((qrand() % 50) == 0); } diff --git a/examples/animation/sub-attaq/boat_p.h b/examples/animation/sub-attaq/boat_p.h index 17fbe5c..6f03e48 100644 --- a/examples/animation/sub-attaq/boat_p.h +++ b/examples/animation/sub-attaq/boat_p.h @@ -67,7 +67,7 @@ public: this->key = key; } protected: - virtual bool eventTest(QEvent *event) const + virtual bool eventTest(QEvent *event) { Q_UNUSED(event); if (!QKeyEventTransition::eventTest(event)) @@ -93,7 +93,7 @@ public: this->key = key; } protected: - virtual bool eventTest(QEvent *event) const + virtual bool eventTest(QEvent *event) { Q_UNUSED(event); if (!QKeyEventTransition::eventTest(event)) @@ -131,7 +131,7 @@ public: this->key = key; } protected: - virtual bool eventTest(QEvent *event) const + virtual bool eventTest(QEvent *event) { Q_UNUSED(event); if (!QKeyEventTransition::eventTest(event)) diff --git a/examples/animation/sub-attaq/states.cpp b/examples/animation/sub-attaq/states.cpp index c6af924..7650b0f 100644 --- a/examples/animation/sub-attaq/states.cpp +++ b/examples/animation/sub-attaq/states.cpp @@ -281,7 +281,7 @@ UpdateScoreTransition::UpdateScoreTransition(GraphicsScene *scene, PlayState *ga { } -bool UpdateScoreTransition::eventTest(QEvent *event) const +bool UpdateScoreTransition::eventTest(QEvent *event) { if (!QSignalTransition::eventTest(event)) return false; @@ -300,7 +300,7 @@ WinTransition::WinTransition(GraphicsScene *scene, PlayState *game, QAbstractSta { } -bool WinTransition::eventTest(QEvent *event) const +bool WinTransition::eventTest(QEvent *event) { if (!QSignalTransition::eventTest(event)) return false; @@ -319,7 +319,7 @@ CustomSpaceTransition::CustomSpaceTransition(QWidget *widget, PlayState *game, Q { } -bool CustomSpaceTransition::eventTest(QEvent *event) const +bool CustomSpaceTransition::eventTest(QEvent *event) { Q_UNUSED(event); if (!QKeyEventTransition::eventTest(event)) diff --git a/examples/animation/sub-attaq/states.h b/examples/animation/sub-attaq/states.h index 27beb71..a1cb5ff 100644 --- a/examples/animation/sub-attaq/states.h +++ b/examples/animation/sub-attaq/states.h @@ -152,7 +152,7 @@ class UpdateScoreTransition : public QSignalTransition public: UpdateScoreTransition(GraphicsScene *scene, PlayState *game, QAbstractState *target); protected: - virtual bool eventTest(QEvent *event) const; + virtual bool eventTest(QEvent *event); private: PlayState * game; GraphicsScene *scene; @@ -164,7 +164,7 @@ class WinTransition : public QSignalTransition public: WinTransition(GraphicsScene *scene, PlayState *game, QAbstractState *target); protected: - virtual bool eventTest(QEvent *event) const; + virtual bool eventTest(QEvent *event); private: PlayState * game; GraphicsScene *scene; @@ -176,7 +176,7 @@ private: public: CustomSpaceTransition(QWidget *widget, PlayState *game, QEvent::Type type, int key); protected: - virtual bool eventTest(QEvent *event) const; + virtual bool eventTest(QEvent *event); private: PlayState *game; int key; diff --git a/examples/statemachine/factorial/main.cpp b/examples/statemachine/factorial/main.cpp index ae5928f..1065eb8 100644 --- a/examples/statemachine/factorial/main.cpp +++ b/examples/statemachine/factorial/main.cpp @@ -100,7 +100,7 @@ public: : QSignalTransition(fact, SIGNAL(xChanged(int))), m_fact(fact) {} - virtual bool eventTest(QEvent *e) const + virtual bool eventTest(QEvent *e) { if (!QSignalTransition::eventTest(e)) return false; @@ -130,7 +130,7 @@ public: : QSignalTransition(fact, SIGNAL(xChanged(int))), m_fact(fact) {} - virtual bool eventTest(QEvent *e) const + virtual bool eventTest(QEvent *e) { if (!QSignalTransition::eventTest(e)) return false; diff --git a/examples/statemachine/pingpong/main.cpp b/examples/statemachine/pingpong/main.cpp index db66bfd..331627e 100644 --- a/examples/statemachine/pingpong/main.cpp +++ b/examples/statemachine/pingpong/main.cpp @@ -86,7 +86,7 @@ public: PongTransition() {} protected: - virtual bool eventTest(QEvent *e) const { + virtual bool eventTest(QEvent *e) { return (e->type() == QEvent::User+3); } virtual void onTransition(QEvent *) @@ -104,7 +104,7 @@ public: PingTransition() {} protected: - virtual bool eventTest(QEvent *e) const { + virtual bool eventTest(QEvent *e) { return (e->type() == QEvent::User+2); } virtual void onTransition(QEvent *) diff --git a/examples/statemachine/tankgame/gameovertransition.cpp b/examples/statemachine/tankgame/gameovertransition.cpp index 21c3510..cec786e 100644 --- a/examples/statemachine/tankgame/gameovertransition.cpp +++ b/examples/statemachine/tankgame/gameovertransition.cpp @@ -22,7 +22,7 @@ void GameOverTransition::addTankItem(TankItem *tankItem) connect(tankItem, SIGNAL(aboutToBeDestroyed()), mapper, SLOT(map())); } -bool GameOverTransition::eventTest(QEvent *e) const +bool GameOverTransition::eventTest(QEvent *e) { bool ret = QSignalTransition::eventTest(e); diff --git a/examples/statemachine/tankgame/gameovertransition.h b/examples/statemachine/tankgame/gameovertransition.h index 53c9b86..9a86b83 100644 --- a/examples/statemachine/tankgame/gameovertransition.h +++ b/examples/statemachine/tankgame/gameovertransition.h @@ -13,10 +13,10 @@ public: void addTankItem(TankItem *tankItem); protected: - bool eventTest(QEvent *event) const; + bool eventTest(QEvent *event); private: - mutable QList m_tankItems; + QList m_tankItems; }; #endif diff --git a/examples/statemachine/tankgameplugins/seek_ai/seek_ai.h b/examples/statemachine/tankgameplugins/seek_ai/seek_ai.h index 597f8595..9d4aabc 100644 --- a/examples/statemachine/tankgameplugins/seek_ai/seek_ai.h +++ b/examples/statemachine/tankgameplugins/seek_ai/seek_ai.h @@ -81,7 +81,7 @@ public: } protected: - bool eventTest(QEvent *event) const + bool eventTest(QEvent *event) { bool b = QSignalTransition::eventTest(event); if (b) { @@ -105,7 +105,7 @@ protected: } private: - mutable QLineF m_lastLine; + QLineF m_lastLine; QObject *m_tank; QState *m_turnTo; }; @@ -180,7 +180,7 @@ public: } protected: - bool eventTest(QEvent *event) const + bool eventTest(QEvent *event) { bool b = QSignalTransition::eventTest(event); if (b) { diff --git a/src/corelib/statemachine/qabstracttransition.cpp b/src/corelib/statemachine/qabstracttransition.cpp index f7a7d34..a930581 100644 --- a/src/corelib/statemachine/qabstracttransition.cpp +++ b/src/corelib/statemachine/qabstracttransition.cpp @@ -125,9 +125,9 @@ QStateMachine *QAbstractTransitionPrivate::machine() const return 0; } -bool QAbstractTransitionPrivate::callEventTest(QEvent *e) const +bool QAbstractTransitionPrivate::callEventTest(QEvent *e) { - Q_Q(const QAbstractTransition); + Q_Q(QAbstractTransition); return q->eventTest(e); } diff --git a/src/corelib/statemachine/qabstracttransition.h b/src/corelib/statemachine/qabstracttransition.h index 8f0cefa..c63d55a 100644 --- a/src/corelib/statemachine/qabstracttransition.h +++ b/src/corelib/statemachine/qabstracttransition.h @@ -88,7 +88,7 @@ public: #endif protected: - virtual bool eventTest(QEvent *event) const = 0; + virtual bool eventTest(QEvent *event) = 0; virtual void onTransition(QEvent *event) = 0; diff --git a/src/corelib/statemachine/qabstracttransition_p.h b/src/corelib/statemachine/qabstracttransition_p.h index 156e70e..a6db220 100644 --- a/src/corelib/statemachine/qabstracttransition_p.h +++ b/src/corelib/statemachine/qabstracttransition_p.h @@ -75,7 +75,7 @@ public: static QAbstractTransitionPrivate *get(QAbstractTransition *q); static const QAbstractTransitionPrivate *get(const QAbstractTransition *q); - bool callEventTest(QEvent *e) const; + bool callEventTest(QEvent *e); void callOnTransition(QEvent *e); QState *sourceState() const; QStateMachine *machine() const; diff --git a/src/corelib/statemachine/qeventtransition.cpp b/src/corelib/statemachine/qeventtransition.cpp index 3ae3ed9..74eb577 100644 --- a/src/corelib/statemachine/qeventtransition.cpp +++ b/src/corelib/statemachine/qeventtransition.cpp @@ -252,7 +252,7 @@ void QEventTransition::setEventObject(QObject *object) /*! \reimp */ -bool QEventTransition::eventTest(QEvent *event) const +bool QEventTransition::eventTest(QEvent *event) { Q_D(const QEventTransition); if (event->type() == QEvent::Wrapped) { diff --git a/src/corelib/statemachine/qeventtransition.h b/src/corelib/statemachine/qeventtransition.h index 68ee4fc..3530bdd 100644 --- a/src/corelib/statemachine/qeventtransition.h +++ b/src/corelib/statemachine/qeventtransition.h @@ -71,7 +71,7 @@ public: void setEventType(QEvent::Type type); protected: - bool eventTest(QEvent *event) const; + bool eventTest(QEvent *event); void onTransition(QEvent *event); bool event(QEvent *e); diff --git a/src/corelib/statemachine/qsignaltransition.cpp b/src/corelib/statemachine/qsignaltransition.cpp index fd17292..4caa917 100644 --- a/src/corelib/statemachine/qsignaltransition.cpp +++ b/src/corelib/statemachine/qsignaltransition.cpp @@ -225,7 +225,7 @@ void QSignalTransition::setSignal(const QByteArray &signal) true if the event's sender and signal index match this transition, and returns false otherwise. */ -bool QSignalTransition::eventTest(QEvent *event) const +bool QSignalTransition::eventTest(QEvent *event) { Q_D(const QSignalTransition); if (event->type() == QEvent::Signal) { diff --git a/src/corelib/statemachine/qsignaltransition.h b/src/corelib/statemachine/qsignaltransition.h index 7f457b8..b485785 100644 --- a/src/corelib/statemachine/qsignaltransition.h +++ b/src/corelib/statemachine/qsignaltransition.h @@ -72,7 +72,7 @@ public: void setSignal(const QByteArray &signal); protected: - bool eventTest(QEvent *event) const; + bool eventTest(QEvent *event); void onTransition(QEvent *event); bool event(QEvent *e); diff --git a/src/corelib/statemachine/qstate.cpp b/src/corelib/statemachine/qstate.cpp index 8893bfe..e42e463 100644 --- a/src/corelib/statemachine/qstate.cpp +++ b/src/corelib/statemachine/qstate.cpp @@ -363,7 +363,7 @@ public: : QAbstractTransition(QList() << target) {} protected: void onTransition(QEvent *) {} - bool eventTest(QEvent *) const { return true; } + bool eventTest(QEvent *) { return true; } }; } // namespace diff --git a/src/corelib/statemachine/qstatemachine.cpp b/src/corelib/statemachine/qstatemachine.cpp index 046fbb4..65f49ae 100644 --- a/src/corelib/statemachine/qstatemachine.cpp +++ b/src/corelib/statemachine/qstatemachine.cpp @@ -1124,7 +1124,7 @@ public: InitialTransition(QAbstractState *target) : QAbstractTransition(QList() << target) {} protected: - virtual bool eventTest(QEvent *) const { return true; } + virtual bool eventTest(QEvent *) { return true; } virtual void onTransition(QEvent *) {} }; diff --git a/src/gui/statemachine/qbasickeyeventtransition.cpp b/src/gui/statemachine/qbasickeyeventtransition.cpp index 7821feb..7336612 100644 --- a/src/gui/statemachine/qbasickeyeventtransition.cpp +++ b/src/gui/statemachine/qbasickeyeventtransition.cpp @@ -156,7 +156,7 @@ void QBasicKeyEventTransition::setModifiersMask(Qt::KeyboardModifiers modifiersM /*! \reimp */ -bool QBasicKeyEventTransition::eventTest(QEvent *event) const +bool QBasicKeyEventTransition::eventTest(QEvent *event) { Q_D(const QBasicKeyEventTransition); if (event->type() == d->eventType) { diff --git a/src/gui/statemachine/qbasickeyeventtransition_p.h b/src/gui/statemachine/qbasickeyeventtransition_p.h index 0d08da0..7506747 100644 --- a/src/gui/statemachine/qbasickeyeventtransition_p.h +++ b/src/gui/statemachine/qbasickeyeventtransition_p.h @@ -55,7 +55,7 @@ public: void setModifiersMask(Qt::KeyboardModifiers modifiers); protected: - bool eventTest(QEvent *event) const; + bool eventTest(QEvent *event); void onTransition(QEvent *); private: diff --git a/src/gui/statemachine/qbasicmouseeventtransition.cpp b/src/gui/statemachine/qbasicmouseeventtransition.cpp index 0cb727e..c4c2302 100644 --- a/src/gui/statemachine/qbasicmouseeventtransition.cpp +++ b/src/gui/statemachine/qbasicmouseeventtransition.cpp @@ -159,7 +159,7 @@ void QBasicMouseEventTransition::setPath(const QPainterPath &path) /*! \reimp */ -bool QBasicMouseEventTransition::eventTest(QEvent *event) const +bool QBasicMouseEventTransition::eventTest(QEvent *event) { Q_D(const QBasicMouseEventTransition); if (event->type() == d->eventType) { diff --git a/src/gui/statemachine/qbasicmouseeventtransition_p.h b/src/gui/statemachine/qbasicmouseeventtransition_p.h index 20c7f8f..57f83c6 100644 --- a/src/gui/statemachine/qbasicmouseeventtransition_p.h +++ b/src/gui/statemachine/qbasicmouseeventtransition_p.h @@ -58,7 +58,7 @@ public: void setPath(const QPainterPath &path); protected: - bool eventTest(QEvent *event) const; + bool eventTest(QEvent *event); void onTransition(QEvent *); private: diff --git a/src/gui/statemachine/qkeyeventtransition.cpp b/src/gui/statemachine/qkeyeventtransition.cpp index e6ab11b..3cf51a3 100644 --- a/src/gui/statemachine/qkeyeventtransition.cpp +++ b/src/gui/statemachine/qkeyeventtransition.cpp @@ -140,7 +140,7 @@ void QKeyEventTransition::setModifiersMask(Qt::KeyboardModifiers modifiersMask) /*! \reimp */ -bool QKeyEventTransition::eventTest(QEvent *event) const +bool QKeyEventTransition::eventTest(QEvent *event) { Q_D(const QKeyEventTransition); if (!QEventTransition::eventTest(event)) diff --git a/src/gui/statemachine/qkeyeventtransition.h b/src/gui/statemachine/qkeyeventtransition.h index 3f797f1..08595e8 100644 --- a/src/gui/statemachine/qkeyeventtransition.h +++ b/src/gui/statemachine/qkeyeventtransition.h @@ -46,7 +46,7 @@ public: protected: void onTransition(QEvent *event); - bool eventTest(QEvent *event) const; + bool eventTest(QEvent *event); private: Q_DISABLE_COPY(QKeyEventTransition) diff --git a/src/gui/statemachine/qmouseeventtransition.cpp b/src/gui/statemachine/qmouseeventtransition.cpp index 3191a2f..5ffdab0 100644 --- a/src/gui/statemachine/qmouseeventtransition.cpp +++ b/src/gui/statemachine/qmouseeventtransition.cpp @@ -170,7 +170,7 @@ void QMouseEventTransition::setPath(const QPainterPath &path) /*! \reimp */ -bool QMouseEventTransition::eventTest(QEvent *event) const +bool QMouseEventTransition::eventTest(QEvent *event) { Q_D(const QMouseEventTransition); if (!QEventTransition::eventTest(event)) diff --git a/src/gui/statemachine/qmouseeventtransition.h b/src/gui/statemachine/qmouseeventtransition.h index eee971e..e878a58 100644 --- a/src/gui/statemachine/qmouseeventtransition.h +++ b/src/gui/statemachine/qmouseeventtransition.h @@ -52,7 +52,7 @@ public: protected: void onTransition(QEvent *event); - bool eventTest(QEvent *event) const; + bool eventTest(QEvent *event); private: Q_DISABLE_COPY(QMouseEventTransition) diff --git a/tests/auto/qstate/tst_qstate.cpp b/tests/auto/qstate/tst_qstate.cpp index bb7de20..75b1905 100644 --- a/tests/auto/qstate/tst_qstate.cpp +++ b/tests/auto/qstate/tst_qstate.cpp @@ -255,7 +255,7 @@ public: } protected: - bool eventTest(QEvent *e) const + bool eventTest(QEvent *e) { return e->type() == m_type; } diff --git a/tests/auto/qstatemachine/tst_qstatemachine.cpp b/tests/auto/qstatemachine/tst_qstatemachine.cpp index f8a778a..7476831 100644 --- a/tests/auto/qstatemachine/tst_qstatemachine.cpp +++ b/tests/auto/qstatemachine/tst_qstatemachine.cpp @@ -204,7 +204,7 @@ public: : QAbstractTransition(QList() << target) {} QList triggers; protected: - virtual bool eventTest(QEvent *) const { + virtual bool eventTest(QEvent *) { return true; } virtual void onTransition(QEvent *) { @@ -246,7 +246,7 @@ public: EventTransition(QEvent::Type type, QAbstractState *target, QState *parent = 0) : QAbstractTransition(QList() << target, parent), m_type(type) {} protected: - virtual bool eventTest(QEvent *e) const { + virtual bool eventTest(QEvent *e) { return (e->type() == m_type); } virtual void onTransition(QEvent *) {} @@ -1394,7 +1394,7 @@ public: : QAbstractTransition(QList() << target), m_value(value) {} protected: - virtual bool eventTest(QEvent *e) const + virtual bool eventTest(QEvent *e) { if (e->type() != QEvent::Type(QEvent::User+2)) return false; @@ -1596,7 +1596,7 @@ public: return m_args; } protected: - bool eventTest(QEvent *e) const { + bool eventTest(QEvent *e) { if (!QSignalTransition::eventTest(e)) return false; QSignalEvent *se = static_cast(e); -- cgit v0.12 From f3418105d25fe9a9fea5b94715d9ec04ca268ee7 Mon Sep 17 00:00:00 2001 From: Eskil Abrahamsen Blomfeldt Date: Thu, 14 May 2009 15:11:02 +0200 Subject: Fix top level compile in examples/animation Three examples have been moved. --- examples/animation/animation.pro | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/animation/animation.pro b/examples/animation/animation.pro index d5121a1..0a57d3d 100644 --- a/examples/animation/animation.pro +++ b/examples/animation/animation.pro @@ -7,9 +7,6 @@ SUBDIRS += \ example \ moveblocks \ padnavigator-ng \ - photobrowser \ - piemenu \ - selectbutton \ states \ stickman \ sub-attaq -- cgit v0.12 From 92ff29b161709f4503d628e468c0eec1daf5835a Mon Sep 17 00:00:00 2001 From: Leonardo Sobral Cunha Date: Thu, 14 May 2009 17:16:11 +0200 Subject: Indentation and whitespace fixes in animation api --- src/corelib/animation/qparallelanimationgroup.cpp | 2 +- src/corelib/animation/qpauseanimation.cpp | 2 +- src/corelib/animation/qpropertyanimation.cpp | 4 ++-- src/corelib/animation/qpropertyanimation_p.h | 1 - src/corelib/animation/qvariantanimation.cpp | 14 ++++++-------- tests/auto/qpropertyanimation/tst_qpropertyanimation.cpp | 4 ++-- 6 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/corelib/animation/qparallelanimationgroup.cpp b/src/corelib/animation/qparallelanimationgroup.cpp index e4bce6a..48c8b3e 100644 --- a/src/corelib/animation/qparallelanimationgroup.cpp +++ b/src/corelib/animation/qparallelanimationgroup.cpp @@ -62,7 +62,7 @@ group->start(); \endcode - + In this example, \c anim1 and \c anim2 are two \l{QPropertyAnimation}s that have already been set up. diff --git a/src/corelib/animation/qpauseanimation.cpp b/src/corelib/animation/qpauseanimation.cpp index 685fa98..c853745 100644 --- a/src/corelib/animation/qpauseanimation.cpp +++ b/src/corelib/animation/qpauseanimation.cpp @@ -52,7 +52,7 @@ \l{QAbstractAnimation::finished()}{finish} before a specified number of milliseconds have elapsed from when it was started. You specify the duration of the pause in the constructor. It can also - be set directly with setDuration(). + be set directly with setDuration(). It is not necessary to construct a QPauseAnimation yourself. QSequentialAnimationGroup provides the convenience functions diff --git a/src/corelib/animation/qpropertyanimation.cpp b/src/corelib/animation/qpropertyanimation.cpp index 96cfa6b..1755aa7 100644 --- a/src/corelib/animation/qpropertyanimation.cpp +++ b/src/corelib/animation/qpropertyanimation.cpp @@ -48,14 +48,14 @@ QPropertyAnimation interpolates over \l{Qt's Property System}{Qt properties}. As property values are stored in \l{QVariant}s, the class inherits QVariantAnimation, and supports animation of the - same \l{QVariant::Type}{variant types} as its super class. + same \l{QVariant::Type}{variant types} as its super class. A class declaring properties must be a QObject. To make it possible to animate a property, it must provide a setter (so that QPropertyAnimation can set the property's value). Note that this makes it possible to animate many of Qt's widgets. Let's look at an example: - + \code QPropertyAnimation animation(myWidget, "geometry"); animation.setDuration(10000); diff --git a/src/corelib/animation/qpropertyanimation_p.h b/src/corelib/animation/qpropertyanimation_p.h index ed3666d..9d9dd31 100644 --- a/src/corelib/animation/qpropertyanimation_p.h +++ b/src/corelib/animation/qpropertyanimation_p.h @@ -70,7 +70,6 @@ public: { } - QPointer target; //for the QProperty diff --git a/src/corelib/animation/qvariantanimation.cpp b/src/corelib/animation/qvariantanimation.cpp index 73ed0df..58f0e03 100644 --- a/src/corelib/animation/qvariantanimation.cpp +++ b/src/corelib/animation/qvariantanimation.cpp @@ -72,7 +72,7 @@ QT_BEGIN_NAMESPACE animates Qt \l{Qt's Property System}{properties}. See the QPropertyAnimation class description if you wish to animate such properties. - + You can then set start and end values for the property by calling setStartValue() and setEndValue(), and finally call start() to start the animation. QVariantAnimation will interpolate the @@ -111,12 +111,12 @@ QT_BEGIN_NAMESPACE \o \l{QMetaType::}{QSizeF} \o \l{QMetaType::}{QRect} \o \l{QMetaType::}{QRectF} - \endlist + \endlist If you need to interpolate other variant types, including custom types, you have to implement interpolation for these yourself. You do this by reimplementing interpolated(), which returns - interpolation values for the value being interpolated. + interpolation values for the value being interpolated. \omit We need some snippets around here. \endomit @@ -152,7 +152,7 @@ template<> Q_INLINE_TEMPLATE QRectF _q_interpolate(const QRectF &f, const QRectF f.getRect(&x1, &y1, &w1, &h1); qreal x2, y2, w2, h2; t.getRect(&x2, &y2, &w2, &h2); - return QRectF( _q_interpolate(x1, x2, progress), _q_interpolate(y1, y2, progress), + return QRectF(_q_interpolate(x1, x2, progress), _q_interpolate(y1, y2, progress), _q_interpolate(w1, w2, progress), _q_interpolate(h1, h2, progress)); } @@ -227,11 +227,10 @@ void QVariantAnimationPrivate::updateCurrentValue() #endif if (currentValue != ret) { //the value has changed - emit q->valueChanged(currentValue); + emit q->valueChanged(currentValue); } } - QVariant QVariantAnimationPrivate::valueAt(qreal step) const { QVariantAnimation::KeyValues::const_iterator result = @@ -242,7 +241,6 @@ QVariant QVariantAnimationPrivate::valueAt(qreal step) const return QVariant(); } - void QVariantAnimationPrivate::setValueAt(qreal step, const QVariant &value) { if (step < qreal(0.0) || step > qreal(1.0)) { @@ -587,7 +585,7 @@ bool QVariantAnimation::event(QEvent *event) \reimp */ void QVariantAnimation::updateState(QAbstractAnimation::State oldState, - QAbstractAnimation::State newState) + QAbstractAnimation::State newState) { Q_UNUSED(oldState); Q_UNUSED(newState); diff --git a/tests/auto/qpropertyanimation/tst_qpropertyanimation.cpp b/tests/auto/qpropertyanimation/tst_qpropertyanimation.cpp index 2e5fd00..172950f 100644 --- a/tests/auto/qpropertyanimation/tst_qpropertyanimation.cpp +++ b/tests/auto/qpropertyanimation/tst_qpropertyanimation.cpp @@ -794,9 +794,9 @@ void tst_QPropertyAnimation::operationsInStates() * | pause() |start() |resume() |stop() * ----------+------------+-----------+-----------+-------------------+ * Stopped | Stopped |Running |Stopped |Stopped | - * _| qWarning | |qWarning |- | + * _| qWarning |restart |qWarning | | * Paused | Paused |Running |Running |Stopped | - * _| - | | | | + * _| | | | | * Running | Paused |Running |Running |Stopped | * | |restart |qWarning | | * ----------+------------+-----------+-----------+-------------------+ -- cgit v0.12 From 9227f6249de0e60a2a428549848c875c01dbf4d2 Mon Sep 17 00:00:00 2001 From: Leonardo Sobral Cunha Date: Thu, 14 May 2009 17:52:09 +0200 Subject: Avoid interpolating if we have less than 2 key values in QVariantAnimation If we have less than 2 key values, we should neither try to interpolate nor set the current value. Reviewed-by: janarve --- src/corelib/animation/qvariantanimation.cpp | 14 +++++++--- .../qpropertyanimation/tst_qpropertyanimation.cpp | 31 ++++++++++++++++++++++ 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/corelib/animation/qvariantanimation.cpp b/src/corelib/animation/qvariantanimation.cpp index 58f0e03..fbce917 100644 --- a/src/corelib/animation/qvariantanimation.cpp +++ b/src/corelib/animation/qvariantanimation.cpp @@ -186,12 +186,20 @@ void QVariantAnimationPrivate::convertValues(int t) void QVariantAnimationPrivate::updateCurrentValue() { + // can't interpolate if we have only 1 key value + if (keyValues.count() <= 1) + return; + Q_Q(QVariantAnimation); + const qreal progress = easing.valueForProgress(((duration == 0) ? qreal(1) : qreal(currentTime) / qreal(duration))); if (progress < currentInterval.start.first || progress > currentInterval.end.first) { //let's update currentInterval - QVariantAnimation::KeyValues::const_iterator itStart = qLowerBound(keyValues.constBegin(), keyValues.constEnd(), qMakePair(progress, QVariant()), animationValueLessThan); + QVariantAnimation::KeyValues::const_iterator itStart = qLowerBound(keyValues.constBegin(), + keyValues.constEnd(), + qMakePair(progress, QVariant()), + animationValueLessThan); QVariantAnimation::KeyValues::const_iterator itEnd = itStart; // If we are at the end we should continue to use the last keyValues in case of extrapolation (progress > 1.0). @@ -199,10 +207,8 @@ void QVariantAnimationPrivate::updateCurrentValue() if (itStart != keyValues.constEnd()) { //this can't happen because we always prepend the default start value there - if (itStart == keyValues.begin()) { + if (itStart == keyValues.constBegin()) { ++itEnd; - if (itEnd == keyValues.constEnd()) - return; //there is no upper bound } else { --itStart; } diff --git a/tests/auto/qpropertyanimation/tst_qpropertyanimation.cpp b/tests/auto/qpropertyanimation/tst_qpropertyanimation.cpp index 172950f..c753477 100644 --- a/tests/auto/qpropertyanimation/tst_qpropertyanimation.cpp +++ b/tests/auto/qpropertyanimation/tst_qpropertyanimation.cpp @@ -95,6 +95,7 @@ private slots: void zeroDurationStart(); void operationsInStates_data(); void operationsInStates(); + void oneKeyValue(); }; tst_QPropertyAnimation::tst_QPropertyAnimation() @@ -850,5 +851,35 @@ void tst_QPropertyAnimation::operationsInStates() #undef Resume #undef Stop +void tst_QPropertyAnimation::oneKeyValue() +{ + QObject o; + o.setProperty("ole", 42); + QCOMPARE(o.property("ole").toInt(), 42); + + QPropertyAnimation animation(&o, "ole"); + animation.setStartValue(43); + animation.setEndValue(44); + animation.setDuration(100); + + animation.setCurrentTime(0); + + QVERIFY(animation.currentValue().isValid()); + QCOMPARE(animation.currentValue().toInt(), 43); + QCOMPARE(o.property("ole").toInt(), 42); + + // remove the last key value + animation.setKeyValueAt(1.0, QVariant()); + + // we will neither interpolate, nor update the current value + // since there is only one 1 key value defined + animation.setCurrentTime(100); + + // the animation should not have been modified + QVERIFY(animation.currentValue().isValid()); + QCOMPARE(animation.currentValue().toInt(), 43); + QCOMPARE(o.property("ole").toInt(), 42); +} + QTEST_MAIN(tst_QPropertyAnimation) #include "tst_qpropertyanimation.moc" -- cgit v0.12 From 4f07fd724a7cc763d57f4b2e23d407b820bb8880 Mon Sep 17 00:00:00 2001 From: Leonardo Sobral Cunha Date: Fri, 15 May 2009 12:40:00 +0200 Subject: Update current value on QVariantAnimation::setKeyValues The current value was udpated on setKeyValueAt, but not on setKeyValues and this was leading to a semantic inconsistency. Reviewed-by: janarve --- src/corelib/animation/qvariantanimation.cpp | 1 + .../qpropertyanimation/tst_qpropertyanimation.cpp | 34 ++++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/src/corelib/animation/qvariantanimation.cpp b/src/corelib/animation/qvariantanimation.cpp index fbce917..cad9341 100644 --- a/src/corelib/animation/qvariantanimation.cpp +++ b/src/corelib/animation/qvariantanimation.cpp @@ -550,6 +550,7 @@ void QVariantAnimation::setKeyValues(const KeyValues &keyValues) d->keyValues = keyValues; qSort(d->keyValues.begin(), d->keyValues.end(), animationValueLessThan); d->currentInterval.start.first = 2; // this will force the refresh + d->updateCurrentValue(); d->hasStartValue = !d->keyValues.isEmpty() && d->keyValues.at(0).first == 0; } diff --git a/tests/auto/qpropertyanimation/tst_qpropertyanimation.cpp b/tests/auto/qpropertyanimation/tst_qpropertyanimation.cpp index c753477..7e910d4 100644 --- a/tests/auto/qpropertyanimation/tst_qpropertyanimation.cpp +++ b/tests/auto/qpropertyanimation/tst_qpropertyanimation.cpp @@ -42,6 +42,7 @@ #include #include +#include #include //TESTED_CLASS=QPropertyAnimation @@ -96,6 +97,7 @@ private slots: void operationsInStates_data(); void operationsInStates(); void oneKeyValue(); + void updateOnSetKeyValues(); }; tst_QPropertyAnimation::tst_QPropertyAnimation() @@ -881,5 +883,37 @@ void tst_QPropertyAnimation::oneKeyValue() QCOMPARE(o.property("ole").toInt(), 42); } +void tst_QPropertyAnimation::updateOnSetKeyValues() +{ + QObject o; + o.setProperty("ole", 100); + QCOMPARE(o.property("ole").toInt(), 100); + + QPropertyAnimation animation(&o, "ole"); + animation.setStartValue(100); + animation.setEndValue(200); + animation.setDuration(100); + + animation.setCurrentTime(50); + QCOMPARE(animation.currentValue().toInt(), 150); + animation.setKeyValueAt(0.0, 300); + QCOMPARE(animation.currentValue().toInt(), 250); + + o.setProperty("ole", 100); + QPropertyAnimation animation2(&o, "ole"); + QVariantAnimation::KeyValues kValues; + kValues << QVariantAnimation::KeyValue(0.0, 100) << QVariantAnimation::KeyValue(1.0, 200); + animation2.setKeyValues(kValues); + animation2.setDuration(100); + animation2.setCurrentTime(50); + QCOMPARE(animation2.currentValue().toInt(), 150); + + kValues.clear(); + kValues << QVariantAnimation::KeyValue(0.0, 300) << QVariantAnimation::KeyValue(1.0, 200); + animation2.setKeyValues(kValues); + + QCOMPARE(animation2.currentValue().toInt(), animation.currentValue().toInt()); +} + QTEST_MAIN(tst_QPropertyAnimation) #include "tst_qpropertyanimation.moc" -- cgit v0.12 From a90608bb1827493da578cf9ad41415fc8a7f6e65 Mon Sep 17 00:00:00 2001 From: Leonardo Sobral Cunha Date: Fri, 15 May 2009 13:31:57 +0200 Subject: General refactors in QVariantAnimation Reviewed-by: janarve --- src/corelib/animation/qvariantanimation.cpp | 58 ++++++++++++++++------------- src/corelib/animation/qvariantanimation_p.h | 4 +- 2 files changed, 35 insertions(+), 27 deletions(-) diff --git a/src/corelib/animation/qvariantanimation.cpp b/src/corelib/animation/qvariantanimation.cpp index cad9341..9b62356 100644 --- a/src/corelib/animation/qvariantanimation.cpp +++ b/src/corelib/animation/qvariantanimation.cpp @@ -174,27 +174,24 @@ void QVariantAnimationPrivate::convertValues(int t) if (pair.second.userType() != t) pair.second.convert(static_cast(t)); } - currentInterval.start.first = 2; // this will force the refresh - interpolator = 0; // if the type changed we need to update the interpolator + interpolator = 0; // if the type changed we need to update the interpolator } /*! - \fn void QVariantAnimation::updateCurrentValue(const QVariant &value) = 0; - This pure virtual function is called when the animated value is changed. - \a value is the new value. + \internal + The goal of this function is to update the currentInterval member. As a consequence, we also + need to update the currentValue. + Set \a force to true to always recalculate the interval. */ - -void QVariantAnimationPrivate::updateCurrentValue() +void QVariantAnimationPrivate::recalculateCurrentInterval(bool force/*=false*/) { // can't interpolate if we have only 1 key value if (keyValues.count() <= 1) return; - Q_Q(QVariantAnimation); - const qreal progress = easing.valueForProgress(((duration == 0) ? qreal(1) : qreal(currentTime) / qreal(duration))); - if (progress < currentInterval.start.first || progress > currentInterval.end.first) { + if (force || progress < currentInterval.start.first || progress > currentInterval.end.first) { //let's update currentInterval QVariantAnimation::KeyValues::const_iterator itStart = qLowerBound(keyValues.constBegin(), keyValues.constEnd(), @@ -205,22 +202,33 @@ void QVariantAnimationPrivate::updateCurrentValue() // If we are at the end we should continue to use the last keyValues in case of extrapolation (progress > 1.0). // This is because the easing function can return a value slightly outside the range [0, 1] if (itStart != keyValues.constEnd()) { - - //this can't happen because we always prepend the default start value there + // this can't happen because we always prepend the default start value there if (itStart == keyValues.constBegin()) { ++itEnd; } else { --itStart; } - //update all the values of the currentInterval + // update all the values of the currentInterval currentInterval.start = *itStart; currentInterval.end = *itEnd; } } + setCurrentValueForProgress(progress); +} + +void QVariantAnimationPrivate::setCurrentValue() +{ + const qreal progress = easing.valueForProgress(((duration == 0) ? qreal(1) : qreal(currentTime) / qreal(duration))); + setCurrentValueForProgress(progress); +} + +void QVariantAnimationPrivate::setCurrentValueForProgress(const qreal progress) +{ + Q_Q(QVariantAnimation); - const qreal startProgress = currentInterval.start.first, - endProgress = currentInterval.end.first; + const qreal startProgress = currentInterval.start.first; + const qreal endProgress = currentInterval.end.first; const qreal localProgress = (progress - startProgress) / (endProgress - startProgress); QVariant ret = q->interpolated(currentInterval.start.second, @@ -261,15 +269,14 @@ void QVariantAnimationPrivate::setValueAt(qreal step, const QVariant &value) keyValues.insert(result, pair); } else { if (value.isValid()) - result->second = value; //remove the previous value + result->second = value; // replaces the previous value else if (step == 0 && !hasStartValue && defaultStartValue.isValid()) - result->second = defaultStartValue; //we reset to the default start value + result->second = defaultStartValue; // resets to the default start value else - keyValues.erase(result); //replace the previous value + keyValues.erase(result); // removes the previous value } - currentInterval.start.first = 2; // this will force the refresh - updateCurrentValue(); + recalculateCurrentInterval(/*force=*/true); } void QVariantAnimationPrivate::setDefaultStartValue(const QVariant &value) @@ -328,7 +335,7 @@ void QVariantAnimation::setEasingCurve(const QEasingCurve &easing) { Q_D(QVariantAnimation); d->easing = easing; - d->updateCurrentValue(); + d->recalculateCurrentInterval(); } Q_GLOBAL_STATIC(QVector, registeredInterpolators) @@ -439,7 +446,7 @@ void QVariantAnimation::setDuration(int msecs) if (d->duration == msecs) return; d->duration = msecs; - d->updateCurrentValue(); + d->recalculateCurrentInterval(); } /*! @@ -549,9 +556,8 @@ void QVariantAnimation::setKeyValues(const KeyValues &keyValues) Q_D(QVariantAnimation); d->keyValues = keyValues; qSort(d->keyValues.begin(), d->keyValues.end(), animationValueLessThan); - d->currentInterval.start.first = 2; // this will force the refresh - d->updateCurrentValue(); d->hasStartValue = !d->keyValues.isEmpty() && d->keyValues.at(0).first == 0; + d->recalculateCurrentInterval(/*force=*/true); } /*! @@ -576,7 +582,7 @@ QVariant QVariantAnimation::currentValue() const { Q_D(const QVariantAnimation); if (!d->currentValue.isValid()) - const_cast(d)->updateCurrentValue(); + const_cast(d)->recalculateCurrentInterval(); return d->currentValue; } @@ -640,7 +646,7 @@ void QVariantAnimation::updateCurrentTime(int msecs) { Q_D(QVariantAnimation); Q_UNUSED(msecs); - d->updateCurrentValue(); + d->recalculateCurrentInterval(); } QT_END_NAMESPACE diff --git a/src/corelib/animation/qvariantanimation_p.h b/src/corelib/animation/qvariantanimation_p.h index 14a3ef6..9e81649 100644 --- a/src/corelib/animation/qvariantanimation_p.h +++ b/src/corelib/animation/qvariantanimation_p.h @@ -108,7 +108,9 @@ public: quint32 changedSignalMask; - void updateCurrentValue(); + void setCurrentValue(); + void setCurrentValueForProgress(const qreal progress); + void recalculateCurrentInterval(bool force=false); void setValueAt(qreal, const QVariant &); QVariant valueAt(qreal step) const; void convertValues(int t); -- cgit v0.12 From 3e280a737607821feeed391d7881baae33f9dcfe Mon Sep 17 00:00:00 2001 From: Leonardo Sobral Cunha Date: Fri, 15 May 2009 13:35:58 +0200 Subject: Avoid resetting the QVariantAnimation::currentValue when changing state Reviewed-by: janarve --- src/corelib/animation/qvariantanimation.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/corelib/animation/qvariantanimation.cpp b/src/corelib/animation/qvariantanimation.cpp index 9b62356..157a321 100644 --- a/src/corelib/animation/qvariantanimation.cpp +++ b/src/corelib/animation/qvariantanimation.cpp @@ -602,8 +602,6 @@ void QVariantAnimation::updateState(QAbstractAnimation::State oldState, { Q_UNUSED(oldState); Q_UNUSED(newState); - Q_D(QVariantAnimation); - d->currentValue = QVariant(); // this will force the refresh } /*! -- cgit v0.12 From 80fa08d3e7f109e11e67ef73ff7a98312bbca984 Mon Sep 17 00:00:00 2001 From: David Boddie Date: Fri, 15 May 2009 16:03:01 +0200 Subject: Doc: Added a work in progress class hierarchy overview. Reviewed-by: Geir Vattekar --- doc/src/diagrams/animations-architecture.svg | 351 +++++++++++++++++++++++++++ 1 file changed, 351 insertions(+) create mode 100644 doc/src/diagrams/animations-architecture.svg diff --git a/doc/src/diagrams/animations-architecture.svg b/doc/src/diagrams/animations-architecture.svg new file mode 100644 index 0000000..0246510 --- /dev/null +++ b/doc/src/diagrams/animations-architecture.svg @@ -0,0 +1,351 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + QAbstractAnimation + + QVariantAnimation + + QAnimationGroup + + + + QPropertyAnimation + + + QSequentialAnimationGroup + + QParallelAnimationGroup + + + + -- cgit v0.12 From 9aba165d0ca970949bcc5fc590569ea58d992626 Mon Sep 17 00:00:00 2001 From: Geir Vattekar Date: Fri, 15 May 2009 16:30:29 +0200 Subject: Doc: Added architecture image to animation overview. Reviewed-by: David Boddie --- doc/src/animation.qdoc | 5 ++++- doc/src/images/animations-architecture.png | Bin 0 -> 27619 bytes 2 files changed, 4 insertions(+), 1 deletion(-) create mode 100644 doc/src/images/animations-architecture.png diff --git a/doc/src/animation.qdoc b/doc/src/animation.qdoc index 5eb2eda..f3baf71 100644 --- a/doc/src/animation.qdoc +++ b/doc/src/animation.qdoc @@ -64,7 +64,10 @@ We will in this section take a high-level look at the animation framework's architecture and how it is used to animate Qt - properties. + properties. The following diagram shows the most important classes + in the animation framework. + + \image animations-architecture.png The animation framework foundation consists of the base class QAbstractAnimation, and its two subclasses QVariantAnimation and diff --git a/doc/src/images/animations-architecture.png b/doc/src/images/animations-architecture.png new file mode 100644 index 0000000..9b581af Binary files /dev/null and b/doc/src/images/animations-architecture.png differ -- cgit v0.12