Qt/QML Exposer une variable C++ au QML
Unsolved
French
-
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() } } }