Sur cette page

TaskTree : feux de signalisation

Illustre comment implémenter le comportement d'une machine à états à l'aide de TaskTree à travers une simulation de feux tricolores.

Le feu tricolore TaskTree est une implémentation alternative des exemples de feux tricolores présentés dans Qt SCXML et Qt State Machine.

Feu de signalisation avec le feu vert allumé et un bouton « Pause »

L'application se compose de trois éléments principaux :

  • Un widget « TrafficLight » qui affiche l'état actuel.
  • Une recette d’ TaskTree qui contrôle les transitions d’état.
  • Un GlueInterface qui synchronise l’interface utilisateur avec la machine à états.

Les sections suivantes décrivent plus en détail le code de l’exemple.

Classe d'aide « Glue »

La classe d’ GlueInterface permet une séparation claire entre l’interface utilisateur et la logique opérationnelle.

enum class Light
{
    Off    = 0,
    Red    = 1 << 0,
    Yellow = 1 << 1,
    Green  = 1 << 2,
};

Q_DECLARE_FLAGS(Lights, Light)
Q_DECLARE_OPERATORS_FOR_FLAGS(Lights)

class GlueInterface final : public QObject
{
    Q_OBJECT

public:
    // operational logic -> GUI
    void setLights(Lights lights) { emit lightsChanged(lights); }

    // GUI -> operational logic
    void smash() { emit smashed(); }
    void repair() { emit repaired(); }

signals:
    void lightsChanged(Lights lights);

    void smashed();
    void repaired();
};

La classe d'GlueInterface fournit les méthodes d'interface suivantes :

MéthodeObjectif
setLights()Met à jour l'état du feu tricolore à partir de la recette TaskTree
lightsChanged()Informe l'interface utilisateur lorsque l'état des feux change
smash()/ repair()Gère la simulation de défaillance et les actions de reprise

Lorsque les utilisateurs interagissent avec l'interface utilisateur :

  • Cliquer sur « Pause » déclenche smash(), simulant ainsi un dysfonctionnement.
  • Cliquer sur « Lecture » déclenche repair(), rétablissant ainsi le fonctionnement normal.

Implémentation de la machine à états

La logique de la machine à états est implémentée via une recette TaskTree. La méthode ` recipe() ` crée cette logique en utilisant une référence à ` GlueInterface` :

ExecutableItem recipe(GlueInterface &iface)
{
    return Forever {
        Group { // "working" state
            stopOnSuccess,
            parallel,
            signalAwaiterTask(&iface, &GlueInterface::smashed), // transitions to the "broken" state
            Forever {
                switchLightsTask(&iface, Light::Red, 3s), // "red" state
                switchLightsTask(&iface, Light::Red | Light::Yellow, 1s), // "redGoingGreen" state
                switchLightsTask(&iface, Light::Green, 3s), // "green" state
                switchLightsTask(&iface, Light::Yellow, 1s), // "greenGoingRed" state
            }
        },
        Group { // "broken" state
            stopOnSuccess,
            parallel,
            signalAwaiterTask(&iface, &GlueInterface::repaired), // transitions to the "working" state
            Forever {
                switchLightsTask(&iface, Light::Yellow, 1s), // "blinking" state
                switchLightsTask(&iface, Light::Off, 1s), // "unblinking" state
            }
        }
    };
}
Gestion de l’état racine

L'élément Forever de niveau supérieur exécute ses enfants dans une boucle infinie. Il contient deux sous-groupes représentant les états « fonctionnel » et « défaillant ».

Règles de transition d’états :

  • Les états s’exécutent dans l’ordre, car l’ execution mode par défaut est sequential.
  • Lorsqu’un état se termine, l’autre commence.
  • Les transitions sont déclenchées lorsqu'un utilisateur clique sur le bouton « Pause » ou « Lecture » de l'interface utilisateur, ce qui émet le signal correspondant.
L’état de fonctionnement « working »

L’ Group « working » se compose de deux tâches s’exécutant dans l’état « parallel » :

  • L’ signalAwaiterTask se connecte au signal « GlueInterface::smashed ». Si le signal est déclenché, la tâche se termine immédiatement avec « DoneResult::Success ». Cela provoque l’arrêt de l’exécution de l’ Group « working », et la recette passe à l’état « broken ».
  • La tâche imbriquée « Forever » se compose de quatre tâches « switchLightsTask » (« red », « redGoingGreen », « green » et « greenGoingRed ») exécutées sequentially dans une boucle infinie.

L’élément « stopOnSuccess » de chaque groupe lui ordonne d’arrêter son exécution dès qu’une de ses tâches enfants se termine par « DoneResult::Success ».

Implémentation de ` switchLightsTask ` :

  • Mise à jour des états d’éclairage via ` GlueInterface::setLights()`.
  • Crée un délai temporisé à l’aide de ` timeoutTask`.
  • Conserve l’état pendant la durée spécifiée à l’aide de ` timeout`.
static Group switchLightsTask(GlueInterface *iface, Lights lights, const milliseconds &timeout)
{
    return {
        onGroupSetup([iface, lights] { iface->setLights(lights); }),
        timeoutTask(timeout, DoneResult::Success)
    };
}
L'état de fonctionnement « défectueux »

L’ Group « broken » est similaire à l’ Group« working ». La seule différence réside dans le fait qu’elle se connecte au signal « GlueInterface::repaired », et que les lumières basculent uniquement entre deux états, allumant et éteignant la lumière jaune.

Configuration de l’application

La configuration principale de l’application crée et connecte tous les composants :

int main(int argc, char **argv)
{
    QApplication app(argc, argv);

    GlueInterface iface;
    QTaskTree taskTree({recipe(iface)});
    TrafficLight widget(iface);

    widget.show();
    taskTree.start();

    return app.exec();
}

La fonction ` main() ` instancie les objets `QApplication`, ` GlueInterface`, `QTaskTree` et ` TrafficLight `.

Pour construire l’objet ` taskTree `, nous lui transmettons un objet ` recipe() ` construit à partir de l’objet ` iface `. L’objet ` widget ` utilise également l’objet ` iface ` qui lui a été transmis.

Avant d’exécuter l’application, la fonction main() affiche le widget TrafficLight et lance l’arborescence des tâches.

Exécution de l’exemple

Vous pouvez exécuter l’exemple à partir de :

Exemple de projet sur code.qt.io

© 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.