我在文件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读取正确的属性值?
答案 0 :(得分:1)
小工具从根本上受到设计的限制,你不能在QML的指针基础上工作,你必须使用实例,这也意味着按值传递和返回。这也意味着您所做的任何操作都应用于副本,而不是您想要的实际对象。
有一种方法可以解决这个问题,通过使用PIMPL,并且实际上小工具对象是一个指针,也就是说,它只有一个成员变量,它是指向实际对象数据实现的指针,这是一个单独的对象。
这使得小工具对象的复制非常便宜,因为它只是一个指针,并且所有副本都引用相同的对象数据,因此您的更改不会丢失。
更新
无论如何,如果您的gadget属性只是一个int或您的注释中所述的其他原语,那么请将其作为int属性。你可以拥有带通知的属性,如果你在绑定中使用它们,QML会抱怨它们没有通知,但是你可以简单地传递一个从未发出的虚拟信号并且你已经设置好了。
即使你选择了PIMPL,也没有必要在每个原语的基础上进行动态分配。指针并不关心它指向的内容,除非存在内存变为无效的危险,指针 - 悬空。