Kadir Erdem Demir
Kadir Erdem Demir

Reputation: 3595

QStateMachine event loop with animation

I want to create a endless loop with QStateMachine in which I also need animations.

QColor leastTransparent, mostTransparent = color();
leastTransparent.setAlpha(250);
mostTransparent.setAlpha(150);

QState *s1 = new QState();
s1->assignProperty(this, "color", leastTransparent);

QState *s2 = new QState();
s2->assignProperty(this, "color", mostTransparent);

QSignalTransition *transition =  s1->addTransition( this, SIGNAL(triggerSignal()),s2);
QSignalTransition *transition2 = s2->addTransition(s2, SIGNAL(entered),s1);

QPropertyAnimation* animation = new QPropertyAnimation( this, "color");
animation->setDuration( 5000 );
transition->addAnimation(animation);

QPropertyAnimation* animation2 = new QPropertyAnimation( this, "color");
animation2->setDuration(10000);
transition2->addAnimation(animation2);

m_stateMachineAnimation->addState(s1);
m_stateMachineAnimation->addState(s2);
m_stateMachineAnimation->setInitialState(s1);
m_stateMachineAnimation->setGlobalRestorePolicy(QStateMachine::RestoreProperties);
m_stateMachineAnimation->start();

What I expect here is the color will get more opaque for first 5 seconds after "triggerSignal". And state will be "s2". And than "s2" 's enter signal be triggered and it will get more and more transparent for 10 seconds.

But instead I am having s2 trigger immediately without waiting 5 seconds right after the "triggerSignal" and than immediately s1 is being triggered again without waiting 10 seconds.

Why my duration is not taken into account by QStateMachine. How can I achieve such a animation with QStateMachine

Upvotes: 1

Views: 490

Answers (1)

You seem to expect the animation to create some sort of an in-between state. It does no such thing. The transition merely triggers the animation. You transition immediately from s2 to s1, giving no time for animations to finish. Instead, you need to explicitly trigger the subsequent transition when the final values of the properties are set. The QState::propertiesAssigned signal is most useful for this purpose. Alternatively, you could use the animation's finished() signal.

In the example below, click within the window to start the animation loop:

// https://github.com/KubaO/stackoverflown/tree/master/questions/statemachine-animation-42682462
#include <QtWidgets>

const char kColor[] = "color";
class Widget : public QWidget {
   Q_OBJECT
   Q_PROPERTY(QColor color MEMBER m_color NOTIFY colorChanged)
   QColor m_color{Qt::blue};
   QStateMachine m_machine{this};
   QState s0{&m_machine}, s1{&m_machine}, s2{&m_machine};
   QEventTransition t01{this, QEvent::MouseButtonPress};
   QPropertyAnimation anim_s1{this, kColor}, anim_s2{this, kColor};
   void paintEvent(QPaintEvent *) override {
      QPainter{this}.fillRect(rect(), m_color);
   }
   Q_SIGNAL void colorChanged(const QColor &);
public:
   Widget() {
      connect(this, &Widget::colorChanged, [this]{ update(); });
      s1.assignProperty(this, kColor, QColor{Qt::red});
      s2.assignProperty(this, kColor, QColor{Qt::green});

      t01.setTargetState(&s1);
      s0.addTransition(&t01);                              t01.addAnimation(&anim_s1);
      s1.addTransition(&s1, &QState::propertiesAssigned, &s2)->addAnimation(&anim_s2);
      s2.addTransition(&s2, &QState::propertiesAssigned, &s1)->addAnimation(&anim_s1);

      anim_s1.setDuration(1000);
      anim_s2.setDuration(2000);

      m_machine.setInitialState(&s0);
      m_machine.start();
   }
};

int main(int argc, char ** argv) {
   QApplication app{argc, argv};
   Widget w;
   w.setFixedSize(300, 200);
   w.show();
   return app.exec();
}
#include "main.moc"

As an aside, this demonstrates that the animation interpolates RGB values, causing the color to go dark between red and blue as the values go from (1,0,0) through (.5,.5,0) onto (0,1,0). For human consumption, it'd make more sense to interpolate HSV, so that the value (brightness) stays the same, and only the hue (what we humans really call "color") changes.

Upvotes: 2

Related Questions