Bienvenue sur le forum !

Si vous souhaitez rejoindre la communauté, cliquez sur l'un de ces boutons !

Qt 5 : 5.9.1 - Qt Creator : 4.4.0 - Qt Installer : 2.0.3 - JOM : 1.1.2 - Qt Build suite : 1.7.0 - VS Qt 5 : 2.0.0

Utilisation du C++ avec QML

Bonjour,

J'avais l'habitude d'utiliser les Widgets pour faire mes applications avec Qt. Or je m'intéresse au QML et ses possibilités pour faire des interfaces agréable et dynamique.

Mais entre ce que me propose ma version de Qt et ce que je trouve sur Internet, j'ai l'impression de ne rien trouver et surtout je ne comprend pas...

J'utilise souvent une communication série dans mes softs et j'aimerais donc utiliser le C++ et sa bibliothèque QSerialPort avec des interfaces en QML.

Est-ce compliqué et correct ? Actuellement quand je crée un projet Qt Quick j'ai un main.cpp :
#include <QGuiApplication>
#include <QQmlApplicationEngine>

int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);

QQmlApplicationEngine engine;
engine.load(QUrl(QLatin1String("qrc:/main.qml")));

return app.exec();
}
Mais je n'ai pas de bibliothèque QDeclarative, la plupart des tutoriels en parle, comment je fais le lien C++/QML ?

Merci pour votre aide. J'utilise Qt 5.7.0.

Réponses

  • 23 May modifié
    Pourquoi parles tu de QtDeclarative ?

    Pour faire le lien entre C++ et QML, il suffit de créer une classe dérivée de QObject, d'exposer les propriétés et fonctions et de les utiliser en QML.

    // C++
    class MyClass : public QObject {
    Q_OBJECT
    Q_PROPERTY(QString myProperty MEMBER m_myProperty NOTIFY myPropertyChanged) // property

    public:
    Q_INVOKABLE void doSomething(); // function

    signals:
    void myPropertyChanged();

    private:
    QString m_myProperty;
    };


    qmlRegisterType<MyClass>(mylib", 1, 0, "MyClass"); // dans le main.cpp par exemple


    // QML
    import mylib 1.0

    Button {
    MyClass {
    id: myClass
    myProperty: "some text"
    }
    onClicked: myClass.doSomething();
    }
    Cf la doc Qt et http://qmlbook.github.io/
  • Merci pour ton retour gbdivers.

    Désolé d'avoir mis du temps à revenir. J'ai réussi à faire quelques trucs gràce à ton exemple et le lien que tu as ajouté.

    Sur le lien que tu as ajouté il parle de faire une classe dérivée de QQmlExtensionPlugin. Quelle est la différence avec une classe dérivée de QObject ou ses possibilités ? J'ai pas tout saisie.

    Voici un petit test que j'ai qui se charge d'afficher une ComboBox avec les ports disponibles ainsi qu'un bouton connexion/déconnexion. J'espère que je m'y prend correctement.

    Mon .h :
    class SerialThread : public QObject
    {
    Q_OBJECT
    Q_PROPERTY(QString myText MEMBER p_Text NOTIFY myTextChanged) // property
    Q_PROPERTY(QStringList item MEMBER p_Item NOTIFY itemChanged) // property
    Q_PROPERTY(QString port MEMBER p_Port NOTIFY portChanged) // property
    Q_PROPERTY(int value MEMBER p_Value NOTIFY valueChanged) // property

    public:
    explicit SerialThread(QObject *parent = 0);

    Q_INVOKABLE void bpConnectClicked (); // function


    signals:
    void myTextChanged();
    void itemChanged();
    void portChanged();
    void valueChanged();


    public slots:
    void receptionTrame();


    private:

    QString p_Text;
    QStringList p_Item;
    QString p_Port;
    int p_Value;

    QSerialPort* serial;

    };

    Mon bout de code QML :

    ComboBox {
    id: cbPortComm
    width: 100
    height: 40
    y : 40
    model: serialComm.item
    onCurrentTextChanged: {
    serialComm.port = cbPortComm.currentText
    }
    }

    Button {
    id: buttonConnection
    anchors.horizontalCenter: cbPortComm.horizontalCenter
    width: 100
    height: 30
    text: qsTr(serialComm.myText)

    SerialThread{
    id: serialComm
    }
    onClicked: serialComm.bpConnectClicked();
    onTextChanged: {
    if (text == "Deconnexion" ){
    cbPortComm.enabled = false
    }
    else
    cbPortComm.enabled = true

    }
    }
    Puis dans le '.cpp' je fais un 'emit' à chaque fois que je modifie une variable.

    Je me pose une question, est-ce que ça veut dire que je dois créer une variable pour chaque propriété dans mon QML ? Ma comboBox, j'ai donc une variable pour créer la liste, une variable pour lire le choix et si je veux modifier autre chose je recrée une variable ?
  • 31 May modifié
    Bon bah tant pis pour mon post précédent.

    Est-il possible de créer un thread directement à partir de ma classe SerialThread ? Actuellement quand j'essai de faire un essai il me dit que je ne peux pas intéragir avec mon QML à partir d'un autre thread.

    De sorte que ma comm ne ralentisse pas ma UI.

    Est ce que je peux me permettre de faire :
        serial = new QSerialPort(this);

    // Preparation du thread (gère la communication série)
    QThread* thread = new QThread;
    serial->moveToThread(thread);
    thread->start();
    Ou dois je créer une seconde classe qui contiendra mon thread et mon QSerialPort ?

    Merci pour votre aide.
  • Désolé, je n'avais pas pris le temps de te répondre.

    > une classe dérivée de QQmlExtensionPlugin

    QQmlExtensionPlugin = permet de creer un plugin QML (c'est ce que tu as dans le dossier de Qt plugins/qml), c'est a dire des fonctionnalités qui sont chargées automatiquement par le QML Engine et que tu peux utiliser dans un code QML.

    Il n'est pas obligé d'utiliser ca, tu peux creer une application C++/QML, creer tes classes C++, puis exporter ces classes en QML avec qmlRegisterType. QQmlExtensionPlugin sera surtout interessant si tu veux creer une "lib" réutilisable en QML.

    > int p_Value;
    > QSerialPort* serial;

    HS : Prends l'habitude de toujours initialiser tes variables :

    int p_Value = 0;
    QSerialPort* serial = nullptr;
    > Puis dans le '.cpp' je fais un 'emit' à chaque fois que je modifie une variable.

    Il est preferable (plus simple) d'appeler le setter correspondant. (Qui contient un test, pour émettre que lorsque la valeur change réellement. Cela évite les "infinite loop", qui peut arriver selon le code JS écrit).

    > Je me pose une question, est-ce que ça veut dire que je dois créer une variable pour chaque propriété dans mon QML ? Ma comboBox, j'ai donc une variable pour créer la liste, une variable pour lire le choix et si je veux modifier autre chose je recrée une variable ?

    Oui. Malheureusement, il n'existe pas de méthode automatique pour faire cela. Le code à écrire est simple (il suffit de copier-coller), mais cela reste lourd de devoir tout écrire explicitement.

    Sans réflexion/introspection en C++, difficile de faire autrement. (Il faudra attendre Qt 7 :) )

    > Actuellement quand j'essai de faire un essai il me dit que je ne peux pas intéragir avec mon QML à partir d'un autre thread.

    Il est fort probable que QSerialPort soit deja threadé en interne. De plus, le QML Engine et le Quick Renderer sont aussi threadés. Bref, si tu ne rencontres pas de problème, t'embêtes pas avec ca.

    Si tu veux vraiment faire cela, il faut effectivement utiliser les signaux-slots pour communiquer entre ton thread personnalisé et le main thread, et c'est lui qui enverra les signaux-slots au thread du QML Engine.

    Du coup, oui, il faut créer une classe qui dérive de QThread, de facon a ajouter les signaux-slots pour communiquer avec le thread principal.
  • Merci gbdivers pour tes conseils.

    > Thread
    Je ne pense pas que QSerialPort soit threadé en interne, j'avais fait un soft avec 3 communications série et j'avais la UI qui freezait. J'avais donc dû les faire passer dans un thread et ça fonctionnait mieux.

    Donc pour le QML, je peux directement dire que ma classe SerialThread dérive de QThread et jouer avec les signaux-slots où je dois créer une seconde classe avec mon QSerialPort qui passera mes variables à travers des signaux à la première classe liée à mon QML ?


    > Lib QML
    J'ai pas mal joué ces derniers jours avec le QML, j'essaie de me faire des libs que je pourrai facilement réutiliser plus tard pour m'entraîner.

    Par exemple, si je veux mettre en place ma liaison série, j'importe ma lib dans Qt Quick Designer et je peux directement ajouter une ComboBox avec la liste des ports et mon Bouton de connexion qui enclenchera la lecture sur le port sélectionné.
    Mais dans Qt Quick Designer, lorsque je souhaite ajouter un composants, il me dit qu'il ne peux pas être créé. Mais quand je compile, le composant est bien présent et fonctionne, je n'arrive pas à l'afficher sur le Quick Designer.

    ButtonConnect.qml
    import QtQuick 2.7
    import QtQuick.Controls 2.0
    import serialib 1.0

    Button {
    id: buttonConnect
    text: qsTr("Connexion")

    width: 50;height: 50;
    property SerialThread serialId

    onClicked: {
    if (serialId.bpConnectClicked()) {
    text= "Deconnexion"
    listPorts.enabled = false
    }
    else {
    text= "Connexion"
    listPorts.enabled = true
    }
    }
    }
    Et mon exemple.qml (Qt Quick Designer)
    import QtQuick 2.7
    import QtQuick.Controls 2.0
    import QtQuick.Layouts 1.0
    import "SerialComm"
    ...
    SerialThread{
    id: serialTest
    }

    ButtonConnect {
    id: buttonConnect1
    x: 87
    y: 55
    serialId: serialTest
    }
    ...
    Bon je sais que c'est du détail et que de toute façon, je vais utiliser du code QML directement pour faire mes interfaces, mais j'aime faire les choses au mieux :)>-
    Je me suis aidé de l'exemple Qt "Dial Control Example" que j'ai importé dans mon projet et il fonctionne très bien avec le Designer.

    Merci encore pour ton aide dans mes premiers pas avec le QML ^^
Connectez-vous ou Inscrivez-vous pour répondre.