Programming

Qt QStateMachine: QPropertyAnimation Not Starting on Transition

Fix QPropertyAnimation staying Paused in Qt QStateMachine transitions with addAnimation(). Learn why it fails on QGraphicsItem, how to use QGraphicsObject + Q_PROPERTY, assignProperty, and sync with finished() signals for smooth qt state machine animations.

4 answers 1 view

Qt QStateMachine: Why isn’t QPropertyAnimation triggered on state transition despite using addAnimation()?

I have set up a simple state machine with an idle state and a moving state. A QSignalTransition from idle to moving is triggered by a custom signal (startMovingAnimation). A QPropertyAnimation is attached to this transition via addAnimation(). The state transition occurs correctly (the entered signal on movingState fires and prints “got moving”), but the animation does not start (its state remains Paused, not Running).

Manually starting the animation in the moving state’s entered signal works fine:

cpp
connect(movingState, &QState::entered, [anim]() { anim->start(); });

However, according to the documentation, addAnimation() on the transition should automatically handle this. What am I doing wrong?

Here is the full setup code:

cpp
void CharacterItem::setupStateMachine() {
 m_moveStateMachine = new QStateMachine();

 QState* idleState = new QState(m_moveStateMachine);
 QState* movingState = new QState(m_moveStateMachine);

 QSignalTransition* idleToMovingTransition
 = idleState->addTransition(this, &CharacterItem::startMovingAnimation, movingState);
 QPropertyAnimation* anim = new QPropertyAnimation(this, "pos", m_moveStateMachine);
 anim->setDuration(1000);
 anim->setStartValue(QPoint{0, 0});
 anim->setEndValue(QPoint(100, 250));
 idleToMovingTransition->addAnimation(anim);

 connect(movingState, &QState::entered, [anim]() { qDebug() << "got moving"; });
 connect(anim, &QPropertyAnimation::stateChanged, [](QAbstractAnimation::State state) {
 qDebug() << state;
 });
 QFinalState* final = new QFinalState(m_moveStateMachine);

 m_moveStateMachine->setInitialState(idleState);
 m_moveStateMachine->start();
}

Your QPropertyAnimation remains Paused during QStateMachine transitions because CharacterItem likely inherits from QGraphicsItem (not QGraphicsObject), lacking a Q_PROPERTY declaration for “pos” that Qt requires for automatic animation via addAnimation(). In Qt state machine setups, transitions only auto-start QPropertyAnimation on QObject-derived classes with properly declared, readable/writable properties—otherwise, it defaults to Paused, forcing manual starts like your entered() workaround. Fix this by subclassing QGraphicsObject and adding Q_PROPERTY(QPointF pos READ pos WRITE setPos NOTIFY posChanged), then tweak start values dynamically for smoother qstatemachine behavior.


Contents


Understanding QStateMachine Transitions and QPropertyAnimation Basics

Ever set up a clean QStateMachine with transitions, only to watch your QPropertyAnimation sit there, stubbornly Paused? You’re not alone. In Qt state machine workflows, QSignalTransition like your startMovingAnimation fires perfectly—entered() on movingState proves it—but animations attached via addAnimation() need specific conditions to kick off automatically.

Here’s the deal: QPropertyAnimation relies on Qt’s meta-object system. When you say new QPropertyAnimation(this, "pos", m_moveStateMachine), Qt scans for a Q_PROPERTY named “pos” with READ/WRITE access. If it’s missing or malformed, the animation registers but won’t start during the transition. Instead, it idles in Paused state, waiting for an explicit start()—exactly like your manual connect in entered().

Why design it this way? Efficiency. Qt documentation explains that state machines integrate animations declaratively, interpolating properties seamlessly across states. But skip the property declaration? No dice. Your code looks solid at first glance—duration, start/end values set—but the root issue hides in CharacterItem’s heritage.


Common Reasons QPropertyAnimation Stays Paused in Qt State Machine

So, what trips up most devs with qstatemachine and QPropertyAnimation? Let’s break it down. First culprit: inheritance. If CharacterItem extends QGraphicsItem, you’re golden for scene management, but animations demand QGraphicsObject. Plain QGraphicsItem skips the QObject meta-system, so “pos” isn’t animatable via property binding.

Second, property mismatches. Even on QGraphicsObject, without Q_PROPERTY(QPointF pos READ pos WRITE setPos NOTIFY posChanged), Qt treats “pos” as inaccessible. Your debug on stateChanged confirms Paused—Qt registered it but can’t drive the value changes.

Third, value equality. Set explicit setStartValue(QPoint{0,0}), but if the current pos() matches at transition time, duration passes with zero delta. No motion, looks broken. A QtCentre thread nails this: transitions grab runtime values, so static starts flop.

And don’t forget lifecycle quirks. QStateMachine processes transitions asynchronously, but animations should auto-start in the “assign properties” phase. If not? Paused. Manual start() bypasses this, which is why your workaround flies—but it’s not the Qt way.

Quick test: Dump metaObject()->property("pos").isValid() before starting the machine. False? There’s your smoking gun.


Fixing Property Declaration for Pos Animation in QStateMachine

Ready to fix it? Start with CharacterItem. Subclass QGraphicsObject instead:

cpp
class CharacterItem : public QGraphicsObject {
 Q_OBJECT
 Q_PROPERTY(QPointF pos READ pos WRITE setPos NOTIFY posChanged)

public:
 // ... your existing code
signals:
 void posChanged();
 void startMovingAnimation();
};

QGraphicsObject inherits QGraphicsItem’s scene integration plus full QObject support. The Q_PROPERTY macro registers “pos” for meta-access—READ delegates to pos(), WRITE to setPos(), NOTIFY fires on changes (emit it in setPos() if overriding).

Rebuild, run your setupStateMachine(). Boom—transition hits, animation Runs automatically. No more Paused state. Why? Now Qt’s property system validates “pos” during addAnimation(), queuing it for the transition’s property assignment phase.

But wait—your setStartValue and setEndValue? They’ll work, but dynamically. More on that next. Pro tip: Add USER true to Q_PROPERTY if exposing beyond internals.


Handling Animation Start Values and AssignProperty in Transitions

Static setStartValue feels intuitive, but qstatemachine shines with dynamic values. At transition trigger, current pos() becomes start; end comes from target state assignments. Your code overrides this—risky if pos isn’t exactly {0,0}.

Better: Use QState::assignProperty(). On idleState:

cpp
idleState->assignProperty(this, "pos", QPointF{0, 0});
movingState->assignProperty(this, "pos", QPointF{100, 250});

Ditch anim->setStartValue() and setEndValue(). Now, QPropertyAnimation interpolates between state values automatically. Transition grabs idle’s assigned pos as start, moving’s as end. Perfect for responsive qt state machine flows.

If you must keep explicit values, call anim->setStartValue(this->pos()) right before transition—but that’s brittle. AssignProperty keeps it declarative, as Qt docs intend.

Test it: Emit startMovingAnimation() mid-motion. Animation snaps to current pos, glides to end. Smooth.


Waiting for Animation Finish Before Next QStateMachine Transition

Transitions don’t block by default—next state fires immediately, potentially clashing animations. Your setup stops at movingState, but chain more? Race city.

Sync with signals. Connect animation finished() to a follow-up transition trigger, or target state’s propertiesAssigned(). A Stack Overflow solution shows:

cpp
connect(anim, &QPropertyAnimation::finished, movingState, &QState::propertiesAssigned);

Or for chained states:

cpp
QSignalTransition* movingToFinal = movingState->addTransition(this, &CharacterItem::animationDone);
connect(anim, &QPropertyAnimation::finished, this, &CharacterItem::animationDone);

This pauses the machine effectively until done. No busy loops, pure event-driven. Handles reversals too—add back-transition with reverse anim.

Edge case: Multiple animations on one transition? All run parallel; connect to QSequentialAnimationGroup for ordering.


Best Practices and Examples for Qt Animations in State Machines

Wrap it up with a full example. Assume fixed CharacterItem:

cpp
void CharacterItem::setupStateMachine() {
 m_moveStateMachine = new QStateMachine(this);
 QState* idleState = new QState(m_moveStateMachine);
 QState* movingState = new QState(m_moveStateMachine);

 // Assign properties declaratively
 idleState->assignProperty(this, "pos", QPointF{0, 0});
 movingState->assignProperty(this, "pos", QPointF{100, 250});

 QSignalTransition* transition = idleState->addTransition(this, &CharacterItem::startMovingAnimation, movingState);
 QPropertyAnimation* anim = new QPropertyAnimation(this, "pos", m_moveStateMachine);
 anim->setDuration(1000);
 anim->setEasingCurve(QEasingCurve::OutQuad); // Polish it
 transition->addAnimation(anim);

 connect(movingState, &QState::entered, this, [this]() {
 qDebug() << "got moving, anim running";
 });
 connect(anim, &QPropertyAnimation::stateChanged, [](QAbstractAnimation::State s) {
 qDebug() << "Anim state:" << s;
 });

 m_moveStateMachine->setInitialState(idleState);
 m_moveStateMachine->start();
}

Best practices? Always QGraphicsObject + Q_PROPERTY. Favor assignProperty over hard values. Group animations for complex sequences. Debug with QStateMachine::addDefaultAnimation(). Scale to hierarchies—parent machines orchestrate child ones.

For QML fans, State + PropertyAnimation mirrors this declaratively. But C++ qstatemachine rules for performance-critical games.


Sources

  1. Animation Overview — Qt documentation on integrating QPropertyAnimation with state machine transitions: https://doc.qt.io/qt-5/animation-overview.html
  2. QStateMachine event loop with animation — Stack Overflow answer on waiting for animation completion in transitions: https://stackoverflow.com/questions/42682462/qstatemachine-event-loop-with-animation
  3. Animation not working on transition between states — QtCentre discussion on property values and Q_PROPERTY requirements: https://www.qtcentre.org/threads/26945-Animation-not-working-on-transition-between-states

Conclusion

Switch CharacterItem to QGraphicsObject with proper Q_PROPERTY for “pos”, lean on assignProperty() for values, and hook finished() for sequencing—that’s your bulletproof QStateMachine animation flow. Manual starts are a hack; declarative wins for scalable Qt state machine apps. Test thoroughly, and you’ll see smooth, automatic QPropertyAnimation every transition. Questions on easing or groups? Dive deeper into the docs.

K

QStateMachine transitions with addAnimation() start QPropertyAnimation but do not wait for completion, firing the next Qt state machine state immediately. To synchronize, connect the target state’s propertiesAssigned signal or animation’s finished() to trigger the following transition. This ensures QStateMachine animations like color changes complete before proceeding, as shown in a full example with QPropertyAnimation interpolating between states. Manual starts in entered() bypass this but lose automatic integration.

N

In QStateMachine, QPropertyAnimation uses the current property value as start, so identical start/end values (e.g., zoom=1) result in no Qt animations. Fix by using QState::assignProperty() on source state for initial value, updating getters/setters, or setStartValue(). This differentiates values during transition, automatically starting the animation without manual intervention in Qt state machine.

QPropertyAnimation in QStateMachine transitions remains Paused if the target lacks a declared Q_PROPERTY like “pos” in a QObject-derived class (e.g., plain QGraphicsItem vs. QGraphicsObject). Declare Q_PROPERTY(QPointF pos READ pos WRITE setPos NOTIFY posChanged) to enable meta-object animation. Transitions auto-start valid animations during Qt state machine changes; otherwise, manual start() in entered() is needed, but proper properties integrate seamlessly with addAnimation().

Authors
K
Software Developer
N
Software Developer
Sources
Documentation Portal
Verified by moderation
Moderation
Qt QStateMachine: QPropertyAnimation Not Starting on Transition