Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. Exposing a C++ variable to QML
Forum Updated to NodeBB v4.3 + New Features

Exposing a C++ variable to QML

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
3 Posts 2 Posters 281 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 20 Oct 2023, 08:16 last edited by
    #1

    Is there a way to expose a C++ variable to QML without risking a memory leak? I have reproduced the problem in this example:

    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()
            }
        }
    }
    
    GrecKoG 1 Reply Last reply 20 Oct 2023, 19:15
    0
    • K Kawa7
      20 Oct 2023, 08:16

      Is there a way to expose a C++ variable to QML without risking a memory leak? I have reproduced the problem in this example:

      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()
              }
          }
      }
      
      GrecKoG Offline
      GrecKoG Offline
      GrecKo
      Qt Champions 2018
      wrote on 20 Oct 2023, 19:15 last edited by
      #2

      @Kawa7 Why do you create an instance of an object, emit it and don't keep it around?

      You have to define who gets to own it.
      Put it in a model/list, return it in a function, etc.

      K 1 Reply Last reply 25 Oct 2023, 14:33
      1
      • GrecKoG GrecKo
        20 Oct 2023, 19:15

        @Kawa7 Why do you create an instance of an object, emit it and don't keep it around?

        You have to define who gets to own it.
        Put it in a model/list, return it in a function, etc.

        K Offline
        K Offline
        Kawa7
        wrote on 25 Oct 2023, 14:33 last edited by
        #3

        @GrecKo In the context of my project, the event is temporary and I have no reason to keep the instance of this object.

        1 Reply Last reply
        0

        1/3

        20 Oct 2023, 08:16

        • Login

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