Tuto - Comment faire communiquer Qml et C++ ?

ELC a11 - Programmation des interfaces graphiques en C++

Sommaire

Il s’agit ici d’illustrer comment mettre en place une interaction entre la partie graphique de l’application (qml) et des classes C++ qui gèrent la logique de l’application. L’application développée consiste en un compteur graphique que l’on peut incrémenter ou décrémenter avec les flèches du clavier.

compteur graphique


1. Arbre de scène de notre application (Qml - partie statique)

  • Créer un projet Qt Quick Application appelé Compteur2024. N’oubliez pas de cocher l’option Create a project that you can open in Qt Design Studio. Notez l’emplacement de votre projet sur votre DD.
  • Compilez et exécutez le projet (flèche verte dans le bandeau latéral gauche de l’application).
  • Ouvrez ce même projet mais avec l’application QT Design Studio, en sélectionnant le fichier Compteur2024.qmlproject. Cet outil permet de créer la partie graphique de manière interactive (remarque: il n’est pas nécessaire de quitter Qt Creator).
  • Familiarisez-vous avec l’interface. Voici une vue organisée en 4 colonnes (de gauche à droite):
    1. (Colonne Project) Les éléments graphiques à notre disposition (Components, en bas), ainsi que l’arborescence des éléments de notre application (Navigator, en haut);
    2. (Colonne 2D) Une vue de l’état de l’application;
    3. (Colonne Code) Le code qml correspondant à cette vue (mise-à-jour dynamique du contenu en fonction des changements apportés dans la colonne précédente);
    4. (Colonne Properties) Les propriétés de l’élément graphique en cours (dans l’exemple, il s’agit du label Hello Compteur2024).

Vue de _QT Design Studio_

  • Nettoyage de l’application par défaut
    • Dans la Colonne Code, supprimer les éléments graphiques Button, Text, et states situés à l’intérieur du Rectangle.
    • Dans la Colonne Properties, remplacer 1920 et 1080 par 320 et 240. La Colonne Code est automatiquement mise à jour.
    • Modifier la couleur blanche avec un couleur de votre choix en naviguant dans les propriétés. Profitez-en pour tester différentes propriétés (taille du bord…).
    • Ajouter un rectangle (Colonne `Project) de taille 100x50, centré dans la fenêtre (pour cela, modifiez Anchors dans le tab layout de la colonne Properties), dont la couleur contraste avec la couleur de fond.
    • Ajouter un Text centré dans ce rectangle, de taille 100x50 (il couvre ainsi tout le rectangle), centré en largeur, et centré en hauteur, police Tahoma, grasse, taille 20.
    • Cochez focus: true (cette option est cachée en bas de la colonne Properties).

À ce stade, vous devriez avoir quelque chose qui ressemble à cela:

Vue de _QT Design Studio_

  • Sauvegarder le projet en l’état, et basculer dans Qt Creator. Le fichier qml que nous venons d’éditer avec Qt Design Studio est mis-à-jour dans Qt Creator. Exécuter le projet pour vérifier que tout est OK.

Nous en avons terminé avec la partie statique de l’application (appelé arbre de scène graphique), passons maintenant aux interactions.


2. Programme principal et classe Compteur

  • Créer une classe Compteur, héritée de la classe QObject.

  • Retrouver le fichier main.cpp dans l’arborescence du projet (Compteur2024App/Source Files/src/main.cpp) et remplacez le programme principal par celui-ci (les 4 lignes ajoutées sont mises en évidence par un commentaire : // <– AJOUT ICI):

// Copyright (C) 2021 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>                // <-- AJOUT ICI
#include "compteur.h"                 // <-- AJOUT ICI

#include "app_environment.h"
#include "content/compteur.h"
#include "import_qml_components_plugins.h"
#include "import_qml_plugins.h"

int main(int argc, char *argv[])
{
    set_qt_environment();

    QGuiApplication app(argc, argv);
    Compteur aCompteur ;               // <-- AJOUT ICI

    QQmlApplicationEngine engine;
    const QUrl url(u"qrc:/qt/qml/Main/main.qml"_qs);
    QObject::connect(
        &engine,
        &QQmlApplicationEngine::objectCreated,
        &app,
        [url](QObject *obj, const QUrl &objUrl) {
            if (!obj && url == objUrl)
                QCoreApplication::exit(-1);
        },
        Qt::QueuedConnection);

    engine.addImportPath(QCoreApplication::applicationDirPath() + "/qml");
    engine.addImportPath(":/");
    engine.rootContext()->setContextProperty("vueObjetCpt", &aCompteur); // <-- AJOUT ICI
    engine.load(url);

    if (engine.rootObjects().isEmpty()) {
        return -1;
    }

    return app.exec();
}

Les ajouts permettent de créer un objet aCompteur, que l’on associe à "vueObjetCpt", ce dernier désignant le nom de notre objet dans le code Qml.

Exécuter votre programme pour vérifier que tout se passe bien (aucun changement apparent à ce stade).


3. Partie dynamique : gestion des flèches.

Basculer dans Qt Design Studio en ayant pris soin de sauvegarder vos derniers changements dans Qt Creator.

  • Dans la Colonne Code, remplacer le texte text: qsTr(“Text”) de l’objet Text par ces quelques lignes :
text: vueObjetCpt.cptQML
Keys.onPressed: {
  switch (event.key) {
    case Qt.Key_Up:
      vueObjetCpt.increment();
      break;
    case Qt.Key_Down:
      vueObjetCpt.decrement();
      break;
  }
}

Vous verrez sans doute apparaître ce message d’erreur dans le code : JavaScript blocks are not supported in ui.qml files. N’en tenez pas compte.
Le terme vueObjetCpt est le nom donné à votre objet aCompteur dans qml (cf main.cpp). Nous venons d’introduire un attribut (cptQML) et 2 méthodes (increment() et decrement()) que nous allons maintenant définir dans la classe Compteur.

Basculer dans Qt Creator après avoir pris soin de sauvegarder vos changements dans Qt Design Studio.

  • Nous allons maintenant modifier la classe Compteur.

    • Créer un attribut privé int fCompteur;, l’initialiser à 10 dans le constructeur de la classe.
    • Créer les deux méthodes d’incrémentation et de décrémentation du compteur :
      • Dans compteur.h, partie publique, ajouter :
Q_INVOKABLE void increment();
Q_INVOKABLE void decrement();
    - Dans _compteur.cpp_, ajouter :
void Compteur::increment(){
    fCompteur++;
}

void Compteur::decrement(){
    fCompteur--;
}

4. Établir la communication entre Qml et C++

Créer un Q_PROPERTY dans compteur.h, dans la partie publique (cf doc QT) :

Q_PROPERTY(QString cptQML READ readCompteur NOTIFY cptChanged)

Attention : il n’y a pas de ; à la fin de ligne.
Explications : La propriété cptQML est un QString (essentiellement une chaîne de caractères). Quand un signal cptChanged a été envoyé (et donc que la variable fCompteur a été changée), alors on peut obtenir sa valeur à l’aide de la méthode readCompteur (qui doit donc renvoyer un QString).

  • Ajouter la méthode privée readCompteur dans compteur.cpp:
QString Compteur::readCompteur() {
    return QString::number(fCompteur);
}

N’oubliez pas de déclarer la méthode dans compteur.h, partie private.
Cette méthode est utilisée par l’interface graphique pour lire la valeur contenue dans la variable fCompteur. La variable (entière) est d’abord transformée en chaîne de caractères. - Créer le signal suivant dans compteur.h (section signals:) :

void cptChanged();

Ce signal doit être envoyé dès lors que le compteur change : il faut en informer l’interface graphique pour qu’elle lise la nouvelle valeur du compteur et l’affiche dans l’élément graphique Text. En conséquence, dans compteur.cpp, faites un appel emit cptChanged(); dans les 3 méthodes (après la modification de la variable fCompteur).

  • Exécutez le programme et vérifiez que vous pouvez manipuler le compteur avec les flèches.

Question Comment bloquer le compteur à des valeurs comprises entre 5 et 15 ?