【问题标题】:Call C++ function from QML/JS with C++ object as argument使用 C++ 对象作为参数从 QML/JS 调用 C++ 函数
【发布时间】:2020-07-09 22:20:54
【问题描述】:

我想从 QML/JS 调用一个 C++ 函数,该函数将在 QML 中实例化的 C++ 类(数据对象)作为参数。 程序编译并运行,但是当我尝试从 QML/JS 调用 C++ 函数时,出现以下错误:

“无法将参数 0 转换为” “onClicked@qrc:/main.qml:26” “将不兼容的参数从 JavaScript 传递给 C++ 函数是危险的并且已被弃用。” “这将在 Qt 的未来版本中引发 JavaScript TypeError!”

我已经尝试了引用/值/指针调用的所有组合,以及 Q_GADGET 和 Q_OBJECT 的多种变体,但我根本无法让它工作。

我使用基于 QObject 的 C++ 数据类型以及所有调用组合的最小示例如下:

C++ 数据类型


#include <QObject>
#include <QDebug>

//Data type which should be instantiated in QML
class CustomDataObject : public QObject{

    Q_OBJECT
    Q_PROPERTY(int exampleValue MEMBER exampleValue)

public:

    int exampleValue;

    explicit CustomDataObject(QObject * parent = nullptr) : QObject(parent) {}
    ~CustomDataObject() = default;
    CustomDataObject(const CustomDataObject& blob)  {}

};

Q_DECLARE_METATYPE(CustomDataObject);

具有要调用的函数的 C++ 类


//Class with function called from QML
class UniqueDataBackend : public QObject {

    Q_OBJECT

public:

    //Call with native data type (works)
    Q_INVOKABLE void processNativeObject(int value) {
        qDebug() << "C++: Nativ Value: " << value;
    }

    //Call by value (does not work)
    Q_INVOKABLE void processDataObjectByValue(const CustomDataObject data) {
        qDebug() << "C++: Data Object Value: " << data.exampleValue;
    }

    //Call by reference (does not work)
    Q_INVOKABLE void processDataObjectByReference(const CustomDataObject & data) {
        qDebug() << "C++: Data Object Value: " << data.exampleValue;
    }

    //Call by pointer (does not work)
    Q_INVOKABLE void processDataObjectByPointer(const CustomDataObject * data) {
        qDebug() << "C++: Data Object Value: " << data->exampleValue;
    }


    UniqueDataBackend(QObject * parent = nullptr) : QObject(parent) {}
    ~UniqueDataBackend() = default;
    UniqueDataBackend(const UniqueDataBackend& blob)  {}
};

Q_DECLARE_METATYPE(UniqueDataBackend);

ma​​in.cpp


#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QQmlEngine>

#include "CustomStruct.h"

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;

    //Register types for use in QML
    qRegisterMetaType<CustomDataObject>();
    qmlRegisterType<CustomDataObject>("Custom.Types", 1, 0, "CustomDataObject");

    //Register intances for use in QML
engine.rootContext()->setContextProperty("UniqueDataBackend", new UniqueDataBackend());

    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();
}


QML 文件 (main.qml)


import QtQuick 2.12
import QtQuick.Controls 2.12
import Custom.Types 1.0

ApplicationWindow {
    id: window
    width: 400
    height: 300
    visible: true

    CustomDataObject {
        id: customData
        exampleValue: 84
    }

    Button {
        text: "Click me!"
        anchors.fill: parent
        onClicked: {
            console.log("QML: Calling c++ function with native data type!")
            UniqueDataBackend.processNativeObject(42)

            console.log("QML: Checking value of custom data type in javascript: " + customData.exampleValue)

            console.log("QML: Calling c++ function by value with custom data type!")
            UniqueDataBackend.processDataObjectByValue(customData)

            console.log("QML: Calling c++ function by reference with custom data type!")
            UniqueDataBackend.processDataObjectByReference(customData)

            console.log("QML: Calling c++ function by pointer with custom data type!")
            UniqueDataBackend.processDataObjectByPointer(customData)
        }
    }


}


输出


qml: QML: Calling c++ function with native data type!
C++: Nativ Value:  42

qml: QML: Checking value of custom data type in javascript: 84

qml: QML: Calling c++ function by value with custom data type!
"Could not convert argument 0 at"
     "onClicked@qrc:/main.qml:26"
"Passing incompatible arguments to C++ functions from JavaScript is dangerous and deprecated."
"This will throw a JavaScript TypeError in future releases of Qt!"
C++: Data Object Value:  0

qml: QML: Calling c++ function by reference with custom data type!
"Could not convert argument 0 at"
     "onClicked@qrc:/main.qml:29"
"Passing incompatible arguments to C++ functions from JavaScript is dangerous and deprecated."
"This will throw a JavaScript TypeError in future releases of Qt!"
C++: Data Object Value:  -842150451

qml: QML: Calling c++ function by pointer with custom data type!
qrc:/main.qml:32: Error: Unknown method parameter type: const CustomDataObject*

我认为这是自定义数据类型的问题,因为我可以毫无问题地调用具有本机数据类型的函数,但对于我的生活,我无法弄清楚。

非常感谢您的帮助/cmets!

【问题讨论】:

  • 非 const 指针呢?
  • 有效!我不知道我是如何忽视这一点的。万分感谢!想要将其写为答案,以便我将其标记为已解决?

标签: c++ qt qml


【解决方案1】:

也可以通过注册 const 类型来修复报告未知类型 (Error: Unknown method parameter type: const CustomDataObject*) 的错误:

qRegisterMetaType<CustomDataObject*>("const CustomDataObject*");

关于无法转换的错误 ("Could not convert argument 0 at...") 比较棘手。 QML 中的类型转换使用 QT 的元类型系统,它基于QVariant。 QVariant.canConvert() 文档中列出了内置转换。此外,您还可以阅读到指向QObject 子类型的指针之间的转换是自动的。这就是为什么你的processDataObjectByPointer 可以工作的原因,如果你按照上面提到的注册const 类型。

但除了错误消息"Passing incompatible arguments to C++ functions from JavaScript is dangerous and deprecated." 之外,我找不到关于将对象作为值或引用传递的内容,显然类型是兼容的,但消息提示我正确的做法是将值和引用作为QJSValue 传递.下面的方法可以作为您期望的参考。同样适用于“按值”等价物:

void UniqueDataBackend::processDataObjectByQJSValue(const QJSValue& data) {
    qDebug() << "C++: Data Object Value: "  << qobject_cast<CustomDataObject*>(data.toQObject())->getExample();
}

出于好奇,我检查了是否可以使用QMetaType.registerConverter<From,CustomDataObject> 注册函数转换器,以便您可以调用通过值和引用传递对象的方法。这里的难点在于知道 Qml 内部使用的 From 类型。安装QT的调试符号,源码,在几个地方放断点,发现它的ID是39,查看QMetaType中的table of built-in types,是QObject*。使用转换器需要实现赋值运算符:

CustomDataObject &CustomDataObject::operator=(const CustomDataObject &other) {
   exampleValue = other.exampleValue;
   return *this;
}

以下代码安装了一个转换器,该转换器完成了 value 和 const 引用 的调用。但不是通过 reference,因为实际参数是在转换器处创建的 QML 对象的 copy;因此更改不会反映在原始文件中。请记住,这不是官方建议的,并且完全取决于内部表示(它适用于 QT 5.12.8):

QMetaType::registerConverter<QObject*,CustomDataObject>( [] (QObject* qObjPtr) {
    CustomDataObject* dataPtr = qobject_cast<CustomDataObject*>( qObjPtr ); 
    return (dataPtr == nullptr) ? CustomDataObject() : CustomDataObject( *dataPtr ) ;
});

【讨论】:

    猜你喜欢
    • 2015-01-21
    • 2013-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多