在QML中访问cpp结构的最佳方式

10

我需要在cpp和QML之间传递结构体。如果我使用property,我应该创建单独的set和get函数。我的结构体至少包含5个成员,因此我觉得对所有这些成员使用set和get不好。以下是我尝试做的示例:

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 
        }
    }
}
如果我只打印 (classObj.myVariant),我会得到QVariant(MyStruct),但是当我尝试访问任何参数,例如classObj.myVariant.name1时,我会得到"未定义"的结果。另外,如何从QML设置变量? [更新] - 还应将MyStruct添加到Q_DECLARE_METATYPE中,如下所示:Q_DECLARE_METATYPE(MyStruct)
2个回答

17

您需要元数据才能从QML访问C ++对象。

对于非QObject派生的对象,可以通过使用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)
};

它成功了!!! 如果我需要从QML填充QVariant,该怎么做?你能给个例子吗? - pra7
1
很简单,只需输入 yourvar.val = 555 - dtech
有没有一种方法可以同时设置结构的所有成员?现在,如果我在QML中使用classObj.mystr.name1 =“abc”classObj.mystr.name2 =“ans”等,每次调用setter函数。有没有办法克服这个问题?我已经更新了我的问题,请检查。 - pra7
1
不,这是不可能的。如果它已经工作,你不需要做任何额外的事情。如果你想节省重复,只需编写一个函数,设置接受目标对象和5个属性值的所有内容即可。 - dtech
1
我建议您扩展您的答案以成为一个完整的工作示例。我认为留给用户的10%可能会导致问题。 - TSG
显示剩余11条评论

9
  1. 你的结构体或简单类必须至少拥有 Q_GADGET 标记
  2. 你应该声明属性以便从 QML 中进行访问
  3. 你必须通过 Q_DECLARE_METATYPE() 声明你的结构体 / 类
  4. 你必须在加载 QML 文件之前(例如在 main.cpp 中)使用 qRegisterMetaType<>() 注册它

这样你就会得到像这样的代码:

//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.

并在某处使用:

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);
//...

因此它可在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
        }
    }
//...

注意1:请小心,似乎Qt元对象系统不支持嵌套类/结构体。

注意2:您可以像使用class一样公开struct。从QObject继承并使用Q_OBJECT。参见这篇来自Evgenij Legotskoj的文章

注意3:上述说明使得结构体/类被qml知晓并且可以访问属性/成员,但是在qml中不能实例化。请参考Qt文档

注意4:请注意,qmlRegisterType<>()方法在Qt 5.15+中被标记为“已过时”。请保持更新;)


这是一个关于如何使用现有结构体的很好的描述,但是如果用户只想访问结构体定义呢? - mzimmers
@mzimmers,您的意思是什么?也许您想要的可以通过完整功能的基于QObject的类来实现。 - S.M.Mousavi
我应该说的是结构体声明,而不是定义(这样就可以从QML创建一个新实例)。我认为通过正确声明结构体并使用QML变量赋值调用c'tor可能是可能的,但我还没有完全弄清楚。 - mzimmers
我已经在NOTE 3中提到了。您需要声明一个具有Q_OBJECT宏的完整功能的基于Object的结构/类,以使其在QML中可实例化。因此,您可以自由地使用该新类型。 - S.M.Mousavi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接