在Qt中,如何将C++信号传递给QML的槽函数?

65

我想从C ++发送一个信号到我的QML文件中的插槽。我已经使其可以正常工作,但如果我想将 QString 发送到我的QML插槽中,连接时会出现错误。

我在main.cpp中进行连接

QObject *contentView = rootObject->findChild<QObject*>(QString("contentView"));
QObject::connect(&myObj,      SIGNAL(finishedGatheringDataForItem(QString)), 
                 contentView, SLOT(updateViewWithItem(QString)));

我的QML文件中相关部分

Rectangle {
        objectName: "contentView"
        function updateViewWithItem(string) { console.log('got some Items'); }  // slot
}

错误:

Object::connect: No such slot QDeclarativeRectangle_QML_2::updateViewWithItem(QString)

1
你的 updateViewWithItem 函数中缺少 QString 参数吗?(这就是错误指出的问题) - Bart
抱歉,在测试后忘记再次添加了。但仍然无法正常工作。我尝试使用updateViewWithItem(QString)和updateViewWithItem(string)。 - alex
1
C++中的signal是指一个C++库还是Qt类? - UmNyobe
1
谢谢,这个问题/答案真的帮了我很多。为了将来参考,现在有一个很好的例子在这里:https://github.com/andrewrjones/qml2-to-cpp-and-back-signals - Mark Ch
6个回答

47
在这种情况下,您应该使用 Connections(也许这是唯一的连接方式)。
  1. Put your object myObj to QML file by setContextProperty

    qmlVectorForm->rootContext()->setContextProperty("YourObject", myOb);
    
  2. Your signal is

    finishedGatheringDataForItem(QString signalString)
    
  3. In QML file, add Connectios likes below:

    Connections {
        target: YourObject 
        onFinishedGatheringDataForItem: {
            qmlString = signalString
        }
    }
    

QML文件的哪个位置? - HorusKol
当数据以高频率或大量交换时,这可能会导致UI延迟。 - mmoment
2
确保SIGNAL SLOT并不总是具有良好的性能。因此,为了优化,您应该在通过SIGNAL发送数据之前对其进行处理。在某些情况下,如果您在两个线程之间连接,请注意不要以如此高的速率发送SIGNAL,否则会导致UI卡顿。您应该制定一种机制来控制发送频率(可能使用计时器或延迟)。 - Ken
2
@HorusKol 将 Connections 对象放置在应该接收连接的 QML 组件内。通常它将是根组件。请参阅 http://doc.qt.io/qt-5/qml-qtqml-connections.html 中的示例。 - Paul Masri-Stone
我仍然无法习惯QML方式和老旧的C++代码之间的主要差异。基本上,插槽会在内部生成,而无需手动定义(因为在QML中,插槽基本上是“signal”声明的附属功能)。 - rbaleksandar

36

我认为最好您查看一下这个教程:

http://doc.qt.io/qt-4.8/qtbinding.html

特别是这部分内容:

http://doc.qt.io/qt-4.8/qtbinding.html#receiving-signals

我觉得在这种情况下您的错误可能是没有将其声明为一个槽或者您没有使它可以被调用。这两个选项都在Qt教程中有说明。

此外,您需要使用QVariant来在C++和QML之间交换数据。 您还可以注册类型,例如Widget等,以便您可以在QML中作为“本地”类型使用,如矩形。在大多数情况下,这是不推荐的,除非您需要某些特定的外部类或某些数据,否则无法在QML界面中显示。

QVariant的原因是QML基于脚本的方法。QVariant基本上包含您的数据和数据类型的描述,以便QML知道如何正确处理它。这就是为什么您必须使用String、int等参数在QML中指定参数的原因,但与C++的原始数据交换仍然是一个QVariant。

我以前使用过qmlRegisterType,但对于简单的数据类型来说,这是一个非常不方便的解决方案。它更多地用于更复杂的数据,例如自定义小部件、画布或视频元素,QML本身不支持或扩展的QStandardItemModels。它是一种更方便的在QML和C++之间交换数据的方式,并且首先不需要信号或槽,因为QStandardItemModel会自动更新GUI。要使用QStandardItemModel,您需要使用qmlRegisterType注册该类型。。然后可以在基于模型的视图中使用该模型,如ListView等。

我附加了一篇教程,介绍如何使用QListModel。

http://doc.qt.io/qt-4.8/qdeclarativemodels.html


我这里没有使用QDeclarativeView子类。正如我在问题中指出的那样,如果我省略参数,一切都可以正常工作。我是否需要以某种方式将QString引入到我的QML中? - alex
3
在这种情况下,尝试使用QVariant。我遇到了几次这样的问题,即QML无法识别QVariant以外的其他数据类型。 - mmoment
嘿,那个解决了我的问题!还在想是否有更好的解决方案。你试过使用qmlRegisterType吗? - alex
我刚刚在上面更新了我的回答。如果它足够满意,请将其标记为答案。 - mmoment

3

对于那些也遇到这个问题的人,我想说一切都简单得多。你只需要从C++获取QVariant参数的信号即可。例如:

QObject::connect(&recipient, SIGNAL(resTalk(QVariant)), engine.rootObjects().at(0)->findChild<QObject*>("winSettings"),
                     SLOT(showWithErrorNetwork(QVariant)));

我的信号声明如下:

signals:
    void resTalk(QVariant res);

所以我正在调用信号:
emit resTalk(true); //For more complex types, use  'emit yourSignal(QVariant(yourArg))'

这里是我在QML中使用的插槽:

    function showWithErrorNetwork(isNoError=false) {
        if(!isNoError) {
            visible = true
            warningText.text = "Network error. Check the data."
            warningText.visible = true
        }
    }

1
最佳答案 - Florian K

1
我尝试了很多解决方案来更新QML的C++信号,但很多都不起作用。这个解决方案可以工作并经过测试,它基于这个答案:https://stackoverflow.com/a/59502860/2486332(作者是@Adriano Campos)。
你可以使用信号从C++发送数据到QML,像这样:

main.cpp:

#include <QQmlContext>

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;
    engine.load(QUrl(QStringLiteral("qrc:/main.qml")));

    // Class init
    YourClass yourObject;

    // Embedding C++ Objects into QML with Context Properties
    QQmlContext* ctx = engine.rootContext();
    ctx->setContextProperty("yourObject", &yourObject);

    return app.exec();
}

main.qml:

import QtQuick 2.6

Window {
    id: mainWindow

    Connections {
        target: yourObject
        onSignalData: {
            console.log("Data: " + signal_param)
            textToChange.text = "Changed to: " + signal_param
        }
    }

    Text {
        id: textToChange
        text: "beforeChange"
    }
}

yourClass.h:

class YourClass : public QObject
{
Q_OBJECT
signals:
    // Signal from YourClass
    void signalData(QString signal_param);
}

yourClass.cpp:

emit signalData("Hello QML"); // Signal from yourClass

这个页面提供了一份完整的教程,介绍如何将一个Qt C++类与信号和槽暴露给QML。请访问https://felgo.com/cross-platform-development/how-to-expose-a-qt-cpp-class-with-signals-and-slots-to-qml

1

没有使用Connections和任何上下文的解决方案是通过连接信号与信号而不是信号与槽。在此处找到。

示例代码如下。

qml:

Window{
    signal qmlSend(string textOut)
    signal qmlReceive(string textIn)
    onQmlReceive:{
      console.log(textIn)
    }
}

背景类的头文件包含以下内容:
public signals:
    void cppSend(QString textOut);
public slots:
    void cppReceive(QString textIn);

我是一名有用的助手,可以为您提供文本翻译。

以下是需要翻译的内容:

而 main.cpp 以以下方式将它们连接起来:

1. 从 QML 到 C++:

QObject::connect(qmlRootObject, SIGNAL(qmlSend(QString)),
                backgroundObject, SLOT(cppReceive(QString)));

2.从cpp到qml:

QObject::connect(backgroundObject, SIGNAL(cppSend(QString)),
                 qmlRootObject, SIGNAL(qmlReceive(QString)));

-1

为什么不使用rootContext?

在C++端,您有:

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

//--------------------------------------------------------
#include <myClass.h>
//--------------------------------------------------------

int main(int argc, char *argv[])
{

    QGuiApplication app(argc, argv);
    QQmlApplicationEngine engine;

    //--------------------------------------------------------
    myClass * myobj = new myClass(&app);
    //--------------------------------------------------------

    //--------------------------------------------------------
    engine.rootContext()->setContextProperty("myobj",myobj);
    //--------------------------------------------------------

    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 方面,您有:

import QtQuick 2.9
import QtQuick.Window 2.2

Window {
    id: window
    visible: true
    width: 640
    height: 480
    title: qsTr("Hello World")

    //--------------------------------------------------------
    Component.onCompleted: {
       myobj.onSomeSignal.connect(signalHandling) 
    }
    //--------------------------------------------------------

    //--------------------------------------------------------
    function signalHandling(){
       console.log("Signal emitted from c++ side")
    }
    //--------------------------------------------------------
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接