QML学习笔记(四十五)QML与C++交互:信号槽的双向实现

前言

在qml内部,我们已经学习过用Connections来连接不同组件间的信号和槽函数(处理器),但其实我们只要在qml中暴露过c++类的上下文属性,我们就可以以相同的方法绑定该c++类的槽函数,以此来实现c++端和qml端的信号槽绑定。

一、c++端的信号

我们新建一个c++类叫CppSignalSender:

.h:

cpp 复制代码
#ifndef CPPSIGNALSENDER_H
#define CPPSIGNALSENDER_H

#include <QObject>
#include <QTimer>

class CppSignalSender : public QObject
{
    Q_OBJECT
public:
    explicit CppSignalSender(QObject *parent = nullptr);

signals:
    void callQml(QString parameter);
    void cppTimer(QString value);

public slots:
    void cppSlot();

private:
    QTimer m_timer;
    int m_value;

};

#endif // CPPSIGNALSENDER_H

.cpp:

cpp 复制代码
#include "cppsignalsender.h"

CppSignalSender::CppSignalSender(QObject *parent)
    : QObject(parent),
      m_timer(new QTimer(this)),
      m_value(0)
{
    connect(&m_timer, &QTimer::timeout, this, [=](){
        ++m_value;
        emit cppTimer(QString::number(m_value));
    });

    m_timer.start(1000);
}

void CppSignalSender::cppSlot()
{
    emit callQml("Information from C++");
}

这里实现了两个信号,和一个定时器。我希望实现两个小功能:

1.qml端点击按钮后,通过上下文属性直接调用cppSlot槽函数,然后再发出callQml信号,回到qml端的Connections。

2.c++端通过定时器每秒累加value,并且将当前数字通过cppTimer信号发送到qm端。这里直接在构造函数里lamada表达式写了。

最后,我们再main中暴露该c++对象的上下文属性:

cpp 复制代码
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "cppsignalsender.h"

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

    QGuiApplication app(argc, argv);

    QQmlApplicationEngine engine;
    CppSignalSender sender;
    engine.rootContext()->setContextProperty("CppSignalSender", &sender);

    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绑定信号

cpp 复制代码
import QtQuick 2.14
import QtQuick.Window 2.14
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("QmlCppSignalSend")

    Connections{
        target: CppSignalSender

        onCallQml: function(parameter){
            console.log("This is QML: callQml signal cought:")
            mText.text = parameter
        }

        onCppTimer: function(value){
            mRectText.text = value
        }
    }

    Column{
        Rectangle{
            width: 200
            height: 200
            color: "red"
            radius: 10

            Text {
                id: mRectText
                text: "0"
                anchors.centerIn: parent
                color: "white"
                font.pointSize: 30
            }
        }

        Button{
            text: "Call C++ Method"
            onClicked: {
                CppSignalSender.cppSlot()
            }
        }

        Text {
            id: mText
            text: qsTr("Default")
        }
    }
}

代码比较简单,Connections中直接对CppSignalSender这个target的信号进行了槽函数重载,然后就可以改变按钮的文本了。

效果:

三、反过来QML发信号给C++

虽然我们知道qml端可以直接通过c++暴露的上下文属性名,直接调用它的槽函数。但实际上我们也可以将qml和C++对象的信号槽绑定起来,实现端在C++业务代码中。

我在qml中添加一个按钮:

cpp 复制代码
Button{
    text: "send"
    objectName: "qmlSender"
    property int value: 0
    signal sendValue(int value)   // 自定义信号
    onClicked: {
        value++
        sendValue(value)
    }
}

这里每点击一下按钮,就会自动递增value,并将它通过信号发送出去。这里它并不知道具体会有谁来接收这个东西,我们在main也就是c++端看实现:

cpp 复制代码
// engine 加载完 QML 后
QObject *qmlObj = engine.rootObjects().first()->findChild<QObject*>("qmlSender");
if (qmlObj){
    // 普通槽函数
    QObject::connect(qmlObj, SIGNAL(sendValue(int)),&sender,   SLOT(onQmlSendData(int)));


}

我们查找到qmlObj后,就像普通的c++信号槽一样,直接connect连接信号和刚才的c++类CppSignalSender。

最后,我在CppSignalSender中实现这个槽函数:

cpp 复制代码
void CppSignalSender::onQmlSendData(int value)
{
    qDebug()<<"c++ recv value: "<<value;
}

运行看打印:

四、总结

信号槽是qt中的最大特色机制,c++端和qml端 能够顺利通过信号槽来连接,也是其设计的侧重所在。在这种需要更改界面组件中的信息时,通过信号槽来实现显然是更优的选择。

而信号槽的使用,也可以降低双方之间的耦合性,只需要获取到对方的obj(属性或指针),然后只管发送信号,具体实现就不需要管了。

相关推荐
冯诺依曼的锦鲤4 小时前
算法练习:双指针专题
c++·算法
WaWaJie_Ngen4 小时前
【设计模式】工厂模式(Factory)
c++·设计模式·简单工厂模式·工厂方法模式·抽象工厂模式
埃伊蟹黄面4 小时前
深入理解STL关联容器:map/multimap与set/multiset全解析
开发语言·c++
小狮子安度因5 小时前
FFmpeg+QT输出音频
qt·ffmpeg·音视频
「QT(C++)开发工程师」5 小时前
C++语言编程规范-风格
linux·开发语言·c++·qt
Rock_yzh5 小时前
AI学习日记——PyTorch深度学习快速入门:神经网络构建与训练实战
人工智能·pytorch·python·深度学习·神经网络·学习
hello kitty w5 小时前
Python学习(10) ----- Python的继承
开发语言·python·学习
CandyU25 小时前
C++ 学习 —— 02 - 排序算法
c++·学习·排序算法
~无忧花开~5 小时前
CSS学习笔记(二):CSS动画核心属性全解析
开发语言·前端·css·笔记·学习·css3·动画