【问题标题】:How to execute QML slot from C++ without a signal?如何在没有信号的情况下从 C++ 执行 QML 插槽?
【发布时间】:2020-11-02 12:17:06
【问题描述】:

我得到了一个 QObject 形状的 QML 对象。使用->setProperty(..., ...) 我可以更改属性,但是如何在没有信号的情况下执行槽?

目前我在我的 C++ 类中声明了一个信号:

signals:
  void testSignal();

QML 对象中的信号/槽:

signal executeTestFunc(); onExecuteTestFunc: {
  testAnimation.start();
}

然后我把这两个连接起来:

QObject::connect(this, SIGNAL(testSignal()),
                 QmlObject, SIGNAL(executeTestFunc()));

但这并不干净,如我所见:

  1. 事实上这是奇怪的信号/信号连接。
  2. 我不想使用 SIGNAL/SLOT 机制,除非我必须这样做,因为性能和长代码。

有没有办法直接从QObject 执行QML onExecuteTestFunc();

【问题讨论】:

  • slot 是一个常规函数,你可以像所有其他函数一样执行它。顺便说一句,QObject::connect 应该将插槽连接到信号,而不是信号到信号,可能你这里有错字。
  • @folibis,我知道信号应该连接到插槽,但我发现这是唯一的方法:-(。你能告诉我如何从 C++ 代码执行 QML 插槽然后信号/槽机制?
  • 考虑到 QML 插槽是一个常规函数,您可以使用 QMetaObject::invokeMethod 从 C++ 调用它
  • @folibis 信号可以实际上连接到其他信号,并且有很好的用例。不要使用 QMetaObject::invokeMethod 之类的东西从 C++ 调用到 QML。它可以工作,但会产生不灵活且不可维护的代码。你的 C++ 不应该知道你的 QML 的结构。

标签: c++ qt qml signals-slots


【解决方案1】:

您可以创建一个发出信号的 C++ 类。该信号将被 QML 捕获。不需要带有 SIGNAL/SLOT 的显式 connection。示例:

C++:

class Presenter : public QObject
{
    Q_OBJECT
public:
    explicit Presenter()
    {
        QTimer *timer = new QTimer();
        timer->setInterval(500);
        connect(timer, &QTimer::timeout,
                this, &Presenter::timeout);
        timer->start();
    }

    Q_SIGNAL void timeout();
};

QML:

Window {
    ...
    Presenter {
        onTimeout: {
            console.log("called from c++")
        }
    }
}

结果:

qml: called from c++
qml: called from c++
qml: called from c++
qml: called from c++
...

【讨论】:

    【解决方案2】:

    @Thomenson 已经就如何连接到来自 C++ 的信号并在 QML 中对其进行操作给出了很好的答案。如果您可以从 QML 创建 C++,他的解决方案就有效,但情况可能并非总是如此。

    连接元素

    您还可以考虑其他两种选择。首先,如果您有一个不是从 QML 创建的对象,而是以其他方式放入其中的对象(QML 上下文、静态对象、从可调用...),您可以使用 Connections 元素:

    Connections {
        target: theObjectWithTheSignal
        onSignalName: {doSomething();}
    }
    

    这是一种灵活的方式来响应来自您不是在 QML 中创建的对象的信号,甚至是在 QML 的其他地方创建的对象。

    使用 JSValue 回调

    如果您真的坚持避免使用信号/插槽(尽管我不明白为什么;Qt 和 QML 是围绕它构建的,并且试图避免它与框架对抗而不是利用其优势),还有另一种方法。您可以在暴露给 QML 的 C++ 对象上创建QJSValue 类型的属性。然后,在 C++ 设置中,检查设置的内容是否可调用(使用QJSValue::isCallable())。然后,当您希望触发您想要在 QML 中执行的任何内容时,请使用 QJSValue::call 调用它。

    在 QML 方面,您可以简单地分配或绑定可调用的东西,就像您为信号处理程序所做的那样。

    反模式:QMetaObject::invokeMethod

    还有另一种方式,我将仅将其作为对反模式的警告。您可以使用 Qt 的自省机制从 C++ 调用 QML。您可以通过设置objectName 找到一个对象,并使用QMetaObject::invokeMethod 对其调用任何(可调用的)方法,读取和写入任何属性并监听所有信号。这包括调用您在 QML 中定义的方法。使用它,您可以从 C++ 操作 QML。 不要这样做。这会导致代码不灵活且不可维护。

    【讨论】:

    • 据我所知,您不应该使用高频信号或通过它传输大量数据。不对吗?
    • 如果您使用信号/插槽进行跨线程通信,信号/插槽可能会因高频和/或大量数据而阻塞。在这种情况下,调用和参数被编组到一个事件中,并且数据包装在 QVariants 中,然后在接收对象线程中再次解包。但是,在这种情况下,连接将是直接的,这会导致简单的方法调用。但是,您可能根本不希望对 QML 进行太高频率和高数据量的调用。不过那是另一回事了。注意 QJSValue 还要求您将参数包装到 QJSValues 列表中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-10-08
    相关资源
    最近更新 更多