【问题标题】:QML function invoked from C++ not able to update element从 C++ 调用的 QML 函数无法更新元素
【发布时间】:2019-04-18 18:51:33
【问题描述】:

我正在从 C++ 调用 QML 函数。问题是 QML 函数在从 C++ 调用时无法更新 QML 元素。下面是代码:

在main.qml:

import QtQuick 2.0

function myQmlFunction(msg) {
    console.log("Got message:", msg)
    textbox.text = msg
    return "some return value"
}

Text {
    id: textbox
    text: "nothing"
}

在main.cpp:

QQmlEngine engine;
QQmlComponent component(&engine, "MyItem.qml");
QObject *object = component.create();

QVariant returnedValue;
QVariant msg = "Hello from C++";
QMetaObject::invokeMethod(object, "myQmlFunction",
    Q_RETURN_ARG(QVariant, returnedValue),
    Q_ARG(QVariant, msg));

qDebug() << "QML function returned:" << returnedValue.toString();
delete object;

文本框元素只是一个常规文本,其中的文本仍然是“无”,而不是预期的“Hello from C++”。

关于如何解决此问题或成功将参数从 C++ 传递到 QML 的任何想法?

【问题讨论】:

  • 请改进您的示例并提供minimal reproducible example,有许多未定义的内容可能导致错误。另一方面,您正在使用 component.create(); 创建一个新项目;这可能与在其他地方创建的 MyItem 不同,即使您更新了您的代码也没有意义。
  • main.qml 或 MyItem.qml????
  • 我看到您正在创建一个 MyItem 并立即将其销毁,因此我认为可见的 MyItem 是不同的,并且您更改了另一个 MyItem 的文本并立即将其删除。每次调用 component.create() 时,您都在创建另一个 MyItem ,而不是可能在您的窗口中的那个。如何创建窗口?
  • 再想一想,在发布我的答案后,听起来你有一个XY problem。 @Sparkskie

标签: c++ qt qml qqmlengine


【解决方案1】:

代码

Qml

我假设给出的 qml 代码实际上属于 MyItem.qml 而不是 main.qml。

您的 Qml 文件生成了编译时错误。函数应该放在对象内部,就像这样

// MyItem.qml
import QtQuick 2.0

Text {
    id: textbox
    text: "nothing"

    function myQmlFunction(msg) {
        console.log("Got message:", msg)
        textbox.text = msg
        return "some return value"
    }
}

我不确定你是如何编译你的项目而不产生错误的,但我猜也是

  1. 您的 QtCreator/Qt 版本与我的不同(极不可能的原因); 或
  2. 您试图使您的代码最小化并且最初有一个父级。

我相信你对 Qml 有足够的了解,所以我不打算深入这个。

C++

在 C++ 方面,我不得不摆弄调试输出以查看问题所在。这是我的main.cpp:

// main.cpp
#include <QApplication>
#include <QDebug>
#include <QQmlApplicationEngine>
#include <QQmlComponent>
#include <QQuickItem>

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);  // Qt requires an instance of QApplication


    QQmlEngine *engine = new QQmlEngine;

    QString projectPath = "/Users/user/full/path/to/your/project";  // I'm on a Mac, but replace
                                                    // with the appropriate path to your project

    //  QQmlComponent component(engine, "MyItem.qml");   // this didn't work for me and
                                      // set component.status() to QQmlComponent::Error

    QQmlComponent component(engine, projectPath + "/qml/MyItem.qml");  // use full path

    qDebug() << "Status:" << component.status();
    if (component.status() == QQmlComponent::Error)
        qDebug() << "Errors:" << component.errors();
    else if (component.status() != QQmlComponent::Ready)
    {
        qDebug() << "Component is not ready!";
        return 0;
    }

    QObject *object = component.create();
    if (!object) { qDebug() << "Object creation failed!"; return 0; }

    QQuickItem *item = qobject_cast<QQuickItem*>(object);   // adding this didn't change much
                                                     // but this could be crucial

    QVariant returnedValue;
    QVariant msg = "Hello from C++";

    bool success = QMetaObject::invokeMethod(item, "myQmlFunction",   // replace `object` with `item`
                                             Q_RETURN_ARG(QVariant, returnedValue),
                                             Q_ARG(QVariant, msg));

    if (success)
        qDebug() << "QML function returned:" << returnedValue.toString();
    else
        qDebug() << "QMetaObject::invokeMethod returned false";

    delete object;

    return 0;
}

输出

我在成功构建和成功创建对象时收到的输出是

Status: QQmlComponent::Status(Ready)
Object: MyItem_QMLTYPE_0(0x7f8d4ae8b640)
qml: Got message: Hello from C++
QML function returned: "some return value"

我还没有检查你的 Qml textbox 中的文本是否发生了变化。 (没有打扰。它需要对 C++ 代码进行更多更改,而且这个答案已经足够长了。我也确信不会出错,所以¯\_(ツ)_/¯)。


Lé 非代码

如果我不想使用原始文件路径怎么办?

如果您meh在

中使用原始文件路径(例如/Users/whoami/ugly/looking/path)
QString projectPath = "/Users/user/full/path/to/your/project";

您可以将其添加到您的 .pro 文件中:

DEFINES += SOURCE_PATH=$$PWD

并将projectPath 设置为

QString projectPath = QT_STRINGIFY(SOURCE_PATH);

这个想法是从forum thread借来的。


假设

在我的回答中,我假设您的项目层次结构类似于

/./
 |- myProject.pro
 |- main.cpp
 |- qml/
    |- MyItem.qml

最重要的是您使用 qml 项目的完整路径。如果您确实找到另一个来引用它(也许使用QUrl?)然后做发表评论。


进一步阅读

查看QQmlComponent 类的details section 和QQmlComponent::create member function。阅读这些让我知道要调试哪些值以及要注意什么。

【讨论】:

    【解决方案2】:

    感谢您的帮助, 我也调试了它,textbox.text 被“Hello from C++”覆盖,而窗口中的文本没有被更新。

    就像 eyllanesc 建议的那样,我正在创建一个新的引擎对象,而不是已经显示的窗口。 (在代码的其他地方创建)

    引用同一个对象后,问题就解决了。

    【讨论】:

      猜你喜欢
      • 2013-11-28
      • 2021-02-06
      • 1970-01-01
      • 2015-08-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-14
      • 1970-01-01
      相关资源
      最近更新 更多