Qt State Machine Guía C
El marco de trabajo State Machine proporciona clases para crear y ejecutar gráficos de estado. Esta página ilustra las características clave del framework en C++.
Clases C++ en el marco de trabajo de la máquina de estados
Para ver la lista completa de clases C++ en el marco de trabajo de la máquina de estados, consulte Qt State Machine C++ Classes
Una máquina de estados sencilla
Para demostrar la funcionalidad básica de la API de Máquinas de Estado, veamos un pequeño ejemplo: Una máquina de estados con tres estados, s1, s2 y s3. La máquina de estados está controlada por un único QPushButton; cuando se pulsa el botón, la máquina pasa a otro estado. Inicialmente, la máquina de estados se encuentra en el estado s1. El diagrama de estados de esta máquina es el siguiente:

El siguiente fragmento muestra el código necesario para crear una máquina de estados de este tipo. En primer lugar, creamos la máquina de estados y los estados:
QStateMachine machine; QState *s1 = new QState(); QState *s2 = new QState(); QState *s3 = new QState();
A continuación, creamos las transiciones utilizando la función QState::addTransition():
s1->addTransition(button, &QPushButton::clicked, s2); s2->addTransition(button, &QPushButton::clicked, s3); s3->addTransition(button, &QPushButton::clicked, s1);
A continuación, añadimos los estados a la máquina y establecemos el estado inicial de la máquina:
machine.addState(s1); machine.addState(s2); machine.addState(s3); machine.setInitialState(s1);
Por último, iniciamos la máquina de estados:
machine.start();La máquina de estados se ejecuta de forma asíncrona, es decir, pasa a formar parte del bucle de eventos de tu aplicación.
Hacer un trabajo útil en la entrada y salida del estado
La máquina de estados anterior simplemente transita de un estado a otro, no realiza ninguna operación. La función QState::assignProperty() se puede utilizar para que un estado establezca una propiedad de un QObject cuando se entra en el estado. En el siguiente fragmento, se especifica el valor que debe asignarse a la propiedad text de QLabel para cada estado:
s1->assignProperty(label, "text", "In state s1"); s2->assignProperty(label, "text", "In state s2"); s3->assignProperty(label, "text", "In state s3");
Cuando se introduzca cualquiera de los estados, el texto de la etiqueta cambiará en consecuencia.
La señal QState::entered() se emite cuando se entra en el estado, y la señal QState::exited() se emite cuando se sale del estado. En el siguiente fragmento, la ranura showMaximized() del botón se llamará cuando se entre en el estado s3, y la ranura showMinimized() del botón se llamará cuando se salga de s3:
QObject::connect(s3, &QState::entered, button, &QPushButton:showMaximized); QObject::connect(s3, &QState::exited, button, &QPushButton::showMinimized);
Los estados personalizados pueden reimplementar QAbstractState::onEntry() y QAbstractState::onExit().
Máquinas de Estado que Finalizan
La máquina de estados definida en la sección anterior nunca termina. Para que una máquina de estados pueda terminar, necesita tener un estado final de nivel superior (QFinalState object). Cuando la máquina de estados entra en un estado final de nivel superior, la máquina emitirá la señal QStateMachine::finished() y se detendrá.
Todo lo que necesitas hacer para introducir un estado final en el grafo es crear un objeto QFinalState y utilizarlo como objetivo de una o más transiciones.
Compartir transiciones agrupando estados
Supongamos que queremos que el usuario pueda salir de la aplicación en cualquier momento pulsando el botón Salir. Para conseguirlo, necesitamos crear un estado final y convertirlo en el objetivo de una transición asociada a la señal clicked() del botón Salir. Podríamos añadir una transición desde cada uno de s1, s2 y s3; sin embargo, esto parece redundante, y además habría que acordarse de añadir dicha transición desde cada nuevo estado que se añada en el futuro.
Podemos conseguir el mismo comportamiento (es decir, que al pulsar el botón Salir se salga de la máquina de estados, independientemente del estado en el que se encuentre la máquina de estados) agrupando los estados s1, s2 y s3. Esto se hace creando un nuevo estado de nivel superior y haciendo a los tres estados originales hijos del nuevo estado. El siguiente diagrama muestra la nueva máquina de estados.

Los tres estados originales se han renombrado s11, s12 y s13 para reflejar que ahora son hijos del nuevo estado de nivel superior, s1. Los estados hijos heredan implícitamente las transiciones de su estado padre. Esto significa que ahora basta con añadir una única transición de s1 al estado final s2. Los nuevos estados añadidos a s1 también heredarán automáticamente esta transición.
Todo lo que se necesita para agrupar estados es especificar el padre adecuado cuando se crea el estado. También es necesario especificar cuál de los estados hijos es el inicial (es decir, en qué estado hijo debe entrar la máquina de estados cuando el estado padre es el objetivo de una transición).
QState *s1 = new QState(); QState *s11 = new QState(s1); QState *s12 = new QState(s1); QState *s13 = new QState(s1); s1->setInitialState(s11); machine.addState(s1); QFinalState *s2 = new QFinalState(); s1->addTransition(quitButton, &QPushButton::clicked, s2); machine.addState(s2); machine.setInitialState(s1); QObject::connect(&machine, &QStateMachine::finished, QCoreApplication::instance(), &QCoreApplication::quit);
En este caso queremos que la aplicación salga cuando la máquina de estados termine, por lo que la señal finished() de la máquina está conectada a la ranura quit() de la aplicación.
Un estado hijo puede anular una transición heredada. Por ejemplo, el siguiente código añade una transición que hace que el botón Salir sea ignorado cuando la máquina de estados está en el estado s12.
s12->addTransition(quitButton, &QPushButton::clicked, s12);
Una transición puede tener cualquier estado como destino, es decir, el estado destino no tiene que estar en el mismo nivel en la jerarquía de estados que el estado origen.
Uso de los estados históricos para guardar y restaurar el estado actual
Imaginemos que queremos añadir un mecanismo de "interrupción" al ejemplo discutido en la sección anterior; el usuario debe ser capaz de hacer clic en un botón para que la máquina de estados realice alguna tarea no relacionada, después de lo cual la máquina de estados debe reanudar lo que estaba haciendo antes (es decir, volver al estado anterior, que es uno de s11, s12 y s13 en este caso).
Este comportamiento puede modelarse fácilmente mediante estados históricos. Un estado histórico (objetoQHistoryState ) es un pseudoestado que representa el estado hijo en el que se encontraba el estado padre la última vez que se salió del estado padre.
Un estado histórico se crea como hijo del estado para el que deseamos registrar el estado hijo actual; cuando la máquina de estados detecta la presencia de un estado de este tipo en tiempo de ejecución, registra automáticamente el estado hijo actual (real) cuando se sale del estado padre. Una transición al estado histórico es, de hecho, una transición al estado hijo que la máquina de estados había guardado previamente; la máquina de estados "reenvía" automáticamente la transición al estado hijo real.
El siguiente diagrama muestra la máquina de estados después de añadir el mecanismo de interrupción.

El siguiente código muestra cómo se puede implementar; en este ejemplo simplemente mostramos un cuadro de mensaje cuando se entra en s3, y luego volvemos inmediatamente al estado hijo anterior de s1 a través del estado historia.
QHistoryState *s1h = new QHistoryState(s1); QState *s3 = new QState(); s3->assignProperty(label, "text", "In s3"); QMessageBox *mbox = new QMessageBox(mainWindow); mbox->addButton(QMessageBox::Ok); mbox->setText("Interrupted!"); mbox->setIcon(QMessageBox::Information); QObject::connect(s3, &QState::entered, mbox, &QMessageBox::exec); s3->addTransition(s1h); machine.addState(s3); s1->addTransition(interruptButton, &QPushButton::clicked, s3);
Uso de estados paralelos para evitar una explosión combinatoria de estados
Supongamos que queremos modelar un conjunto de propiedades mutuamente excluyentes de un coche en una única máquina de estados. Digamos que las propiedades que nos interesan son Limpio vs Sucio, y Moverse vs No moverse. Se necesitarían cuatro estados mutuamente excluyentes y ocho transiciones para poder representar y moverse libremente entre todas las combinaciones posibles.

Si añadiéramos una tercera propiedad (por ejemplo, Rojo frente a Azul), el número total de estados se duplicaría, hasta ocho; y si añadiéramos una cuarta propiedad (por ejemplo, Cerrado frente a Convertible), el número total de estados se duplicaría de nuevo, hasta 16.
Utilizando estados paralelos, el número total de estados y transiciones crece linealmente a medida que añadimos más propiedades, en lugar de exponencialmente. Además, los estados pueden añadirse o eliminarse del estado paralelo sin afectar a ninguno de sus estados hermanos.

Para crear un grupo de estados paralelos, pase QState::ParallelStates al constructor QState.
QState *s1 = new QState(QState::ParallelStates); // s11 and s12 will be entered in parallel QState *s11 = new QState(s1); QState *s12 = new QState(s1);
Cuando se entra en un grupo de estados paralelos, se entra simultáneamente en todos sus estados hijos. Las transiciones dentro de los estados hijos individuales funcionan normalmente. Sin embargo, cualquiera de los estados hijos puede tomar una transición que salga del estado padre. Cuando esto ocurre, se sale del estado padre y de todos sus estados hijos.
El paralelismo en el marco de la Máquina de Estado sigue una semántica intercalada. Todas las operaciones paralelas se ejecutarán en un único paso atómico del procesamiento de eventos, por lo que ningún evento puede interrumpir las operaciones paralelas. Sin embargo, los eventos seguirán siendo procesados secuencialmente, ya que la máquina en sí es de un solo hilo. A modo de ejemplo: Consideremos la situación en la que hay dos transiciones que salen del mismo grupo de estados paralelos, y sus condiciones se hacen verdaderas simultáneamente. En este caso, el evento que se procese en último lugar de los dos no tendrá ningún efecto, ya que el primer evento ya habrá provocado la salida de la máquina del estado paralelo.
Detección de la finalización de un estado compuesto
Un estado hijo puede ser final (un objeto QFinalState ); cuando se entra en un estado hijo final, el estado padre emite la señal QState::finished(). El siguiente diagrama muestra un estado compuesto s1 que realiza algunos procesos antes de entrar en un estado final:

Cuando se entra en el estado final de s1, s1 emite automáticamente finished(). Utilizamos una transición de señal para que este evento desencadene un cambio de estado:
s1->addTransition(s1, &QState::finished, s2);
El uso de estados finales en estados compuestos es útil cuando se desea ocultar los detalles internos de un estado compuesto; es decir, lo único que el mundo exterior debe ser capaz de hacer es entrar en el estado, y obtener una notificación cuando el estado ha completado su trabajo. Este es un mecanismo de abstracción y encapsulación muy poderoso cuando se construyen máquinas de estado complejas (profundamente anidadas). (En el ejemplo anterior, por supuesto, se podría crear una transición directamente desde el estado done de s1 en lugar de depender de la señal finished() de s1, pero con la consecuencia de que los detalles de implementación de s1 quedan expuestos y dependen de ellos).
Para grupos de estados paralelos, la señal QState::finished() se emite cuando todos los estados hijos han entrado en estados finales.
Transiciones sin objetivo
No es necesario que una transición tenga un estado objetivo. Una transición sin objetivo puede activarse del mismo modo que cualquier otra transición; la diferencia es que cuando se activa una transición sin objetivo, no provoca ningún cambio de estado. Esto te permite reaccionar a una señal o evento cuando tu máquina se encuentra en un estado determinado, sin tener que salir de ese estado. Ejemplo:
QStateMachine machine; QState *s1 = new QState(&machine); QPushButton button; QSignalTransition *trans = new QSignalTransition(&button, &QPushButton::clicked); s1->addTransition(trans); QMessageBox msgBox; msgBox.setText("The button was clicked; carry on."); QObject::connect(trans, QSignalTransition::triggered, &msgBox, &QMessageBox::exec); machine.setInitialState(s1);
El cuadro de mensaje se mostrará cada vez que se pulse el botón, pero la máquina de estados permanecerá en su estado actual (s1). Sin embargo, si el estado de destino se estableciera explícitamente en s1, se saldría de s1 y se volvería a entrar en él cada vez (por ejemplo, se emitirían las señales QAbstractState::entered() y QAbstractState::exited()).
Eventos, transiciones y guardias
Un QStateMachine ejecuta su propio bucle de eventos. Para las transiciones de señales ( objetosQSignalTransition ), QStateMachine se contabiliza automáticamente un QStateMachine::SignalEvent cuando intercepta la señal correspondiente; del mismo modo, para las transiciones de eventos QObject ( objetosQEventTransition ) se contabiliza un QStateMachine::WrappedEvent.
Puedes enviar tus propios eventos a la máquina de estados utilizando QStateMachine::postEvent().
Cuando envías un evento personalizado a la máquina de estados, normalmente también tienes una o más transiciones personalizadas que pueden ser disparadas desde eventos de ese tipo. Para crear una transición de este tipo, subclase QAbstractTransition y reimplementar eventTest(), donde se comprueba si un evento coincide con el tipo de evento (y opcionalmente otros criterios, por ejemplo, atributos del objeto de evento).
Aquí definimos nuestro propio tipo de evento personalizado, StringEvent, para enviar cadenas a la máquina de estados:
struct StringEvent : public QEvent { StringEvent(const QString &val) : QEvent(QEvent::Type(QEvent::User+1)), value(val) {} QString value; };
A continuación, definimos una transición que sólo se activa cuando la cadena del evento coincide con una cadena determinada (una transición vigilada ):
class StringTransition : public QAbstractTransition { Q_OBJECT public: StringTransition(const QString &value) : m_value(value) {} protected: bool eventTest(QEvent *e) override { if (e->type() != QEvent::Type(QEvent::User+1)) // StringEvent return false; StringEvent *se = static_cast<StringEvent*>(e); return (m_value == se->value); } void onTransition(QEvent *) override {} private: QString m_value; };
En la reimplementación de eventTest(), primero comprobamos si el tipo de evento es el deseado; si es así, convertimos el evento a StringEvent y realizamos la comparación de cadenas.
A continuación se muestra un statechart que utiliza el evento y la transición personalizados:

Este es el aspecto de la implementación del diagrama de estados:
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);
Una vez iniciada la máquina, podemos enviarle eventos.
machine.postEvent(new StringEvent("Hello")); machine.postEvent(new StringEvent("world"));
Un evento que no sea manejado por ninguna transición relevante será consumido silenciosamente por la máquina de estados. Puede ser útil agrupar estados y proporcionar un manejo por defecto de tales eventos; por ejemplo, como se ilustra en el siguiente diagrama de estados:

Para diagramas de estado profundamente anidados, puede añadir tales transiciones "fallback" al nivel de granularidad que sea más apropiado.
Uso de la política de restauración para restaurar propiedades automáticamente
En algunas máquinas de estados puede ser útil centrar la atención en asignar propiedades en los estados, no en restaurarlas cuando el estado ya no está activo. Si sabes que una propiedad debe ser siempre restaurada a su valor inicial cuando la máquina entra en un estado que no da explícitamente un valor a la propiedad, puedes establecer la política de restauración global a QStateMachine::RestoreProperties.
QStateMachine machine; machine.setGlobalRestorePolicy(QStateMachine::RestoreProperties);
Cuando se establece esta política de restauración, la máquina restaurará automáticamente todas las propiedades. Si entra en un estado donde una propiedad dada no está definida, primero buscará en la jerarquía de ancestros para ver si la propiedad está definida allí. Si lo está, la propiedad será restaurada al valor definido por el ancestro más cercano. Si no, se restaurará a su valor inicial (es decir, el valor de la propiedad antes de que se ejecutara cualquier asignación de propiedad en los estados).
Tomemos el siguiente código:
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);
Digamos que la propiedad fooBar es 0.0 cuando la máquina arranca. Cuando la máquina está en el estado s1, la propiedad será 1.0, ya que el estado le asigna explícitamente este valor. Cuando la máquina está en el estado s2, no se define explícitamente ningún valor para la propiedad, por lo que implícitamente se restaurará a 0.0.
Si estamos utilizando estados anidados, el padre define un valor para la propiedad que es heredado por todos los descendientes que no asignen explícitamente un valor a la propiedad.
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);
Aquí s1 tiene dos hijos: s2 y s3. Cuando se entra en s2, la propiedad fooBar tendrá el valor 2.0, ya que éste está explícitamente definido para el estado. Cuando la máquina está en el estado s3, no se define ningún valor para el estado, pero s1 define que la propiedad sea 1.0, así que este es el valor que se asignará a fooBar.
Animaciones y Máquinas de Estado
La API de la máquina de estados se conecta con The Animation Framework para permitir animar automáticamente las propiedades a medida que se asignan en los estados.
La máquina de estados proporciona un estado especial que puede reproducir una animación. Un QState también puede establecer propiedades cuando se entra o se sale del estado, y este estado especial de animación interpolará entre estos valores cuando se le dé un QPropertyAnimation.
Podemos asociar una o más animaciones a una transición entre estados utilizando una clase QSignalTransition o QEventTransition. Ambas clases derivan de QAbstractTransition, que define la función de conveniencia addAnimation() que permite añadir una o más animaciones activadas cuando se produce la transición.
También tenemos la posibilidad de asociar propiedades a los estados en lugar de establecer nosotros mismos los valores de inicio y fin.
Digamos que tenemos el siguiente código:
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, &QPushButton::clicked, s2);
Aquí definimos dos estados de una interfaz de usuario. En s1 el button es pequeño, y en s2 es más grande. Si hacemos clic en el botón para pasar de s1 a s2, la geometría del botón se fijará inmediatamente cuando se haya entrado en un estado determinado. Sin embargo, si queremos que la transición sea suave, todo lo que tenemos que hacer es crear un QPropertyAnimation y añadirlo al objeto de transición.
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, &QPushButton::clicked, s2); transition->addAnimation(new QPropertyAnimation(button, "geometry"));
Añadir una animación para la propiedad en cuestión significa que la asignación de la propiedad ya no tendrá efecto inmediato cuando se haya entrado en el estado. En su lugar, la animación empezará a reproducirse cuando el estado haya sido introducido y animará suavemente la asignación de la propiedad. Como no establecemos el valor inicial ni el valor final de la animación, éstos se establecerán implícitamente. El valor inicial de la animación será el valor actual de la propiedad cuando comience la animación, y el valor final se establecerá basándose en las asignaciones de propiedades definidas para el estado.
Si la política de restauración global de la máquina de estados se establece como QStateMachine::RestoreProperties, también es posible añadir animaciones para las restauraciones de propiedades.
Detección de que todas las propiedades han sido establecidas en un estado
Cuando se utilizan animaciones para asignar propiedades, un estado ya no define los valores exactos que tendrá una propiedad cuando la máquina esté en el estado dado. Mientras la animación se está ejecutando, la propiedad puede tener potencialmente cualquier valor, dependiendo de la animación.
En algunos casos, puede ser útil poder detectar cuando a la propiedad se le ha asignado realmente el valor definido por un estado.
Digamos que tenemos el siguiente código:
QMessageBox *messageBox = new QMessageBox(mainWindow); messageBox->addButton(QMessageBox::Ok); messageBox->setText("Button geometry has been set!"); messageBox->setIcon(QMessageBox::Information); QState *s1 = new QState(); QState *s2 = new QState(); s2->assignProperty(button, "geometry", QRectF(0, 0, 50, 50)); connect(s2, &QState::entered, messageBox, SLOT(exec())); s1->addTransition(button, &QPushButton::clicked, s2);
Cuando se hace clic en button, la máquina pasará al estado s2, que establecerá la geometría del botón, y luego aparecerá un cuadro de mensaje para alertar al usuario de que la geometría ha sido cambiada.
En el caso normal, donde no se utilizan animaciones, esto funcionará como se espera. Sin embargo, si se establece una animación para geometry de button en la transición entre s1 y s2, la animación se iniciará cuando se introduzca s2, pero la propiedad geometry no alcanzará realmente su valor definido antes de que la animación termine de ejecutarse. En este caso, el cuadro de mensaje aparecerá antes de que la geometría del botón se haya establecido realmente.
Para asegurar que el cuadro de mensaje no aparezca hasta que la geometría alcance su valor final, podemos utilizar la señal propertiesAssigned() del estado. La señal propertiesAssigned() se emitirá cuando a la propiedad se le asigne su valor final, ya sea inmediatamente o después de que la animación haya terminado de reproducirse.
QMessageBox *messageBox = new QMessageBox(mainWindow); messageBox->addButton(QMessageBox::Ok); messageBox->setText("Button geometry has been set!"); messageBox->setIcon(QMessageBox::Information); QState *s1 = new QState(); QState *s2 = new QState(); s2->assignProperty(button, "geometry", QRectF(0, 0, 50, 50)); QState *s3 = new QState(); connect(s3, &QState::entered, messageBox, SLOT(exec())); s1->addTransition(button, &QPushButton::clicked, s2); s2->addTransition(s2, &QState::propertiesAssigned, s3);
En este ejemplo, cuando se pulse button, la máquina entrará en s2. Permanecerá en el estado s2 hasta que a la propiedad geometry se le asigne el valor QRect(0, 0, 50, 50). Entonces pasará a s3. Al entrar en s3, aparecerá el cuadro de mensaje. Si la transición a s2 tiene una animación para la propiedad geometry, la máquina permanecerá en s2 hasta que la animación haya terminado de reproducirse. Si no hay tal animación, simplemente se establecerá la propiedad e inmediatamente se entrará en el estado s3.
De cualquier forma, cuando la máquina está en el estado s3, se garantiza que la propiedad geometry tiene asignado el valor definido.
Si la política de restauración global se establece en QStateMachine::RestoreProperties, el estado no emitirá la señal propertiesAssigned() hasta que éstas también se hayan ejecutado.
Qué ocurre si se sale de un estado antes de que la animación haya terminado
Si un estado tiene asignaciones de propiedades, y la transición al estado tiene animaciones para las propiedades, el estado puede potencialmente ser abandonado antes de que las propiedades hayan sido asignadas a los valores definidos por el estado. Esto ocurre en particular cuando hay transiciones de salida del estado que no dependen de la señal propertiesAssigned(), como se describe en la sección anterior.
La API de la máquina de estados garantiza que una propiedad asignada por la máquina de estados o bien:
- Tiene un valor explícitamente asignado a la propiedad.
- Se está animando actualmente a un valor asignado explícitamente a la propiedad.
Cuando se sale de un estado antes de que finalice la animación, el comportamiento de la máquina de estados depende del estado de destino de la transición. Si el estado de destino asigna explícitamente un valor a la propiedad, no se realizará ninguna acción adicional. Se asignará a la propiedad el valor definido por el estado de destino.
Si el estado de destino no asigna ningún valor a la propiedad, existen dos opciones: Por defecto, se asignará a la propiedad el valor definido por el estado que abandona (el valor que se le habría asignado si se hubiera permitido que la animación terminara de reproducirse). Sin embargo, si se establece una política de restauración global, ésta tendrá prioridad, y la propiedad se restaurará de la forma habitual.
Animaciones por defecto
Como se ha descrito anteriormente, puede añadir animaciones a las transiciones para asegurarse de que se animan las asignaciones de propiedades en el estado de destino. Si quieres que se utilice una animación específica para una propiedad determinada independientemente de la transición que se tome, puedes añadirla como animación por defecto a la máquina de estados. Esto es particularmente útil cuando las propiedades asignadas (o restauradas) por estados específicos no se conocen cuando se construye la máquina.
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"));
Cuando la máquina está en el estado s2, la máquina reproducirá la animación por defecto para la propiedad fooBar ya que esta propiedad es asignada por s2.
Tenga en cuenta que las animaciones establecidas explícitamente en las transiciones tendrán prioridad sobre cualquier animación por defecto para la propiedad dada.
Anidamiento de Máquinas de Estado
QStateMachine es una subclase de QState. Esto permite que una máquina de estado sea hija de otra máquina. QStateMachine reimplementa QState::onEntry() y llama a QStateMachine::start(), de modo que cuando se entra en la máquina de estado hija, ésta empezará a ejecutarse automáticamente.
La máquina de estados padre trata a la máquina hija como un estado atómico en el algoritmo de la máquina de estados. La máquina de estado hija es autónoma; mantiene su propia cola de eventos y configuración. En particular, nótese que configuration() de la máquina hija no es parte de la configuración de la máquina padre (sólo lo es la propia máquina hija).
Los estados de la máquina de estado hija no pueden ser especificados como objetivos de transiciones en la máquina de estado padre; sólo la propia máquina de estado hija puede. A la inversa, los estados de la máquina de estado padre no pueden ser especificados como objetivos de transiciones en la máquina de estado hija. La señal finished() de la máquina de estado hija puede utilizarse para activar una transición en la máquina padre.
Véase también Qt State Machine Visión general y Qt State Machine Guía QML.
© 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.