Interaction entre C++ et QML : une solution mettant en avant la facilité pour appeler une fonction C++
Un billet de Jiuu

Le , par Jiyuu, Rédacteur/Modérateur
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 :


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

Code qml : Sélectionner tout
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
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
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
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


Vous avez aimé cette actualité ? Alors partagez-la avec vos amis en cliquant sur les boutons ci-dessous :


 Poster un commentaire

Avatar de air-dex air-dex - Membre émérite https://www.developpez.com
le 22/09/2015 à 15:20
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
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
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
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
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.
Avatar de Jiyuu Jiyuu - Rédacteur/Modérateur https://www.developpez.com
le 22/09/2015 à 15:36
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 ...
Avatar de air-dex air-dex - Membre émérite https://www.developpez.com
le 23/09/2015 à 1:36
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).
Contacter le responsable de la rubrique C++