2

MyGadget在文件中定义了一个 Q_GADGETmygadget.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在 QML 中创建一个实例main并将其作为上下文属性公开:

#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:无法读取 null 的属性“值”。

如果我在 QML 文件中删除对qmlRegisterUncreatableType<MyGadget>in的调用main和对应的调用,import Test 1.0则会显示文本“Hello World undefined”。

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

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

4

1 回答 1

3

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

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

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

更新:

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

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

于 2017-04-22T13:40:02.140 回答