如何通过 Q_PROPERTY 将指向 Q_GADGET 的指针公开给 QML

How to expose a pointer to a Q_GADGET to QML through a Q_PROPERTY

我在文件 mygadget.h

中定义了 Q_GADGET MyGadget
#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 class,它包含 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: Cannot read property 'value' of null.

如果我删除 main 中对 qmlRegisterUncreatableType<MyGadget> 的调用以及 QML 文件中相应的 import Test 1.0 ,则会显示文本 "Hello World undefined"。

我让它按预期打印 "Hello World 4" 的唯一方法是使用 Context::gadget return 存储的 MyGadget 对象的副本而不是指向它的指针,或者将 MyGadget 改为 Q_OBECT。但是这两个在我的实际应用程序中都不是可行的选项,因为我在这里需要引用语义,但在其他地方我也希望在这个例子中对应于 MyGadget 的 class 具有值语义.

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

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

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

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

更新:

无论如何,如果您的小工具 属性 只是一个 int 或您评论中所述的其他基本类型,那么请将其设为 int 属性。您可以拥有带有通知的属性,如果您在绑定中使用它们,QML 会抱怨它们没有通知,但是您可以简单地传递一个永远不会发出的虚拟信号,并且您已设置。

即使你选择 PIMPL,也没有必要在每个图元的基础上或根本没有动态分配。指针不关心它指向什么,除非存在内存失效的危险,并且指针悬空。