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