简体   繁体   English

将 Q_GADGET 中的 Q_INVOKABLE 暴露给 QML

[英]Exposing Q_INVOKABLE in Q_GADGET to QML

My original goal was to make the Name of an enum available in QML.我最初的目标是在 QML 中提供枚举的名称。 The name can be provided via QMetaEnum and QVariant's toString(), neither of which are available in QML.该名称可以通过 QMetaEnum 和 QVariant 的 toString() 提供,在 QML 中都没有。

Articles here on Stack Overflow show how to add a Q_INVOKABLE to a QObject; Stack Overflow 上的文章展示了如何将 Q_INVOKABLE 添加到 QObject; this method can implement either of the above methods to solve the problem (and this method would be a handy solution these enums were not declared inside an Object).此方法可以实现上述任何一种方法来解决问题(并且此方法将是一个方便的解决方案,这些枚举未在 Object 中声明)。 However, these enums are defined using Q_ENUM_NS, which has no place/support for Q_INVOKABLE AFAIK.但是,这些枚举是使用 Q_ENUM_NS 定义的,它没有位置/支持 Q_INVOKABLE AFAIK。

So the question became: how to make a C-function or 'static method' available to QML, in the lightest way possible?所以问题变成了:如何以最轻的方式使 QML 可以使用 C 函数或“静态方法”?

I would like to avoid a Q_OBJECT-based solution, since there is no need for signals or a lot of the overhead provided by QObject.我想避免使用基于 Q_OBJECT 的解决方案,因为不需要信号或 QObject 提供的大量开销。 From reading in doc.qt.io and here, it seemed that the answer would be a Q_INVOKABLE within a Q_GADGET.通过阅读 doc.qt.io 和这里,答案似乎是 Q_GADGET 中的 Q_INVOKABLE。 Reading here Q_GADGET states:在这里阅读Q_GADGET状态:

Q_GADGETs can have Q_ENUM, Q_PROPERTY and Q_INVOKABLE, but they cannot have signals or slots. Q_GADGET 可以有 Q_ENUM、Q_PROPERTY 和 Q_INVOKABLE,但它们不能有信号或槽。

How to create new instance of a Q_GADGET struct in QML? 如何在 QML 中创建 Q_GADGET 结构的新实例? Shows a Struct/Q_GADGET, but uses an intermediary object.显示 Struct/Q_GADGET,但使用中间 object。

QML - Q_INVOKABLE functions Shows Q_INVOKABLE within objects, but not gadgets. QML - Q_INVOKABLE 函数显示对象内的 Q_INVOKABLE,但不显示小工具。

QML can see my Q_GADGET but not Q_OBJECT Comes close, shows code defining multiple Q_PROPERTY within a Struct. QML 可以看到我的 Q_GADGET 但看不到 Q_OBJECT接近了,显示了在 Struct 中定义多个 Q_PROPERTY 的代码。 It also mentions the code works fine, but doesn't show the QML or main.cpp code.它还提到代码工作正常,但没有显示 QML 或 main.cpp 代码。 Note: Not the author's fault since his aim was to get the QObject to work, not the Struct.注意:不是作者的错,因为他的目标是让 QObject 工作,而不是 Struct。

I could go on.我可以打开 go。 I was unable to find an article showing a Q_INVOKABLE within Q_GADGET exposed directly into QML without relying on a QObject .我无法找到一篇文章,显示 Q_GADGET 中的Q_INVOKABLE 直接暴露在 QML 中而不依赖 QObject It is possible to do so?有可能这样做吗? If so, would you use a struct similar to the one below?如果是这样,您会使用类似于下面的结构吗?

    struct Foo
    {
        Q_GADGET
        public:
        Q_INVOKABLE static QString bar() { return QString("invoked"); }
    };

In response to @JarMan, I have added an MRE with main.cpp and main.qml.作为对@JarMan 的回应,我添加了一个带有 main.cpp 和 main.qml 的 MRE。 I have tried various qmlRegister... methods.我尝试了各种 qmlRegister... 方法。 It is not clear to me which of the qmlRegister... methods needs to be used to register the Struct (or if they need to be used at all).我不清楚需要使用哪些 qmlRegister... 方法来注册 Struct(或者是否需要使用它们)。 engine.rootContext()->setContextProperty(...) works well with an object instance, but generates an error for struct Foo. engine.rootContext()->setContextProperty(...)适用于 object 实例,但会为 struct Foo 生成错误。
foo.h foo.h

#include <QQmlContext>

struct Foo
{
    Q_GADGET
    public:
    Q_INVOKABLE static QString bar() { return QString("Invoked"); }
};

main.cpp:主.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 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());
    }
}

SOLUTION FOR setContextProperty setContextProperty 的解决方案

Thanks to @JarMan for providing the solution for setContextProperty: setContextProperty now works (as described below) - it works great.感谢@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));

The changes (above) to main.cpp, commenting import Foo in main.qml, and placing struct in a dedicated header file (shown at top) allowed the MRE to compile, run and produce the expected output.对 main.cpp 的更改(上图),在 main.qml 中注释import Foo ,并将结构放置在专用的 header 文件中(如顶部所示)允许 MRE 编译、运行和生成预期的 Z78E6221F6398FCE41. So one of the two methods works: I can expose the Q_INVOKABLE via setContextProperty.所以这两种方法之一有效:我可以通过 setContextProperty 公开 Q_INVOKABLE。
Lessons learned:得到教训:

  1. Q_GADGET [and Q_OBJECT, for that matter] tend to generate errors if they don't have dedicated header files, and Q_GADGET [和 Q_OBJECT,就此而言] 如果它们没有专用的 header 文件,它们往往会产生错误,并且
  2. Send the struct in a QVariant!在 QVariant 中发送结构!

The combination of qmlRegisterType and import Foo 1.0 is not working. qmlRegisterType 和import Foo 1.0的组合不起作用。 After researching some more, I got similar errors even with a Q_OBJECT, which tells me that my error has nothing to do with Q_GADGET.在研究了更多之后,即使使用 Q_OBJECT,我也遇到了类似的错误,这告诉我我的错误与 Q_GADGET 无关。 I will look more into it.我会更多地研究它。

Note: it seems that Q_GADGET [and Q_ENUM, for that matter] is registered automatically and does not need the Q_DECLARE_METATYPE macro, per this Q_DECLARE_METATYPE(Type) link.注意:似乎 Q_GADGET [和 Q_ENUM,就此而言] 是自动注册的,并且不需要 Q_DECLARE_METATYPE 宏,根据此Q_DECLARE_METATYPE(Type)链接。

I think you're really close.我觉得你真的很亲近。 There were just a couple things missing:只是缺少了几件事:

  1. Use Q_DECLARE_METATYPE to allow QVariant to hold a Foo object:使用Q_DECLARE_METATYPE允许 QVariant 保存 Foo object:
struct Foo
{
    Q_GADGET
public:
    Q_INVOKABLE static QString bar() { return QString("Invoked"); }
};
Q_DECLARE_METATYPE(Foo)
  1. Send your gadget to QML as a QVariant:将您的小工具作为 QVariant 发送到 QML:
    Foo foo;
    engine.rootContext()->setContextProperty("Foo", QVariant::fromValue<Foo>(foo));

Then in the QML, calling Foo.bar() works fine.然后在 QML 中,调用Foo.bar()工作正常。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM