如何通过Q_PROPERTY将指向Q_GADGET的指针暴露给QML

时间:2017-04-22 13:16:55

标签: c++ qt qml qproperty qgadget

我在文件MyGadget中定义了Q_GADGET mygadget.h

#include <QObject>

class MyGadget {
    Q_GADGET
    Q_PROPERTY(int value READ value CONSTANT)

public:
    MyGadget() = default;
    MyGadget(int i) 
        : _value{i}
    {
    }

    int value() const
    {
        return _value;
    }

private:
    int _value{0};
};
Q_DECLARE_METATYPE(MyGadget)
Q_DECLARE_METATYPE(MyGadget*)

和一个Context类,它包含MyGadget的实例,并通过Q_PROPERTY向QML公开指向它的指针:

#include <QObject>
#include "mygadget.h"

class Context : public QObject
{
    Q_OBJECT
    Q_PROPERTY(MyGadget* gadget READ gadget CONSTANT)
public:
    explicit Context()
        : QObject{nullptr}
     {
     }

    MyGadget* gadget() {
        return &_gadget;
    }
private:
    MyGadget _gadget{4};
};

Context中创建main的实例,并将其作为上下文属性公开给QML:

#include <QGuiApplication>
#include <QQuickView>
#include <QString>
#include <QQmlContext>

#include "context.h"

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

    QQuickView view;
    Context c;

    // register MyGadget
    qmlRegisterUncreatableType<MyGadget>("Test", 1, 0, "MyGadget", "");
    qRegisterMetaType<MyGadget*>(); // <- removing this doesn't change anything

    // make Context instance a context propery
    view.rootContext()->setContextProperty("context", &c);

    // show QML
    view.setSource(QUrl{QStringLiteral("qrc:/main.qml")});
    view.show();

    return app.exec();
}

与此一起使用的QML文件是

import QtQuick 2.5
import Test 1.0

Rectangle {
    height: 600
    width: 800
    visible: true

    Text {
        text: qsTr("Hello World " + context.gadget.value)
        anchors.centerIn: parent
    }
}

一切都很好,但在运行时没有显示任何文字,QML会发出警告

  

qrc:/main.qml:9:TypeError:无法读取属性&#39; value&#39; null。

如果我删除qmlRegisterUncreatableType<MyGadget>main的调用以及QML文件中相应的import Test 1.0,则文本&#34; Hello World未定义&#34;而是显示。

我打印它的唯一方法&#34; Hello World 4&#34;正如预期的那样,让Context::gadget返回存储的MyGadget对象的副本而不是指向它的指针,或者使MyGadget成为Q_OBECT。但是这两个在我的实际应用程序中都不是可行的选项,因为我在这里需要引用语义,但在其他地方我也希望在这个例子中对应于MyGadget的类具有值语义。 / p>

如何让QML读取正确的属性值?

1 个答案:

答案 0 :(得分:1)

小工具从根本上受到设计的限制,你不能在QML的指针基础上工作,你必须使用实例,这也意味着按值传递和返回。这也意味着您所做的任何操作都应用于副本,而不是您想要的实际对象。

有一种方法可以解决这个问题,通过使用PIMPL,并且实际上小工具对象是一个指针,也就是说,它只有一个成员变量,它是指向实际对象数据实现的指针,这是一个单独的对象。

这使得小工具对象的复制非常便宜,因为它只是一个指针,并且所有副本都引用相同的对象数据,因此您的更改不会丢失。

更新

无论如何,如果您的gadget属性只是一个int或您的注释中所述的其他原语,那么请将其作为int属性。你可以拥有带通知的属性,如果你在绑定中使用它们,QML会抱怨它们没有通知,但是你可以简单地传递一个从未发出的虚拟信号并且你已经设置好了。

即使你选择了PIMPL,也没有必要在每个原语的基础上进行动态分配。指针并不关心它指向的内容,除非存在内存变为无效的危险,指针 - 悬空。