Mover bloques
El ejemplo Mover bloques muestra cómo animar elementos en un QGraphicsScene utilizando un QStateMachine con una transición personalizada.

El ejemplo anima los bloques azules que puede ver en la imagen superior. La animación mueve los bloques entre cuatro posiciones preestablecidas.
El ejemplo consta de las siguientes clases:
StateSwitcherhereda QState y puede añadirStateSwitchTransitions a otros estados. Al entrar, hará una transición aleatoria a uno de estos estados.StateSwitchTransitiones una transición personalizada que desencadenaStateSwitchEvents.StateSwitchEventes un QEvent que desencadenaStateSwitchTransitions.QGraphicsRectWidgetes un QGraphicsWidget que simplemente pinta su fondo en un color sólido blue.
Los bloques son instancias de QGraphicsRectWidget y se animan en un QGraphicsScene. Para ello construimos un gráfico de estados en el que insertamos las animaciones. El gráfico se ejecuta en QStateMachine. Todo esto se hace en main(). Veamos primero la función main().
La función main()
Después de que QApplication ha sido inicializado, configuramos el QGraphicsScene con sus QGraphicsRectWidgets.
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);
Después de añadir la escena a un QGraphicsView, es el momento de construir el gráfico de estados. Veamos primero un gráfico de estado de lo que estamos tratando de construir.

Observa que group tiene siete subestados, pero sólo hemos incluido tres de ellos en el diagrama. El código que construye este gráfico será examinado línea por línea, y mostrará cómo funciona el gráfico. En primer lugar, construimos el estado 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));
El temporizador se utiliza para añadir un retardo entre cada vez que se mueven los bloques. El temporizador se inicia cuando se entra en group. Como veremos más adelante, group tiene una transición de vuelta a StateSwitcher cuando el temporizador se agota. group es el estado inicial en la máquina, por lo que se programará una animación cuando se inicie el ejemplo.
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() devuelve un QState que establecerá la geometría de nuestros elementos al entrar. También asigna group como padre de este estado.
Un QPropertyAnimation insertado en una transición utilizará los valores asignados a un QState (con QState::assignProperty()), es decir, la animación interpolará entre los valores actuales de las propiedades y los valores en el estado de destino. Más adelante añadiremos transiciones animadas al gráfico de estados.
QParallelAnimationGroup animationGroup; auto anim = new QPropertyAnimation(button4, "geometry"); anim->setDuration(1000); anim->setEasingCurve(QEasingCurve::OutElastic); animationGroup.addAnimation(anim);
Movemos los elementos en paralelo. Cada elemento se añade a animationGroup, que es la animación que se inserta en las transiciones.
auto subGroup = new QSequentialAnimationGroup(&animationGroup); subGroup->addPause(100); anim = new QPropertyAnimation(button3, "geometry"); anim->setDuration(1000); anim->setEasingCurve(QEasingCurve::OutElastic); subGroup->addAnimation(anim);
El grupo de animación secuencial, subGroup, nos ayuda a insertar un retardo entre la animación de cada elemento.
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);
Se añade un StateSwitchTransition al conmutador de estados en StateSwitcher::addState(). También añadimos la animación en esta función. Dado que QPropertyAnimation utiliza los valores de los estados, podemos insertar la misma instancia QPropertyAnimation en todos los StateSwitchTransitions.
Como se mencionó anteriormente, añadimos una transición al conmutador de estados que se activa cuando el temporizador se agota.
machine.addState(group); machine.setInitialState(group); machine.start();
Por último, podemos crear la máquina de estados, añadir nuestro estado inicial y comenzar la ejecución del grafo de estados.
La función createGeometryState()
En createGeometryState(), configuramos la geometría para cada elemento gráfico.
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; }
Como se mencionó anteriormente, QAbstractTransition configurará una animación añadida con addAnimation() utilizando los valores de propiedad establecidos con assignProperty().
La clase StateSwitcher
StateSwitcher tiene transiciones de cambio de estado para cada QStates que creamos con createGeometryState(). Su trabajo es hacer la transición a uno de estos estados al azar cuando se entra en él.
Todas las funciones en StateSwitcher están inlined. Vamos a pasar a través de su definición.
class StateSwitcher : public QState { Q_OBJECT public: explicit StateSwitcher(QStateMachine *machine) : QState(machine) { }
StateSwitcher es un estado diseñado para un propósito particular y siempre será un estado de nivel superior. Usamos m_stateCount para llevar la cuenta de cuántos estados estamos manejando, y m_lastIndex para recordar cuál fue el último estado al que hicimos la transición.
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 {}
Seleccionamos el siguiente estado al que vamos a transicionar y enviamos un StateSwitchEvent, que sabemos que activará el StateSwitchTransition al estado seleccionado.
void addState(QState *state, QAbstractAnimation *animation) { auto trans = new StateSwitchTransition(++m_stateCount); trans->setTargetState(state); addTransition(trans); trans->addAnimation(animation); }
Aquí es donde ocurre la magia. Asignamos un número a cada estado añadido. Este número se asigna tanto a StateSwitchTransition como a StateSwitchEvents. Como hemos visto, los eventos de cambio de estado activarán una transición con el mismo número.
La clase StateSwitchTransition
StateSwitchTransition hereda de QAbstractTransition y se dispara en StateSwitchEvents. Contiene sólo funciones en línea, así que echemos un vistazo a su función eventTest(), que es la única función que definimos..
bool eventTest(QEvent *event) override { return (event->type() == QEvent::Type(StateSwitchEvent::StateSwitchType)) && (static_cast<StateSwitchEvent *>(event)->rand() == m_rand); }
eventTest es llamada por QStateMachine cuando comprueba si una transición debe ser activada-un valor de retorno de true significa que lo hará. Nosotros simplemente comprobamos si nuestro número asignado es igual al número del evento (en cuyo caso se dispara).
La clase StateSwitchEvent
StateSwitchEvent hereda QEvent, y contiene un número que ha sido asignado a un estado y a una transición de cambio de estado por StateSwitcher. Ya hemos visto cómo se utiliza para disparar StateSwitchTransitions en 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; };
Sólo tenemos funciones inline en esta clase, así que un vistazo a su definición será suficiente.
La clase QGraphicsRectWidget
QGraphicsRectWidget hereda QGraphicsWidget y simplemente pinta su rect() de azul. Inlineamos paintEvent(), que es la unica funcion que definimos. Aquí está la definición de la clase QGraphicsRectWidget:
class QGraphicsRectWidget : public QGraphicsWidget { public: void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override { painter->fillRect(rect(), Qt::blue); } };
Avanzando
La técnica mostrada en este ejemplo funciona igualmente bien para todos los QPropertyAnimations. Siempre que el valor a animar sea una propiedad Qt, puedes insertar una animación del mismo en un gráfico de estado.
QState::addAnimation() toma un QAbstractAnimation, por lo que se puede insertar cualquier tipo de animación en el gráfico.
© 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.