Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best way to access a cpp structure in QML

I need to pass structures between cpp and QML. If i use property i should create an individual set and get functions, My structure contains minimum 5 members so i felt it's not good to use set and get for all those members. Following is an example of what i am trying to do :

MyClass.h

#include <QObject>
#include <QDebug>
using namespace std;

struct MyStruct {
Q_GADGET
int m_val;
QString m_name1;
QString m_name2;
QString m_name3;
QString m_name4;
Q_PROPERTY(int val MEMBER m_val)
Q_PROPERTY(QString name1 MEMBER m_name1)
Q_PROPERTY(QString name2 MEMBER m_name2)
Q_PROPERTY(QString name3 MEMBER m_name3)
Q_PROPERTY(QString name4 MEMBER m_name4)
};

class MyClass:public QObject
    {
        Q_OBJECT
    Q_PROPERTY(MyStruct mystr READ getMyStruct
                WRITE setMyStruct NOTIFY myStructChanged)

public:
    explicit MyClass(QObject *parent = nullptr);
    MyStruct strObj;

     // Edit: changed get function
     MyStruct getMyStruct() const
     {
         return strObj;
     }

// Edit: Added set function
     void setMyStruct(myStruct val)
        {
            mystr = val;
            emit myStructChanged();
        }

signals:
void myStructChanged();

}

main.cpp

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QDebug>
#include <QObject>

#include "MyClass.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;

    MyClass classObj;

    engine.rootContext()->setContextProperty("classObj",&classObj);

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

    if (engine.rootObjects().isEmpty())
        return -1;

    return app.exec();
}

Main.qml

import QtQuick 2.6
import QtQuick.Controls 2.2
import QtQuick.Window 2.3

ApplicationWindow {

    id: applicationWindow

    visible: true
    width: 600
    height: 400
    title: qsTr("My App")

    MainForm{
        id : mainform

        Component.onCompleted: {
        console.log("name===="+classObj.mystr.name1)

        //EDIT added more code to explain the use case.
        classObj.myStr.name1 = "abc"  //Calls setter 
        classObj.mystr.name2 = "ans" // Calls setter 
        }
    }
}

If i print just (classObj.myVariant) i am getting QVariant(MyStruct) but when i tried to access any parameter like classObj.myVariant.name1 i am getting "undefined" and also how to set a variant from QML?

[UPDATE] - Should also add MyStruct to Q_DECLARE_METATYPE as below: Q_DECLARE_METATYPE(MyStruct)

like image 329
pra7 Avatar asked Aug 12 '17 12:08

pra7


2 Answers

You need meta data to access C++ objects from QML.

For non QObject derived, this is achieved by using the Q_GADGET macro, and exposing the members as properties:

struct MyStruct {
    Q_GADGET
    int m_val;
    QString m_name1;
    QString m_name2;
    QString m_name3;
    QString m_name4;
    Q_PROPERTY(int val MEMBER m_val)
    Q_PROPERTY(QString name1 MEMBER m_name1)
    Q_PROPERTY(QString name2 MEMBER m_name2)
    Q_PROPERTY(QString name3 MEMBER m_name3)
    Q_PROPERTY(QString name4 MEMBER m_name4)
};
like image 145
dtech Avatar answered Nov 16 '22 00:11

dtech


  1. your struct or simple class must have Q_GADGET as minimum
  2. you should declare properties in order to access from qml
  3. you must declare your struct/class by Q_DECLARE_METATYPE()
  4. you must register it using qRegisterMetaType<>() somewhere before loading qml file by engine such as main.cpp

so you will have something like this:

//review carefully
struct MyStruct {
    Q_GADGET    //<-- 1.
    Q_PROPERTY(QString str1 MEMBER m_str1)    //<-- 2.
public:    //<-- important
    QString m_str1;
};
Q_DECLARE_METATYPE(MyStruct)    //<-- 3.

and use somewhere:

class Controller : public QObject
{
    Q_OBJECT
public:
    explicit Controller(QObject *parent = nullptr);
    Q_INVOKABLE MyStruct setNewConfig(QString v);    //<-- e.g.
    //...
}

main.cpp

//...
qmlRegisterType<Controller>("AppKernel", 1, 0, "Controller");
qRegisterMetaType<MyStruct>();    //<-- 4.
//...
engine.load(url);
//...

so it is usable in qml
main.qml

//...
    Controller {
        id: con
    }
    FileDialog {
        id: fileDialog
        nameFilters: ["Config file (*)"]
        onAccepted: {
            var a = con.setNewConfig(file);
            console.log(a.str1);    //<-- yeah! it is here
        }
    }
//...

NOTE 1: Be careful, it seems that nested classes/struct not supported by Qt meta

NOTE 2: You can expose struct just like a class. Inherit from QObject and use Q_OBJECT. See this article from Evgenij Legotskoj

NOTE 3: Above instructions makes struct/class known to qml and you can access properties/members, but is not instantiable in qml. see Qt document

NOTE 4: Be aware that qmlRegisterType<>() method is marked as "obsolete" in Qt 5.15+. Keep yourself updated ;)

like image 29
S.M.Mousavi Avatar answered Nov 16 '22 00:11

S.M.Mousavi