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 { int val; QString name1 ; QString name2 ; QString name3 ; QString name4 ; }; class MyClass:public QObject { Q_OBJECT Q_PROPERTY(QVariant myVariant READ getmyVariant WRITE setmyVariant NOTIFY myVariantChanged) public: explicit MyClass(QObject *parent = nullptr); MyStruct strObj; QVariant getmyVariant() const { QVariant var; var.setValue(strObj); return var; } void setmyVariant(<Arguments>) { //How to set the value from QML } signals: void myVariantChanged(); }
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.myVariant.name1) //***undefined } } }
If i print just
(classObj.myVariant)
i am getting QVariant(MyStruct) but when i tried to access any parameter likeclassObj.myVariant.name1
i am getting "undefined" and also how to set a variant from QML? -
@Wieland Thanks!!! for your suggestion after lot of reading and searching I got the answer ,Actually we can use structs or any object which is not derived from QObject by using Q_GADGET :
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) };
Then in my class i just replaced QVariant as below :
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(); }
Now in QML file i can just use classObj.mystr.name1 to access the members and i can just use classObj.mystr.name1 = "abc" to set the values.
-
Hi! Simple answer is: no. To make the members' names available to QML your object has to derive from QObject and expose the members via Q_PROPERTY. If, for some reason, you need to keep the struct as it is, you'll need to provide a wrapper class as an interface to QML, like:
#ifndef MYWRAPPER_H #define MYWRAPPER_H #include <QObject> class MyStruct; class MyWrapper : public QObject { Q_OBJECT Q_PROPERTY(int val READ val WRITE setVal NOTIFY valChanged) // ... public: explicit MyWrapper(QObject *parent = nullptr); ~MyWrapper(); int val() const; void setVal(int v); // ... signals: valChanged(int); // ... private: MyStruct *m_myStruct; }; #endif // MYWRAPPER_H
A QVariant with a POD inside does not expose its members' names to the QML environment. If you want to go with QVariant and have names you need to use QVariantMap. Here is a class "Car" that exposes your MyStruct as a QVariantMap:
car.h
#ifndef CAR_H #define CAR_H #include <QObject> #include <QVariantMap> #include "mystruct.h" class Car : public QObject { Q_OBJECT Q_PROPERTY(QVariantMap myStruct READ getMyStruct WRITE setMyStruct NOTIFY myStructChanged) public: Car(QObject *parent = nullptr); QVariantMap getMyStruct() const; void setMyStruct(QVariantMap myStruct); signals: void myStructChanged(QVariantMap myStruct); private: MyStruct m_myStruct; }; #endif // CAR_H
car.cpp
#include "car.h" #include "myadapter.h" Car::Car(QObject *parent) : QObject(parent) { } QVariantMap Car::getMyStruct() const { return myStructToQVariantMap(m_myStruct); } void Car::setMyStruct(QVariantMap myStruct) { MyStruct newValue = myStructFromQVariantMap(myStruct); if (myStructEqual(m_myStruct, newValue)) return; m_myStruct = newValue; emit myStructChanged(myStruct); }
It relies on some helper functions:
myadapter.h#ifndef MYADAPTER_H #define MYADAPTER_H #include "mystruct.h" #include <QVariantMap> QVariantMap myStructToQVariantMap(MyStruct const &myStruct); MyStruct myStructFromQVariantMap(QVariantMap const &vm); bool myStructEqual(MyStruct const &myStruct1, MyStruct const &myStruct2); #endif // MYADAPTER_H
myadapter.cpp
#include "myadapter.h" QVariantMap myStructToQVariantMap(const MyStruct &myStruct) { QVariantMap res; res.insert("val", myStruct.val); res.insert("name1", myStruct.name1); res.insert("name2", myStruct.name2); res.insert("name3", myStruct.name3); res.insert("name4", myStruct.name4); return res; } MyStruct myStructFromQVariantMap(const QVariantMap &vm) { MyStruct res; res.val = vm.value("val").toInt(); res.name1 = vm.value("name1").toString(); res.name2 = vm.value("name2").toString(); res.name3 = vm.value("name3").toString(); res.name4 = vm.value("name4").toString(); return res; } bool myStructEqual(MyStruct const &myStruct1, MyStruct const &myStruct2) { if (myStruct1.val != myStruct2.val) return false; if (myStruct1.name1 != myStruct2.name1) return false; if (myStruct1.name2 != myStruct2.name2) return false; if (myStruct1.name3 != myStruct2.name3) return false; if (myStruct1.name4 != myStruct2.name4) return false; return true; }
As you can see, this QVariantMap approach is pretty cumbersome and obviously involves a lot of copying. But at least you can now access "the members" by their names in QML and you get the notifications when the MyStruct object changes.
Forum.Car { id: myCar } Row { anchors.centerIn: parent spacing: 20 Button { text: "click me" onClicked: { var obj = myCar.myStruct // retrieve a copy of the struct obj.val = 42 // set value in the copy myCar.myStruct = obj // replace old struct with the copy } } Label { text: myCar.myStruct.val } }
-
This post is deleted!
-
@Wieland Thanks!!! for your suggestion after lot of reading and searching I got the answer ,Actually we can use structs or any object which is not derived from QObject by using Q_GADGET :
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) };
Then in my class i just replaced QVariant as below :
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(); }
Now in QML file i can just use classObj.mystr.name1 to access the members and i can just use classObj.mystr.name1 = "abc" to set the values.
-
One more thing, you did this...
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) };
... where you put
Q_GADGET
in the public section. This has the side effect that all the members that come afterQ_GADGET
are private now. That's becauseQ_GADGET
is a macro that expands to...#define Q_GADGET \ public: \ static const QMetaObject staticMetaObject; \ void qt_check_for_QGADGET_macro(); \ typedef void QtGadgetHelper; \ private: \ QT_WARNING_PUSH \ Q_OBJECT_NO_ATTRIBUTES_WARNING \ Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \ QT_WARNING_POP \ QT_ANNOTATE_CLASS(qt_qgadget, "") \ /*end*/
... , see qobjectdefs.h.
So maybe better follow the usual convention to avoid surprises:
class MyStruct { Q_GADGET 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) public: int m_val; QString m_name1; QString m_name2; QString m_name3; QString m_name4; };
-
@Wieland Thanks !! for pointing that .
-
@Wieland Is there any possibility that I can access structure inside a structure?
struct MyStruct { Q_GADGET int m_val; QString m_name1; QString m_name2; QString m_name3; QString m_name4; MyNewStruct m_newStr; //** new Struct includes Q_GADGET macro and member definations. 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) Q_PROPERTY(MyNewStruct newStr MEMBER m_newStr) //**Currently getting error };
-
-
This post is deleted!
-
@Wieland
error: no match for 'operator!=' (operand types are 'myStruct1' and 'myStruct1') if (_t->mynewstr != *reinterpret_cast< myStruct1*>(_v)) {
where Mystruct1 is myNewStruct and the error is in MOC ...
-
The MOC generates some code for you to implement all the Gadget / Property stuff. Looks like the generated code uses the != operator for MyStruct. So you need to implement that:
public: bool operator==(MyStruct const &other) const; bool operator!=(MyStruct const &other) const;
bool MyStruct::operator==(const MyStruct &other) const { // compare members return true; } bool MyStruct::operator!=(MyStruct const &other) const { return !(*this == other); }
-
@Wieland That worked !!! Thanks, How to know that which all operators should be overloaded?
-
@pra7 said in Best way to access a cpp structure in QML:
How to know that which all operators should be overloaded?
You can't really know. But when the compiler complains about a missing operator, just implement it. In this case we didn't really need the
==
operator, but I'd say it's common practice to implement the!=
operator using the==
operator. -
@Wieland Thanks for all your suggestions.
-
@pra7, @Wieland how can I access a list of Structure inside another Structure.
Eg: From the above example, I need to useQList<MyNewStructure> m_StructList; How is that possible? I have tried, QList<MyNewStructure> m_StructList; Q_PROPERTY(QList<MyNewStruct> newStr MEMBER m_newStr) but didnt help.
-
-