将 Q_GADGET 中的 Q_INVOKABLE 暴露给 QML [英] Exposing Q_INVOKABLE in Q_GADGET to QML

查看:38
本文介绍了将 Q_GADGET 中的 Q_INVOKABLE 暴露给 QML的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最初的目标是使枚举的名称在 QML 中可用.名称可以通过 QMetaEnum 和 QVariant 的 toString() 提供,这两种方法在 QML 中都不可用.

My original goal was to make the Name of an enum available in QML. The name can be provided via QMetaEnum and QVariant's toString(), neither of which are available in QML.

Stack Overflow 上的文章展示了如何将 Q_INVOKABLE 添加到 QObject;此方法可以实现上述任何一种方法来解决问题(并且此方法将是一个方便的解决方案,这些枚举未在 Object 中声明).但是,这些枚举是使用 Q_ENUM_NS 定义的,它不支持 Q_INVOKABLE AFAIK.

Articles here on Stack Overflow show how to add a Q_INVOKABLE to a 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). However, these enums are defined using Q_ENUM_NS, which has no place/support for Q_INVOKABLE AFAIK.

所以问题变成了:如何以尽可能简单的方式使 C 函数或静态方法"可用于 QML?

So the question became: how to make a C-function or 'static method' available to QML, in the lightest way possible?

我想避免使用基于 Q_OBJECT 的解决方案,因为不需要信号或 QObject 提供的大量开销.从 doc.qt.io 和这里的阅读来看,答案似乎是 Q_GADGET 中的 Q_INVOKABLE.在这里阅读 Q_GADGET 指出:

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. From reading in doc.qt.io and here, it seemed that the answer would be a Q_INVOKABLE within a Q_GADGET. Reading here Q_GADGET states:

Q_GADGET 可以有 Q_ENUM、Q_PROPERTY 和 Q_INVOKABLE,但它们不能有信号或槽.

Q_GADGETs can have Q_ENUM, Q_PROPERTY and Q_INVOKABLE, but they cannot have signals or slots.

如何创建一个新的实例QML 中的 Q_GADGET 结构? 显示结构/Q_GADGET,但使用中间对象.

How to create new instance of a Q_GADGET struct in QML? Shows a Struct/Q_GADGET, but uses an intermediary object.

QML - Q_INVOKABLE 函数 在对象中显示 Q_INVOKABLE,但在小工具中不显示.

QML - Q_INVOKABLE functions Shows Q_INVOKABLE within objects, but not gadgets.

QML 可以看到我的 Q_GADGET 但不能看到 Q_OBJECT 接近,显示在结构中定义多个 Q_PROPERTY 的代码.它还提到代码工作正常,但没有显示 QML 或 main.cpp 代码.注意:不是作者的错,因为他的目的是让 QObject 工作,而不是结构.

QML can see my Q_GADGET but not Q_OBJECT Comes close, shows code defining multiple Q_PROPERTY within a Struct. It also mentions the code works fine, but doesn't show the QML or main.cpp code. Note: Not the author's fault since his aim was to get the QObject to work, not the Struct.

我可以继续.我找不到一篇文章,显示 Q_GADGET 中的 Q_INVOKABLE 直接暴露在 QML 中,而不依赖于 QObject.可以这样做吗?如果可以,您会使用类似于下面的结构吗?

I could go on. I was unable to find an article showing a Q_INVOKABLE within Q_GADGET exposed directly into QML without relying on a 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"); }
    };

为了回应@JarMan,我添加了一个带有 main.cpp 和 main.qml 的 MRE.我尝试了各种 qmlRegister... 方法.我不清楚需要使用哪些 qmlRegister... 方法来注册 Struct(或者是否需要使用它们).engine.rootContext()->setContextProperty(...) 适用于对象实例,但会为 struct Foo 生成错误.
foo.h

In response to @JarMan, I have added an MRE with main.cpp and main.qml. I have tried various qmlRegister... methods. 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). engine.rootContext()->setContextProperty(...) works well with an object instance, but generates an error for struct Foo.
foo.h

#include <QQmlContext>

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

ma​​in.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();
}

ma​​in.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 现在可以正常工作(如下所述)- 效果很好.

SOLUTION FOR setContextProperty

Thanks to @JarMan for providing the solution for setContextProperty: setContextProperty now works (as described below) - it works great.

    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,并将结构放在专用头文件中(显示在顶部)允许 MRE 编译、运行和生成预期的输出.所以这两种方法之一有效:我可以通过 setContextProperty 公开 Q_INVOKABLE.
经验教训:

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. So one of the two methods works: I can expose the Q_INVOKABLE via setContextProperty.
Lessons learned:

  1. Q_GADGET [和 Q_OBJECT] 如果没有专用的头文件,往往会产生错误,并且
  2. 以 QVariant 形式发送结构体!

qmlRegisterType 和 import Foo 1.0 的组合不起作用.在进一步研究之后,即使使用 Q_OBJECT 我也遇到了类似的错误,这告诉我我的错误与 Q_GADGET 无关.我会仔细研究一下.

The combination of qmlRegisterType and import Foo 1.0 is not working. 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. I will look more into it.

注意:似乎 Q_GADGET [和 Q_ENUM,就此而言] 是自动注册的,不需要 Q_DECLARE_METATYPE 宏,根据这个 Q_DECLARE_METATYPE(Type) 链接.

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.

推荐答案

我认为你真的很接近.只是缺少一些东西:

I think you're really close. There were just a couple things missing:

  1. 使用 Q_DECLARE_METATYPE 允许 QVariant 保存一个 Foo 对象:
  1. Use Q_DECLARE_METATYPE to allow QVariant to hold a Foo object:

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

  1. 将您的小工具作为 QVariant 发送到 QML:

    Foo foo;
    engine.rootContext()->setContextProperty("Foo", QVariant::fromValue<Foo>(foo));

然后在 QML 中,调用 Foo.bar() 工作正常.

Then in the QML, calling Foo.bar() works fine.

这篇关于将 Q_GADGET 中的 Q_INVOKABLE 暴露给 QML的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆