前言
信号与槽函:一对多 多对一 多对多 类似于C++设计模式中的观察者模式
信号与槽函数不是C++标准代码,是QT特有的,最终通过moc(meta -Object-Complier)进行重新编译生成C++标准语法
Q_OBJECT宏
C++可以利用直接调用函数与使用回调函数调用函数来实现一对一的函数实现功能,但是QT再次基础上优化了,变成了效率更高,使用更灵活的信号与槽函数。
再此基础上多了三个C++没有的关键字:emit(发射信号)、slots(槽函数)、signals(信号函数) 。想要使用QT的信号与槽函数的功能就要添加宏定义:Q_OBJECT否则是无法使用的。
目录
一、回调函数与函数指针
首先要复习一下函数指针与回调函数,为什么呢?因为QT的信号与槽的本质就是个回调函数
1.1:函数指针
#include <iostream>
int (*FUNC1)(int,int);
int add(int a,int func ){
int c = a + func;
std::cout << c << std::endl;
return c;
}
//函数指针做形参
void sub(int a,int (*Func)(int b,int c)){
std::cout << a - Func(1,1) << std::endl;
}
int main(int argc, char *argv[])
{
int (*FUNC2)(int,int) = &add;
std::cout << "-------------1:利用函数内声明函数指针调用add函数----------------" << std::endl;
(*FUNC2)(1,2);
FUNC2 = &add;
FUNC2(1,2);
std::cout << "-------------2:利用全局的函数指针调用add函数----------------" << std::endl;
FUNC1 = &add;
FUNC1(3,4);
(*FUNC2)(3,4);
std::cout << "-------------3:调用函数指针作为形参的函数----------------" << std::endl;
sub(5,add);
return 0;
}
1.2:回调函数
由1.1就可以演变出下面代码
#include <iostream>
typedef int (*FUNC1)(int,int);
//库中的另一个函数
int sub(int a,int b){
return a-b;
}
//库的回调函数
int callBack(int a,int b,FUNC1 F){
return F(a,b);
}
//假设下面的函数为我封装起来的库
int add(int a,int b){
return callBack(a,b,sub);
}
int main(){
std::cout << "调用数据:" << add(5,10) << std::endl;
return 0;
}
1.3:为什么用回调函数
其实对与这个问题我最大的感受就是:这个东西让人看起来真的很头大,有的时候代码很混乱,并不是那么的容易读。但是为什么还有这种函数的出现呢,我认为其目的就是在模块化的封装上,也是为了实现很多大牛口中的 解耦
实现高内聚,低耦合
也就是C++中的面向对象的特点之一封装。当我们想要每个模块相互配合相互协作,我们就要利用回调函数来实现,某个函数模块的状态发生改变,我们需要模块改变后及时通知我们的另一模块。这样我们的模块就会减轻耦合性,实现所谓的低耦合。
二、QT4的实现方式
QT4:利用宏 SIGNAL 与 SLOT
优点:信号与槽相对直观
缺点:(不容易查找BUG)使用宏在编译时不会报错,运行阶段会报错。不只是自定义槽函数
connect(ui->pushbutton,SIGNAL(clicked()),this,SLOT(close()));
三、QT5的实现方式
3.1:QT5写法:
优点:完美解决QT4的遗留问题 支持自定义槽函数
connect(ui->pushbutton,&QPushButton::clicked,this,&QMainWindow::close);
3.2:自定义信号与槽
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QTimer *timer = new QTimer(this); // 定时器用于调用信号发送
timer->start(1000); //每一秒发送一次
connect(timer,&QTimer::timeout,this,[=](){
QDateTime dataTime = QDateTime::currentDateTime();
emit sendToTime(dataTime); //发射信号关键字 emit
});
connect(this,&MainWindow::sendToTime,this,&MainWindow::recvTime1);
connect(this,&MainWindow::sendToTime,this,&MainWindow::recvTime2);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::recvTime1(QDateTime dateTime)
{
qDebug() << "recvTime1接受到: " << dateTime << Qt::endl;
}
void MainWindow::recvTime2(QDateTime dateTime)
{
qDebug() << "recvTime2接受到: " << dateTime << Qt::endl;
}
3.3:函数指针方式实现连接
void MainWindow::FuncSlots(){ //函数指针
this->close();
}
void (MainWindow::*Function)() = &MainWindow::FuncSlots;
connect(ui->pushButton,&QPushButton::clicked,this,Function);
3.4:信号发送信号
connect(ui->pushButton,&QPushButton::clicked,this,&MainWindow::send_signal); //接受信号发送信号
四、完整代码与演示
4.1: 代码
.h头文件
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QTimer>
#include <QDateTime>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
signals: //信号
void sendToTime(QDateTime dateTime); //用于发送时间
protected slots: //槽函数
void recvTime1(QDateTime dateTime); //用于接收信号发送过来的时间
void recvTime2(QDateTime dateTime); //用于接收信号发送过来的时间并实现一个信号连接多个槽函数
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
.cpp文件
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
QTimer *timer = new QTimer(this); // 定时器用于调用信号发送
timer->start(1000); //每一秒发送一次
connect(timer,&QTimer::timeout,this,[=](){
QDateTime dataTime = QDateTime::currentDateTime();
emit sendToTime(dataTime); //发射信号关键字 emit
});
QMetaObject::Connection isState = connect(this,&MainWindow::sendToTime,this,&MainWindow::recvTime1);
qDebug()<< "当前信号状态:" << isState << Qt::endl;
connect(this,&MainWindow::sendToTime,this,&MainWindow::recvTime2);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::recvTime1(QDateTime dateTime)
{
qDebug() << "recvTime1接受到: " << dateTime << Qt::endl;
}
void MainWindow::recvTime2(QDateTime dateTime)
{
qDebug() << "recvTime2接受到: " << dateTime << Qt::endl;
}
4.2:实现演示
总结
1、Qt中的信号与槽机制,降低了Qt中对象的耦合度。
2、Qt中的信号可以与多个槽函数进行绑定,但是槽函数的调用顺序没有规律。
3、Qt中的多个信号可以与一个槽函数进行绑定,谁来了都会执行槽函数。
4、Qt中的connect既可以接收信号处理槽函数,又可以接收信号并发送信号。
5、Qt中的connect函数状态判断,true才是可以用的。
6、注意不要乱中函数指针,不然别人可能会读不懂你得代码,无形增加了难度。
如果看不清未来,就把眼前的事做好。