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(属性或指针),然后只管发送信号,具体实现就不需要管了。

相关推荐
宇宙超级无敌暴龙战士18 小时前
旮旯c语言三个任务
c++·c
养一回月亮!18 小时前
使用Qt实现简单绘图板:鼠标绘制与擦除功能详解
开发语言·qt
BanyeBirth19 小时前
C++差分数组(二维)
开发语言·c++·算法
Fcy64819 小时前
C++ map和multimap的使用
开发语言·c++·stl
CC.GG19 小时前
【C++】STL容器----unordered_map和unordered_set的使用
java·数据库·c++
virtual_k1smet19 小时前
梧桐·鸿鹄- 大数据assistant-level
大数据·笔记
lengjingzju20 小时前
基于IMake的 GCC 编译与链接选项深度解析:构建高效、安全、可调试的现代软件
c++·安全·性能优化·软件构建·开源软件
xu_yule20 小时前
算法基础(数论)—算法基本定理
c++·算法·算数基本定理
CoderCodingNo21 小时前
【GESP】C++五级真题(结构体排序考点) luogu-B3968 [GESP202403 五级] 成绩排序
开发语言·c++·算法
UI设计兰亭妙微21 小时前
医疗设备UI设计核心准则与案例拆解——以临床场景为核心的专业设计逻辑
ui·用户体验设计·移动端界面设计