Voir le flux RSS

Jiyuu

[Actualité] Interaction entre C++ et QML. Première méthode : en avant la facilité

Noter ce billet
par , 18/09/2015 à 12h10 (1741 Affichages)
Dans l'un de mes articles je traite des manières d'interagir entre le code Python et le code QML.

Je vous propose ici d'aborder le sujet mais avec un code C++. Les lignes qui suivent présente une première méthode, qui, à mon sens est la plus simple, mais peut-être pas la plus "corporate".

Important : le but de ce billet n'est pas de présenter QML. On suppose donc que vous connaissez déjà un peu ce langage.

Si vous utilisez Qt Creator commencez par créer un nouveau projet et choisissez l'option Qt Quick Controls Application. La capture d'écran qui suit correspond à la fenêtre que vous aurez avec Qt Creator 3.5, c'est à dire la version la plus récente au moment d'écrire ces quelques lignes :
Nom : screen1.png
Affichages : 2852
Taille : 38,8 Ko

Maintenant je vous propose de modifier le code qml afin d'ajouter un bouton :

Code qml : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import QtQuick 2.5
import QtQuick.Controls 1.4
 
ApplicationWindow {
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")
 
    menuBar: MenuBar {
        Menu {
            title: qsTr("File")
            MenuItem {
                text: qsTr("&Open")
                onTriggered: console.log("Open action triggered");
            }
            MenuItem {
                text: qsTr("Exit")
                onTriggered: Qt.quit();
            }
        }
    }
 
    Label {
        id: label
        text: qsTr("Hello World")
        x: 5 ; y: 5
        width: parent.width
        height: 50
    }
    Button{
        x: 5 ; y: 100
        text: qsTr("Click here")
        onClicked: label.text = comCpp.txt()  // les lignes qui suivent expliquent ce qu'est comCpp.txt()
    }
}

Nous allons ensuite modifier notre main.cpp comme suit :
Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>  // nécessaire pour dialoguer entre le code C++ et le code QML
#include <comcpp.h>  // correspond à une classe que nous allons construire ensuite.
 
int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    ComCpp compCpp; 
 
    QQmlApplicationEngine engine;
    engine.rootContext()->setContextProperty("comCpp", &compCpp); // nous passons ensuite notre object comCpp comme contextProperty dans notre QML. C'est ainsi que nous pourrons l'utiliser.
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
 
    return app.exec();
}

Créons maintenant notre classe ComCpp en y ajoutant le slot txt():
comcpp.h
Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#ifndef COMCPP_H
#define COMCPP_H
 
#include <QString>
#include <QObject>
 
 
class ComCpp : public QObject  // IMPORTANT : pour être utilisable dans le code QML, votre classe doit dériver de QObject ou QQuickItem.
{
    Q_OBJECT
public:
    explicit ComCpp(QObject *parent = 0);
 
signals:
 
public slots:
    QString txt();
};
 
#endif // COMCPP_H

comcpp.cpp
Code c++ : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
#include "comcpp.h"
 
ComCpp::ComCpp(QObject *parent) : QObject(parent)
{
 
}
 
QString ComCpp::txt(){
    QString r = "You have clicked on QML Button";
    return r;
}

Vous n'avez plus qu'à tester votre application. En théorie un clic sur le bouton doit faire le travail attendu... Évidemment, il n'est pas nécessaire de passer par du C++ pour faire ceci, mais cela vous montre le principe.

Bonne lecture, à bientôt et n'hésitez pas à laisser vos commentaires.

++

J

Envoyer le billet « Interaction entre C++ et QML. Première méthode : en avant la facilité » dans le blog Viadeo Envoyer le billet « Interaction entre C++ et QML. Première méthode : en avant la facilité » dans le blog Twitter Envoyer le billet « Interaction entre C++ et QML. Première méthode : en avant la facilité » dans le blog Google Envoyer le billet « Interaction entre C++ et QML. Première méthode : en avant la facilité » dans le blog Facebook Envoyer le billet « Interaction entre C++ et QML. Première méthode : en avant la facilité » dans le blog Digg Envoyer le billet « Interaction entre C++ et QML. Première méthode : en avant la facilité » dans le blog Delicious Envoyer le billet « Interaction entre C++ et QML. Première méthode : en avant la facilité » dans le blog MySpace Envoyer le billet « Interaction entre C++ et QML. Première méthode : en avant la facilité » dans le blog Yahoo

Catégories
Qt Quick - QML , Programmation , C++

Commentaires

  1. Avatar de air-dex
    • |
    • permalink
    Perso je n'aime pas cette façon de faire. Je n'aime pas manipuler le QML côté C++. Je préfère largement que les deux restent indépendants. Si je dois mélanger les deux, alors je préfère largement exposer mes classes C++ à QML :

    Côté QML :
    Code QML : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    // main.qml
    import QtQuick 2.5 
    import QtQuick.Controls 1.4 
    import MyControls 3.14
     
    ApplicationWindow { 
        visible: true 
        width: 640 
        height: 480 
        title: qsTr("Hello World") 
     
        menuBar: MenuBar { 
            Menu { 
                title: qsTr("File") 
                MenuItem { 
                    text: qsTr("&Open") 
                    onTriggered: console.log("Open action triggered"); 
                } 
                MenuItem { 
                    text: qsTr("Exit") 
                    onTriggered: Qt.quit(); 
                } 
            } 
        } 
     
        Label { 
            id: label 
            text: qsTr("Hello World") 
            x: 5 ; y: 5 
            width: parent.width 
            height: 50 
        } 
        Button { 
            x: 5 ; y: 100 
            text: qsTr("Click here") 
            onClicked: label.text = comCpp.txt();  // les lignes qui suivent expliquent ce qu'est comCpp.txt() 
        } 
     
        ComCpp { id: comCpp }
    }

    Côté C++ :
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    // comcpp.hpp
    #ifndef COMCPP_HPP
    #define COMCPP_HPP
     
    #include <QObject>
    #include <QString>
     
    class ComCpp : public QObject {
        Q_OBJECT
     
        public:
            ComCpp();
            Q_INVOKABLE QString txt();
            static void declareQML();
    };
     
    #endif // COMCPP_HPP

    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // comcpp.cpp
    #include "comcpp.hpp"
    #include <QtQml>
     
    ComCpp::ComCpp() : QObject() {}
     
    void ComCpp::declareQML() {
        qmlRegisterType<ComCpp>("MyControls", 3, 14, "ComCpp");
    }
     
    QString ComCpp::txt(){ 
        QString r = "You have clicked on QML Button"; 
        return r; 
    }

    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    // main.cpp
    #include <QApplication> 
    #include <QQmlApplicationEngine> 
    #include "comcpp.hpp"
     
    int main(int argc, char *argv[]) { 
        QApplication app(argc, argv); 
        ComCpp::declareQML();    // On expose notre classe à QML ici
        QQmlApplicationEngine engine; 
        engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); 
        return app.exec(); 
    }

    Je trouve ça nettement plus propre. Mon code C++ est totalement indépendant du QML. Côté QML, tout mon backend est dans une librairie QML a priori comme les autres, ici nommée "MyControls" et qui est dans sa version 3.14.

    Pour aller plus loin :
    • Si j'ai besoin d'accéder à des attributs, alors je les mets dans des Q_PROPERTY.
    • On peut aussi appeler les slots de la classe C++ côté QML, mais perso j'évite. Généralement on n'appelle pas les slots comme on appellerait une fonction ou méthode classique, d'où les Q_INVOKABLE .
    • Si j'ai besoin d'enums, alors cf. cette réponse StackOverflow que j'ai écrite il y a quelques temps : "how to access C++ enum from QML?" (<frime>ma réponse la mieux notée sur SO au moment où j'écris ce commentaire </frime>).
    • S'il y a besoin de connecter des signaux C++ et des slots QML ou vice-versa, alors je fais ça côté QML, comme je le ferais avec des signaux et des slots QML classiques. OSEF si en fait c'est du C++ derrière.
    Mis à jour 22/09/2015 à 15h22 par air-dex (Erreurs dans le code)
  2. Avatar de Jiyuu
    • |
    • permalink
    air-dex et merci pour ton commentaire.

    Pour tout te dire, j'ai fait ce billet pour illustrer rapidement une des manières de dialoguer entre du code QML et du code C++ afin d'aider un membre (voir cette discussion)

    Mais comme tu as pu le lire au début du billet, j'accorde largement le fait que cette méthode n'est pas la plus "corporate", mais c'est de loin la plus simple.

    J'avais prévu d'illustrer les autres méthodes, du coup tu as pris un peu les devants ...
  3. Avatar de air-dex
    • |
    • permalink
    Non celui qui a vraiment pris les devants c'est Amnell car il a déjà fait un tuto sur le sujet il y a 4 ans : Communication entre QML et C++/Qt. Rendons à César ce qui est à César.

    C'est d'ailleurs à partir de ce tutoriel là que j'ai élaboré ma propre solution car je ne trouve pas que celle d'Amnell soit idéale (mais pour en discuter c'est sur ce thread et pas ailleurs).