【发布时间】:2021-03-16 01:13:07
【问题描述】:
我最初的目标是让枚举的名称在 QML 中可用。名称可以通过 QMetaEnum 和 QVariant 的 toString() 提供,这两种方法在 QML 中都不可用。
Stack Overflow 上的文章展示了如何将 Q_INVOKABLE 添加到 QObject;此方法可以实现上述任一方法来解决问题(并且此方法将是一个方便的解决方案,这些枚举未在 Object 中声明)。但是,这些枚举是使用 Q_ENUM_NS 定义的,它没有位置/支持 Q_INVOKABLE AFAIK。
所以问题变成了:如何以尽可能最轻的方式使 QML 可以使用 C 函数或“静态方法”?
我想避免使用基于 Q_OBJECT 的解决方案,因为不需要信号或 QObject 提供的大量开销。从 doc.qt.io 和这里的阅读来看,答案似乎是 Q_GADGET 中的 Q_INVOKABLE。在这里阅读Q_GADGET 状态:
Q_GADGET 可以有 Q_ENUM、Q_PROPERTY 和 Q_INVOKABLE,但不能 有信号或槽。
How to create new instance of a Q_GADGET struct in QML? 显示 Struct/Q_GADGET,但使用中间对象。
QML - Q_INVOKABLE functions 在对象中显示 Q_INVOKABLE,但在小工具中不显示。
QML can see my Q_GADGET but not Q_OBJECT 接近,显示代码在 Struct 中定义多个 Q_PROPERTY。它还提到代码工作正常,但没有显示 QML 或 main.cpp 代码。注意:不是作者的错,因为他的目标是让 QObject 工作,而不是 Struct。
我可以继续。我找不到文章显示 Q_GADGET 中的Q_INVOKABLE 直接暴露在 QML 中而不依赖于 QObject。 可以吗?如果可以,你会使用类似于下面的结构吗?
struct Foo
{
Q_GADGET
public:
Q_INVOKABLE static QString bar() { return QString("invoked"); }
};
作为对@JarMan 的回应,我添加了一个带有 main.cpp 和 main.qml 的 MRE。我尝试了各种 qmlRegister... 方法。我不清楚需要使用哪些 qmlRegister... 方法来注册 Struct(或者是否需要使用它们)。 engine.rootContext()->setContextProperty(...) 适用于对象实例,但会为 struct Foo 生成错误。
foo.h
#include <QQmlContext>
struct Foo
{
Q_GADGET
public:
Q_INVOKABLE static QString bar() { return QString("Invoked"); }
};
main.cpp:
#include <QGuiApplication>
#include <QQmlApplicationEngine>
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
Foo foo;
qDebug() << "in main.cpp" << foo.bar(); // works
qmlRegisterType<Foo>("Foo", 1, 0, "Foo"); // error
QQmlApplicationEngine engine;
/*engine.rootContext()->setContextProperty("Foo", &foo);*/ // error
const QUrl url(QStringLiteral("qrc:/main.qml"));
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}
main.qml
import QtQuick 2.15
import QtQuick.Controls 2.15
import Foo 1.0
ApplicationWindow {
visible: true
height: 400
width: 400
Component.onCompleted: {
console.log("In main.qml: "+Foo.bar());
}
}
setContextProperty 的解决方案
感谢 @JarMan 为 setContextProperty 提供解决方案:setContextProperty 现在可以正常工作(如下所述) - 效果很好。
Foo foo;
qDebug() << "in main.cpp" << foo.bar(); // works
//qmlRegisterType<Foo>("Foo", 1, 0, "Foo"); // error
QQmlApplicationEngine engine;
// works
engine.rootContext()->setContextProperty("Foo", QVariant::fromValue<Foo>(foo));
对 main.cpp 的更改(上图)、在 main.qml 中注释 import Foo 以及将 struct 放入专用头文件(如顶部所示)允许 MRE 编译、运行并产生预期的输出。所以这两种方法之一有效:我可以通过 setContextProperty 公开 Q_INVOKABLE。
经验教训:
- Q_GADGET [和 Q_OBJECT,就此而言] 如果它们没有专用的头文件,往往会产生错误,并且
- 在 QVariant 中发送结构!
qmlRegisterType 和import Foo 1.0 的组合不起作用。在研究了更多之后,即使使用 Q_OBJECT,我也遇到了类似的错误,这告诉我我的错误与 Q_GADGET 无关。我会进一步研究它。
注意:根据 Q_DECLARE_METATYPE(Type) 链接,Q_GADGET [和 Q_ENUM,就此而言] 似乎是自动注册的,不需要 Q_DECLARE_METATYPE 宏。
【问题讨论】:
-
您似乎有想要尝试的东西,但我不清楚您是否尝试过。所以我不确定你遇到了什么问题?
-
@JarMan 我最初有更多的文字,然后将其修剪以使其更清晰。在定义了上面的 Struct 之后,我不知道该怎么做才能访问 QML 中的方法“bar”。我将用 MRE 更新问题。我尝试了各种
qmlRegister...和setContextProperty("foo",&foo),但我尝试过的所有变体都会导致错误。