Déplacer des blocs
L'exemple Move Blocks montre comment animer les éléments d'un site QGraphicsScene à l'aide d'un site QStateMachine et d'une transition personnalisée.

L'exemple anime les blocs bleus que vous pouvez voir dans l'image ci-dessus. L'animation déplace les blocs entre quatre positions prédéfinies.
L'exemple se compose des classes suivantes :
StateSwitcherhérite de QState et peut ajouterStateSwitchTransitionà d'autres états. Lorsqu'il est saisi, il passe aléatoirement à l'un de ces états.StateSwitchTransitionest une transition personnalisée qui se déclenche surStateSwitchEvents.StateSwitchEventest une QEvent qui déclencheStateSwitchTransitions.QGraphicsRectWidgetest un QGraphicsWidget qui peint simplement son arrière-plan dans une couleur unie blue.
Les blocs sont des instances de QGraphicsRectWidget et sont animés dans QGraphicsScene. Pour ce faire, nous construisons un graphe d'états dans lequel nous insérons des animations. Le graphe est ensuite exécuté dans QStateMachine. Tout ceci est fait dans main(). Examinons d'abord la fonction main().
La fonction main()
Après l'initialisation de QApplication, nous configurons QGraphicsScene avec ses QGraphicsRectWidget.
auto button1 = new QGraphicsRectWidget; auto button2 = new QGraphicsRectWidget; auto button3 = new QGraphicsRectWidget; auto button4 = new QGraphicsRectWidget; button2->setZValue(1); button3->setZValue(2); button4->setZValue(3); QGraphicsScene scene(0, 0, 300, 300); scene.setBackgroundBrush(Qt::black); scene.addItem(button1); scene.addItem(button2); scene.addItem(button3); scene.addItem(button4);
Après avoir ajouté la scène à QGraphicsView, il est temps de construire le graphe d'état. Examinons d'abord un diagramme d'état de ce que nous essayons de construire.

Notez que le site group a sept sous-états, mais que nous n'en avons inclus que trois dans le diagramme. Le code qui construit ce graphe sera examiné ligne par ligne et montrera comment le graphe fonctionne. Tout d'abord, nous construisons l'état group:
QStateMachine machine; auto group = new QState; group->setObjectName("group"); QTimer timer; timer.setInterval(1250); timer.setSingleShot(true); QObject::connect(group, &QState::entered, &timer, QOverload<>::of(&QTimer::start));
La minuterie est utilisée pour ajouter un délai entre chaque déplacement des blocs. Le timer est démarré lorsque group est entré. Comme nous le verrons plus tard, group dispose d'une transition vers StateSwitcher lorsque le minuteur s'arrête. group est l'état initial de la machine, de sorte qu'une animation sera programmée lorsque l'exemple sera lancé.
auto state1 = createGeometryState(button1, QRect(100, 0, 50, 50), button2, QRect(150, 0, 50, 50), button3, QRect(200, 0, 50, 50), button4, QRect(250, 0, 50, 50), group); ... auto state7 = createGeometryState(button1, QRect(0, 0, 50, 50), button2, QRect(250, 0, 50, 50), button3, QRect(0, 250, 50, 50), button4, QRect(250, 250, 50, 50), group); group->setInitialState(state1);
createGeometryState() renvoie un QState qui définira la géométrie de nos objets à l'entrée. Il assigne également group comme parent de cet état.
Un QPropertyAnimation inséré dans une transition utilisera les valeurs assignées à un QState (avec QState::assignProperty()), c'est-à-dire que l'animation interpolera entre les valeurs actuelles des propriétés et les valeurs dans l'état cible. Nous ajouterons plus tard des transitions animées au graphe d'état.
QParallelAnimationGroup animationGroup; auto anim = new QPropertyAnimation(button4, "geometry"); anim->setDuration(1000); anim->setEasingCurve(QEasingCurve::OutElastic); animationGroup.addAnimation(anim);
Nous déplaçons les éléments en parallèle. Chaque élément est ajouté à animationGroup, qui est l'animation insérée dans les transitions.
auto subGroup = new QSequentialAnimationGroup(&animationGroup); subGroup->addPause(100); anim = new QPropertyAnimation(button3, "geometry"); anim->setDuration(1000); anim->setEasingCurve(QEasingCurve::OutElastic); subGroup->addAnimation(anim);
Le groupe d'animation séquentielle, subGroup, nous aide à insérer un délai entre l'animation de chaque élément.
auto stateSwitcher = new StateSwitcher(&machine); stateSwitcher->setObjectName("stateSwitcher"); group->addTransition(&timer, &QTimer::timeout, stateSwitcher); stateSwitcher->addState(state1, &animationGroup); stateSwitcher->addState(state2, &animationGroup); ... stateSwitcher->addState(state7, &animationGroup);
Une StateSwitchTransition est ajoutée à l'aiguilleur d'état dans StateSwitcher::addState(). Nous ajoutons également l'animation dans cette fonction. Étant donné que QPropertyAnimation utilise les valeurs des états, nous pouvons insérer la même instance QPropertyAnimation dans tous les StateSwitchTransition.
Comme indiqué précédemment, nous ajoutons une transition au commutateur d'état qui se déclenche lorsque la minuterie s'arrête.
machine.addState(group); machine.setInitialState(group); machine.start();
Enfin, nous pouvons créer la machine à états, ajouter notre état initial et lancer l'exécution du graphe d'états.
La fonction createGeometryState()
Dans createGeometryState(), nous configurons la géométrie de chaque élément graphique.
static QState *createGeometryState(QObject *w1, const QRect &rect1, QObject *w2, const QRect &rect2, QObject *w3, const QRect &rect3, QObject *w4, const QRect &rect4, QState *parent) { auto result = new QState(parent); result->assignProperty(w1, "geometry", rect1); result->assignProperty(w2, "geometry", rect2); result->assignProperty(w3, "geometry", rect3); result->assignProperty(w4, "geometry", rect4); return result; }
Comme nous l'avons déjà mentionné, QAbstractTransition met en place une animation ajoutée avec addAnimation() en utilisant les valeurs des propriétés définies avec assignProperty().
La classe StateSwitcher
StateSwitcher possède des transitions de commutation d'état pour chaque QStates que nous avons créé avec createGeometryState(). Son rôle est de passer à l'un de ces états de manière aléatoire lorsqu'il est saisi.
Toutes les fonctions de StateSwitcher sont doublées. Nous allons passer en revue sa définition.
class StateSwitcher : public QState { Q_OBJECT public: explicit StateSwitcher(QStateMachine *machine) : QState(machine) { }
StateSwitcher est un état conçu dans un but particulier et sera toujours un état de premier niveau. Nous utilisons m_stateCount pour garder une trace du nombre d'états que nous gérons, et m_lastIndex pour nous rappeler quel était le dernier état vers lequel nous avons effectué une transition.
void onEntry(QEvent *) override { int n; while ((n = QRandomGenerator::global()->bounded(m_stateCount) + 1) == m_lastIndex) { } m_lastIndex = n; machine()->postEvent(new StateSwitchEvent(n)); } void onExit(QEvent *) override {}
Nous sélectionnons le prochain état vers lequel nous allons passer, et nous postons un StateSwitchEvent, dont nous savons qu'il déclenchera le StateSwitchTransition vers l'état sélectionné.
void addState(QState *state, QAbstractAnimation *animation) { auto trans = new StateSwitchTransition(++m_stateCount); trans->setTargetState(state); addTransition(trans); trans->addAnimation(animation); }
C'est là que la magie opère. Nous attribuons un numéro à chaque état ajouté. Ce numéro est attribué à la fois aux StateSwitchTransition et aux StateSwitchEvents. Comme nous l'avons vu, les événements de changement d'état déclenchent une transition portant le même numéro.
La classe StateSwitchTransition
StateSwitchTransition hérite de QAbstractTransition et se déclenche sur StateSwitchEvents. Elle ne contient que des fonctions inline, examinons donc sa fonction eventTest(), qui est la seule fonction que nous ayons définie.
bool eventTest(QEvent *event) override { return (event->type() == QEvent::Type(StateSwitchEvent::StateSwitchType)) && (static_cast<StateSwitchEvent *>(event)->rand() == m_rand); }
eventTest est appelée par QStateMachine lorsqu'elle vérifie si une transition doit être déclenchée - une valeur de retour de true signifie que c'est le cas. Nous vérifions simplement si le numéro qui nous a été attribué est égal au numéro de l'événement (auquel cas nous le déclenchons).
La classe StateSwitchEvent
StateSwitchEvent hérite de QEvent, et contient un numéro qui a été attribué à un état et à une transition de changement d'état par StateSwitcher. Nous avons déjà vu comment elle est utilisée pour déclencher StateSwitchTransitions dans StateSwitcher.
class StateSwitchEvent: public QEvent { public: explicit StateSwitchEvent(int rand) : QEvent(StateSwitchType), m_rand(rand) { } static constexpr QEvent::Type StateSwitchType = QEvent::Type(QEvent::User + 256); int rand() const { return m_rand; } private: int m_rand; };
Nous n'avons que des fonctions intégrées dans cette classe, donc un coup d'œil à sa définition suffira.
La classe QGraphicsRectWidget
QGraphicsRectWidget hérite de QGraphicsWidget et peint simplement son rect() en bleu. Nous avons mis en ligne paintEvent(), qui est la seule fonction que nous définissons. Voici la définition de la classe QGraphicsRectWidget :
class QGraphicsRectWidget : public QGraphicsWidget { public: void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override { painter->fillRect(rect(), Qt::blue); } };
Aller de l'avant
QPropertyAnimationLa technique présentée dans cet exemple fonctionne également pour tous les Qt Graphs. Tant que la valeur à animer est une propriété Qt, vous pouvez insérer une animation de celle-ci dans un graphe d'état.
QState::addAnimation() prend un QAbstractAnimation, de sorte que n'importe quel type d'animation peut être inséré dans le graphe.
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.