如何通过 Q_PROPERTY 向 QML 公开指向 Q_GADGET 的指针
Posted
技术标签:
【中文标题】如何通过 Q_PROPERTY 向 QML 公开指向 Q_GADGET 的指针【英文标题】:How to expose a pointer to a Q_GADGET to QML through a Q_PROPERTY 【发布时间】:2017-04-22 13:16:54 【问题描述】:我在文件 mygadget.h
中定义了一个 Q_GADGET MyGadget
#include <QObject>
class MyGadget
Q_GADGET
Q_PROPERTY(int value READ value CONSTANT)
public:
MyGadget() = default;
MyGadget(int i)
: _valuei
int value() const
return _value;
private:
int _value0;
;
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()
: QObjectnullptr
MyGadget* gadget()
return &_gadget;
private:
MyGadget _gadget4;
;
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(QUrlQStringLiteral("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
返回存储的MyGadget
对象的副本而不是指向它的指针,或者使MyGadget
成为@987654337 @ 反而。但是这两个在我的实际应用程序中都不是可行的选项,因为我在这里需要引用语义,但在其他地方我也希望在这个例子中为与 MyGadget
对应的类有值语义。
如何让 QML 读取正确的属性值?
【问题讨论】:
【参考方案1】:小工具从根本上受到设计的限制,您不能在 QML 中以指针为基础使用它们,您必须使用实例,这也意味着按值传递和返回。这也意味着您所做的任何操作都会应用于副本,而不是您想要的实际对象。
有一种方法可以解决这个问题,通过使用 PIMPL,并且本质上让小工具对象成为一个指针,也就是说,它只有一个成员变量,它是一个指向实际对象数据实现的指针,这是一个单独的对象。
这使得小工具对象的复制非常便宜,因为它只是一个指针,并且所有副本都引用相同的对象数据,因此您的更改不会丢失。
更新:
无论如何,如果您的小工具属性只是一个 int 或您评论中所述的其他原语,那么请将其作为一个 int 属性。您可以拥有带有通知的属性,如果您在绑定中使用它们,QML 会抱怨它们没有通知,但您可以简单地传递一个从不发出的单一虚拟信号并且您已设置。
即使您选择 PIMPL,也没有必要在每个基元的基础上动态分配或根本没有必要。指针不关心它指向什么,除非内存有变得无效的危险,并且指针 - 悬空。
【讨论】:
这很可悲。我的真实世界应用程序Context
拥有大量由模拟代码更新的物理量(质量、力、速度……)。一旦它们全部更新,我希望 Context
对象通知 QML 更新视图。我明确不想在每次更改一个数量时更新它,因此它们不需要信号。另一方面,这些本质上只是原始类型的包装器,将它们分配在堆上只是为了使用 PIMPL 在应用程序的其余部分中没有意义。
仔细想想,这似乎更像是一个明确的设计决策,而不是技术限制,毕竟当我不注册MyGadget*
时所有行为都会发生变化。你知道为什么会做出这个决定吗?
阅读我的答案更新。似乎(再一次)您试图以错误的方式解决问题。我不知道为什么小工具是以这种方式实现的,很多 Qt 对我来说也没有意义。我最近才发现,并尝试了几乎所有我能想到的***.com/questions/42843942/…,然后才找到bugreports.qt.io/browse/QTBUG-54321
是的,我可能会在Context
的属性中公开原始类型。但我曾希望用正确的类型而不是原始类型来声明 QML 中的属性,以使其类型安全且更易于阅读(例如 property Force
而不是 property int
)
QML 中没有类型安全。我的意思是,如果您尝试将字符串分配给 int 或类似的东西,它会抱怨,但如果没有静态类型,该错误不会变得明显,直到在运行时尝试这样的操作。 QML 实际上甚至远非合理,因为您甚至不能在 QML T 类型中拥有类型 T 引用的属性,这将导致您的应用程序根本不显示任何内容,没有错误没有警告......我发现的另一个错误:@987654323 @以上是关于如何通过 Q_PROPERTY 向 QML 公开指向 Q_GADGET 的指针的主要内容,如果未能解决你的问题,请参考以下文章
从 C++ 向 QML 发出信号以读取 Q_PROPERTY 是同步事件吗?
在没有 Q_PROPERTY 定义的情况下从 C++ 访问 QML 对象的属性