【问题标题】:C++ signal to QML slot in QtC++ 信号到 Qt 中的 QML 插槽
【发布时间】:2012-02-08 16:29:18
【问题描述】:

我想从 C++ 向我的 QML 文件中的插槽发送信号。 我已经让它在没有原始类型参数的情况下工作,但如果我想向我的 QML Slot 发送QString,我在连接时会出错。

我在 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)

【问题讨论】:

  • 您的 updateViewWithItem 中没有 QString 参数? (错误指出)
  • 抱歉,测试后忘记重新添加了。仍然无法正常工作。我试过 updateViewWithItem(QString) 和 updateViewWithItem(string)。
  • 来自 C++ 的信号是指 C++ 库或 Qt 类???
  • 谢谢,这个问答对我很有帮助。为了将来参考,一个很好的例子现在在这里:github.com/andrewrjones/qml2-to-cpp-and-back-signals

标签: c++ qt qml signals-slots


【解决方案1】:

我认为最好查看本教程:

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

尤其是这个部分:

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

我认为您在这种情况下的错误可能是您没有将其声明为插槽,或者您没有使其可调用。 Qt 教程中解释了这两个选项。

此外,您需要使用 QVariant 在 C++ 和 QML 之间交换数据。 您还可以注册类型,例如小部件和东西,以便您可以在 QML 中将它们用作“原生”类型,如矩形。在大多数情况下,不建议这样做,除非您需要某些特定的外部类或某些无法在 QML 界面中显示的数据。

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

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

我附上了这个主题的教程,它描述了如何使用 QListModel。

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

【讨论】:

  • 我在这里不使用 QDeclarativeView 子类。正如我在我的问题中指出的那样,如果我忽略参数,一切都会奏效。我必须以某种方式将 QString 引入我的 QML 吗?
  • 在这种情况下,请尝试使用 QVariant。我有几次遇到这个问题,QML 不能识别除 QVariant 之外的其他数据类型。
  • 嘿,这解决了我的问题!仍然想知道是否有更好的解决方案。曾经尝试过使用 qmlRegisterType?
  • 我刚刚更新了我的回复。如果足够,请将其标记为答案。
【解决方案2】:

在这种情况下,您应该使用Connections(也许这是唯一的连接方式)。

  1. 通过setContextProperty将你的对象myObj放到QML文件中

    qmlVectorForm->rootContext()->setContextProperty("YourObject", myOb);
    
  2. 你的信号是

    finishedGatheringDataForItem(QString signalString)
    
  3. 在 QML 文件中,添加如下 Connectios:

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

【讨论】:

  • QML 文件在哪里?
  • 当数据交换频率高或大量时,这可能会导致 UI 出现延迟。
  • 请确保 SIGNAL SLOT 并不总是具有良好的性能。因此,为了进行优化,您应该在通过 SIGNAL 发送数据之前对其进行处理。在某些情况下,如果您在两个线程之间连接,请注意不要以如此高的速率发送 SIGNAL,这会导致 UI 卡住。您应该制定一种机制来控制发送频率(可能使用计时器或延迟)
  • @HorusKol 将Connections 对象放入应接收连接的 QML 组件中。通常它将是根组件。请参阅doc.qt.io/qt-5/qml-qtqml-connections.html 中的示例
  • 我仍然无法习惯 QML 做这件事的方式和好的旧 C++ 代码之间的主要区别。基本上,插槽是在内部生成的,无需手动定义(因为在 QML 中,插槽基本上是signal decleration 的附带功能)。
【解决方案3】:

没有Connections 和任何上下文的解决方案不是连接信号槽,而是连接信号信号。找到here。 示例代码如下。

qml:

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

Background类的头文件包含

public signals:
    void cppSend(QString textOut);
public slots:
    void cppReceive(QString textIn);

而main.cpp就是这样连接它们的:

1.从qml到cpp:

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

2.从cpp到qml:

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

【讨论】:

    【解决方案4】:

    为什么不使用 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")
        }
        //--------------------------------------------------------
    }
    
    

    【讨论】:

      【解决方案5】:

      我已经尝试了很多解决方案来成功地从 C++ 信号更新 QML,但很多都不起作用。 此解决方案有效并经过测试,它基于以下答案: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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2013-03-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-10-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多