一、信号和槽机制
所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。
连接函数:connect
参数:
参数1:信号的发送者
参数2:发送的信号(函数地址)
参数3:信号的接收者
参数4:处理的槽函数(函数的地址)
优点:松散耦合
实现点击按钮关闭窗口的案例:
connect(myBtn,&MyPushButton::clicked,this,&myWidget::close); //connect(myBtn,&QPushButton::clicked,this,&QPushButton::close);使用父类
二、自定义信号和槽
自定义信号
写到signals 下返回void
需要声明,不需要实现可以有参数,可以重载
自定义槽函数
返回void
需要声明,也需要实现可以有参数,可以重载
写到public slot下或者public或者全局函数触发自定义的信号
emit自定义信号
案例:下课后,老师触发饿了信号,学生响应信号,请客吃饭
student.h
#ifndef STUDENT_H
#define STUDENT_H
#include <QObject>
class Student : public QObject
{
Q_OBJECT
public:
explicit Student(QObject *parent = nullptr);
//返回值void,需要声明也需要实现
//可以有参数,可以发生重载
void treat();
signals:
};
#endif // STUDENT_H
student.cpp
#include "student.h"
#include<QDebug>
Student::Student(QObject *parent) : QObject(parent)
{
}
void Student::treat()
{
qDebug()<<"请老师吃饭";
}
teacher.h
#ifndef TEACHER_H
#define TEACHER_H
#include <QObject>
class Teacher : public QObject
{
Q_OBJECT
public:
explicit Teacher(QObject *parent = nullptr);
signals:
//自定义信号 写到signals下
//返回值使void,只需要声明,不需要实现
//可以有参数,可以重载
void hungry();
};
#endif // TEACHER_H
teacher.cpp
#include "teacher.h"
Teacher::Teacher(QObject *parent) : QObject(parent)
{
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include"teacher.h"
#include"student.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
Teacher *zt;
Student *st;
void ClassIsOver();
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
//Teacher类 老师类
//Student类 学生类
//下课后,老师触发一个信号,饿了,学生响应信号,请客吃饭
Widget::Widget(QWidget *parent)
: QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//创建一个老师对象
this->zt = new Teacher(this);
//创建一个学生对象
this->st = new Student(this);
//老师饿了 学生请客的连接
connect(zt,&Teacher::hungry,st,&Student::treat);
//调用下课函数
ClassIsOver();
}
void Widget::ClassIsOver()
{
//下课函数,调用后触发老师饿了的信号
emit zt->hungry();
}
Widget::~Widget()
{
delete ui;
}
三、自定义信号和槽发生重载的解决办法
需要利用函数指针,明确指向函数的地址
void(Teacher:: *teacherSignal)(QString foodName) = &Teacher::hungry; void(Student:: *studentSlot)(QString foodName) = &Student::treat;
QString转成char*
.toUtf8()先转为AByteArray
.date再转为char*
student.h
#ifndef STUDENT_H
#define STUDENT_H
#include <QObject>
class Student : public QObject
{
Q_OBJECT
public:
explicit Student(QObject *parent = nullptr);
//返回值void,需要声明也需要实现
//可以有参数,可以发生重载
void treat();
void treat(QString foodName);//重载版本声明
signals:
};
#endif // STUDENT_H
student.cpp
#include "student.h"
#include<QDebug>
Student::Student(QObject *parent) : QObject(parent)
{
}
void Student::treat()
{
qDebug()<<"请老师吃饭";
}
void Student::treat(QString foodName)//重载版本实现
{
//QString->char * 先转成QByteArray(.toUtf8())再转char *(.data)
qDebug()<<"请老师吃饭,老师要吃:"<<foodName.toUtf8().data();
}
teacher.h
#ifndef TEACHER_H
#define TEACHER_H
#include <QObject>
class Teacher : public QObject
{
Q_OBJECT
public:
explicit Teacher(QObject *parent = nullptr);
signals:
//自定义信号 写到signals下
//返回值使void,只需要声明,不需要实现
//可以有参数,可以重载
void hungry();
void hungry(QString);//重载版本
};
#endif // TEACHER_H
teacher.cpp
#include "teacher.h"
Teacher::Teacher(QObject *parent) : QObject(parent)
{
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include"teacher.h"
#include"student.h"
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
private:
Ui::Widget *ui;
Teacher *zt;
Student *st;
void ClassIsOver();
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
//Teacher类 老师类
//Student类 学生类
//下课后,老师触发一个信号,饿了,学生响应信号,请客吃饭
Widget::Widget(QWidget *parent)
: QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//创建一个老师对象
this->zt = new Teacher(this);
//创建一个学生对象
this->st = new Student(this);
// //老师饿了 学生请客的连接
// connect(zt,&Teacher::hungry,st,&Student::treat);
// //调用下课函数
// ClassIsOver();
//连接带参数的信号和槽
//指针->地址
//函数指针->函数地址
void(Teacher:: *teacherSignal)(QString foodName) = &Teacher::hungry;
void(Student:: *studentSlot)(QString foodName) = &Student::treat;
connect(zt,teacherSignal,st,studentSlot);
ClassIsOver();
}
void Widget::ClassIsOver()
{
//下课函数,调用后触发老师饿了的信号
//emit zt->hungry();
emit zt->hungry("宫保鸡丁");//重载版本
}
Widget::~Widget()
{
delete ui;
}
说明:
connect(zt,&Teacher::hungry,st,&Student::treat);
若使用老师饿了的地址&Teacher::hungry和学生请客的地址&Student::treat则不能区分是否带参,故出错(带参和不带参地址一样)
解决方案:
对于函数地址&Teacher::hungry,我们使用函数指针指向函数地址
函数指针定义方式:函数返回值类型(*指针变量名)(函数参数列表);如下:
void( *teacherSignal)(QString foodName) = &Teacher::hungry;
在声明成员函数的函数地址的时候,要把成员函数的作用域放在指针前面,正确写法如下:
void(Teacher:: *teacherSignal)(QString foodName) = &Teacher::hungry;
四、信号连接信号
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
//Teacher类 老师类
//Student类 学生类
//下课后,老师触发一个信号,饿了,学生响应信号,请客吃饭
Widget::Widget(QWidget *parent)
: QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//创建一个老师对象
this->zt = new Teacher(this);
//创建一个学生对象
this->st = new Student(this);
// //老师饿了 学生请客的连接
// connect(zt,&Teacher::hungry,st,&Student::treat);
// //调用下课函数
// ClassIsOver();
//连接带参数的信号和槽
//指针->地址
//函数指针->函数地址
void(Teacher:: *teacherSignal)(QString foodName) = &Teacher::hungry;
void(Student:: *studentSlot)(QString foodName) = &Student::treat;
connect(zt,teacherSignal,st,studentSlot);
// ClassIsOver();
//点击一个下课的按钮,再触发下课
QPushButton *btn = new QPushButton("下课",this);
//重置窗口大小
btn->resize(100,40);
//点击按钮 触发下课
//connect(btn,&QPushButton::clicked,this,&Widget::ClassIsOver);
//无参信号和槽连接
void(Teacher:: *teacherSignal2)(void) = &Teacher::hungry;
void(Student:: *studentSlot2)(void) = &Student::treat;
connect(zt,teacherSignal2,st,studentSlot2);
//信号和信号连接
connect(btn,&QPushButton::clicked,zt,teacherSignal2);
//断开信号
//disconnect(zt,teacherSignal2,st,studentSlot2);
}
void Widget::ClassIsOver()
{
//下课函数,调用后触发老师饿了的信号
//emit zt->hungry();
emit zt->hungry("宫保鸡丁");
}
Widget::~Widget()
{
delete ui;
}
运行:点击按钮下课则输出,但窗口不会关闭。断开信号不注释,点击按钮则不输出
拓展:
1.信号可以连接信号
2.信号可以连接多个槽函数(点击按钮先输出再关闭窗口[连接close即可])3.多个信号可以连接一个槽函数
4.信号和槽函数的参数必须―—对应
5.信号的参数个数可以多于槽的参数的个数
五、lambda表达式
Lambda表达式用于定义并创建匿名的函数对象
[]标识符 匿名函数
=:值传递
&:引用传递参数
()参数
{}实现体
mutatle修饰值传递变量,可以修改拷贝出的数据,改变不了本体返回值()[] ->init {}
labmda表达式最常用 [=](){}
//利用lambda表达式实现点击按钮关闭窗口
QPushButton *btn2 = new QPushButton;
btn2->setText("关闭");
btn2->move(100,0);
btn2->setParent(this);
connect(btn2,&QPushButton::clicked,this,[=](){
this->close();
emit zt->hungry("宫保鸡丁");
});
运行,点击下课输出"请老师吃饭",点击关闭输出"请老师吃饭,老师要吃: 宫保鸡丁"同时关闭窗口
输出如下所示:
作业:
两个按钮实现:
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
#include<QWidget>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton *open_btn = new QPushButton("open",this);
open_btn->resize(80,30);
QPushButton *close_btn = new QPushButton("close",this);
close_btn->resize(80,30);
close_btn->move(0,100);
QWidget *window = new QWidget;
window->setWindowTitle("other_window");
setWindowTitle("work_window");
connect(open_btn,&QPushButton::clicked,window,[=](){
window->show();
});
connect(close_btn,&QPushButton::clicked,window,[=](){
window->close();
});
}
Widget::~Widget()
{
delete ui;
}
一个按钮实现:
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
#include<QWidget>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton *btn = new QPushButton("open",this);
setWindowTitle("work_window");
QWidget *window = new QWidget;
window->setWindowTitle("other_window");
connect(btn,&QPushButton::clicked,window,[=](){
if(btn->text()=="open")
{
btn->setText("close");
window->show();
}
else if(btn->text()=="close")
{
btn->setText("open");
window->close();
}
});
}
Widget::~Widget()
{
delete ui;
}