如何通过 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,也没有必要在每个图元的基础上或根本没有动态分配。指针不关心它指向什么,除非存在内存失效的危险,并且指针悬空。
我在文件 mygadget.h
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:
#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,也没有必要在每个图元的基础上或根本没有动态分配。指针不关心它指向什么,除非存在内存失效的危险,并且指针悬空。