Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. International
  3. French
  4. Qt/QML Exposer une variable C++ au QML
Forum Updated to NodeBB v4.3 + New Features

Qt/QML Exposer une variable C++ au QML

Scheduled Pinned Locked Moved Unsolved French
2 Posts 2 Posters 879 Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • K Offline
    K Offline
    Kawa7
    wrote on last edited by
    #1

    Il y a t-il un moyen d'exposer une variable C++ au QML sans avoir de risque de fuite mémoire ? J'ai reproduit le problème dans cette exemple :
    Bug.pro :

    QT += quick \
        widgets
    
    CONFIG += c++17
    
    # The following define makes your compiler emit warnings if you use
    # any Qt feature that has been marked deprecated (the exact warnings
    # depend on your compiler). Refer to the documentation for the
    # deprecated API to know how to port your code away from it.
    DEFINES += QT_DEPRECATED_WARNINGS
    
    # You can also make your code fail to compile if it uses deprecated APIs.
    # In order to do so, uncomment the following line.
    # You can also select to disable deprecated APIs only up to a certain version of Qt.
    #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0
    
    SOURCES += \
            connection.cpp \
            device.cpp \
            main.cpp
    
    RESOURCES += qml.qrc
    
    # Additional import path used to resolve QML modules in Qt Creator's code model
    QML_IMPORT_PATH =
    
    # Additional import path used to resolve QML modules just for Qt Quick Designer
    QML_DESIGNER_IMPORT_PATH =
    
    # Default rules for deployment.
    qnx: target.path = /tmp/$${TARGET}/bin
    else: unix:!android: target.path = /opt/$${TARGET}/bin
    !isEmpty(target.path): INSTALLS += target
    
    HEADERS += \
        connection.h \
        device.h
    

    Device.h :

    #ifndef DEVICE_H
    #define DEVICE_H
    
    #include <QObject>
    
    class Device : public QObject
    {
        Q_OBJECT
        Q_PROPERTY(QString name READ name CONSTANT)
    
    private:
        QString m_name;
    
    public:
        explicit Device(QObject *parent = nullptr);
        Device(const Device & device);
        Device(QString name);
        QString name() const;
    };
    
    //Q_DECLARE_METATYPE(Device)
    
    #endif // DEVICE_H
    

    Device.cpp :

    #include "device.h"
    
    Device::Device(QObject *parent) : QObject(parent)
    {
        m_name = "Device name";
    }
    
    Device::Device(const Device & device) : QObject()
    {
        m_name = device.m_name;
    }
    
    Device::Device(QString name) : QObject()
    {
        m_name = name;
    }
    
    QString Device::name() const
    {
        return m_name;
    }
    

    Connection.h :

    #ifndef CONNECTION_H
    #define CONNECTION_H
    
    #include <QObject>
    #include "device.h"
    
    class Connection : public QObject
    {
        Q_OBJECT
    public:
        explicit Connection(QObject *parent = nullptr);
    
    public slots:
        void call();
    
    signals:
        void signalWithoutPointer(Device device);
        void signalWithPointer(Device * device);
    };
    
    #endif // CONNECTION_H
    

    Connection.cpp :

    #include "connection.h"
    #include <QDebug>
    
    Connection::Connection(QObject *parent) : QObject(parent){}
    
    void Connection::call()
    {
        qDebug() << "Function call emitted";
        Device device = Device("Device");
        Device * pointer = new Device("Pointeur");
        emit signalWithoutPointer(device); //undefined or converted to QVariant
        emit signalWithPointer(&device); //use after free (only if the receiver is in another thread i.e. Qt::QueuedConnection)
        emit signalWithPointer(pointer); //memory leak
    }
    

    main.cpp:

    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    #include <QQuickItem>
    
    #include "connection.h"
    #include "device.h"
    
    int main(int argc, char *argv[])
    {
        QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
    
        QGuiApplication app(argc, argv);
    
        qmlRegisterSingletonType<Connection>("com.bug.connection", 1, 0, "Connection",
                                        [](QQmlEngine * engine, QJSEngine * scriptEngine) -> QObject * {
                                            Q_UNUSED(engine)
                                            Q_UNUSED(scriptEngine)
                                            Connection * connection = new Connection();
                                            return connection;
                                        });
        qmlRegisterType<Device>("com.bug.device", 1, 0, "Device");
    
        QQmlApplicationEngine engine;
        const QUrl url(QStringLiteral("qrc:/main.qml"));
        QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                         &app, [url](QObject *obj, const QUrl &objUrl) {
            if (!obj && url == objUrl)
                QCoreApplication::exit(-1);
        }, Qt::QueuedConnection);
    
        QQmlComponent component(&engine, QUrl(QStringLiteral("qrc:/main.qml")));
        component.create();
        int exec = app.exec();
        return exec;
    }
    

    main.qml :

    import QtQuick 2.12
    import QtQuick.Controls 2.12
    import QtQuick.Layouts 1.12
    import QtQuick.Window 2.12
    import com.bug.connection 1.0
    import com.bug.device 1.0
    
    Window {
        visible: true
        width: 640
        height: 480
        title: qsTr("Hello World")
    
        Text {
            id: text1
            text: qsTr("Default")
        }
    
        Connections {
            target: Connection
            onSignalWithoutPointer: (device) =>{
                console.log("Signal emitted without pointer : " + device)
                console.log("Device's name without pointer : " + device.name)
                text1.text = device.name
            }
            onSignalWithPointer: (device) =>{
                 console.log("Signal emitted with pointer : " + device)
                 console.log("Device's name with pointer : " + device.name)
                 text1.text = device.name
             }
        }
    
        Button {
            id: button
            x: 270
            y: 220
            text: qsTr("Button")
            onClicked: {
                console.log("clicked button")
                Connection.call()
            }
        }
    }
    
    1 Reply Last reply
    0
    • O Offline
      O Offline
      ObiWanKennedy
      wrote last edited by
      #2

      Bonjour,

      Dans votre fonction, call, le device est détruit à la fin de la fonction,
      Le signal signalWithoutPointer ne peut pas marcher car cela demanderai un copy de l'objet Device mais il s'agit d'un QObject ou le constructeur par copy est désactivé.
      Le passage d'un pointeur vers Device, ben, la moment juste après le pointeur est null.
      Le passage avec signal sur pointeur "pointer", lui il marche mais c'est la responsabilité de la classe connection de détruire pointer (et donc de garder une variable dessus).

      Le plus simple c'est de faire un retour de fonction si vous voulez vous simplifiez la vie.

      // in Connection.cpp 
      Device* Connection::call()
      {
          return new Device("Pointeur");
      }
      
      //main.qml (à la place de la Connections)
      property Device device: Connection.call()
      

      Dans ce cadre cas là, par passage par pointer via un retour de fonction, l'objet appartiendra au QML et sera détruit par le QML Engine.


      Cependant, je recommande plutot de passer par une propriété.

      // in connection.h
      // je suggère de renommer cette classe en ConnectionController (pour éviter la confusion avec le type qml Connections
      class Connection : public QObject
      {
          Q_OBJECT
         Q_PROPERTY(Device* device READ device  CONSTANT)
      public:
          explicit Connection(QObject *parent = nullptr);
          Device* device() const;
      
      private:
       std::unique_ptr<Device> m_device;
      };
      
      // in connection.cpp
      Connection::Connection(QObject *parent) : QObject(parent), m_device(new Device("Pointeur"){}
      
      Device* Device::device() const 
      {
      return m_device.get();
      }
      
      // main.qml 
      property Device device: Connection.device
      

      Ici le device reste la responsabilité de la classe Connection, mais c'est géré par le uinique_ptr.
      Si le device peut changer, il conviendra d'ajouter un signal deviceChanged et de supprimer le CONSTANT de la Q_PROPERTY pour y mettre un "NOTIFY deviceChanged".

      1 Reply Last reply
      0

      • Login

      • Login or register to search.
      • First post
        Last post
      0
      • Categories
      • Recent
      • Tags
      • Popular
      • Users
      • Groups
      • Search
      • Get Qt Extensions
      • Unsolved