1.信号与槽
1.1 信号与槽的定义
Qt中,谈到信号,也是涉及到三个要素
信号源:由哪个控件发出的信号
信号的类型:用户进行不同的操作,就可能触发不同的信号(点击按钮,触发点击信号;在输入框中移动光标,触发移动光标的信号;勾选一个复选框;选择一个下拉框 都会触发出不同的信号)。咱们写的GUI程序,就是要和用户进行交互,这个过程中就需要关注,用户当前的操作具体是个什么样的操作
信号的处理方式:槽(slot) =>函数,Qt中可以使用connect这样的函数,把一个信号和一个槽关联起来后续只要信号触发了,Qt就会自动的执行槽函数
所谓的"槽函数"本质上也是一种"回调函数"(callback)。
在Qt中,一般先关联好信号和槽,然后再触发这个信号,顺序不能颠倒,否则信号就不知道如何被处理了。
connect是QObject提供的静态的成员函数,Qt中提供的这些类,本身是存在一定的继承关系的。QObject就是其他Qt内置类的默认父类。
1.2 connect的使用方式

所谓的信号也是Qt中的对象,内部提供的一些成员函数。

代码如下:
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton* button = new QPushButton(this);
button->setText("关闭");
button->move(200,300);
connect(button,&QPushButton::clicked,this,&Widget::close);
}
Widget::~Widget()
{
delete ui;
}

1.3 自定义槽
所谓的自定义一个槽函数,和定义一个普通的成员函数没什么区别。
f1:纯代码方式
cpp
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void handleClicked();
private:
Ui::Widget *ui;
};
cpp
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton* button = new QPushButton(this);
button->setText("按钮");
button->move(100,100);
connect(button,&QPushButton::clicked,this,&Widget::handleClicked);
}
Widget::~Widget()
{
delete ui;
}
void Widget::handleClicked()
{
//按下按钮就修改一下窗口的标题
this->setWindowTitle("按钮已经按下");
}
f2:图形化界面操作



槽函数,就是用户厨房某个操作之后要进行的业务逻辑。
信号就对应到用户的某个操作。
1.4 自定义信号
1、信号,则是一类非常特殊的函数,我们只要写出函数声明,并且告诉Qt,这是一个"信号"即可,至于函数的定义,是Qt在编译过程中,自动生成的.(自动生成的过程,程序员无法干预)。信号在Qt中是特殊的机制.Qt生成的信号是函数的实现,要配合Qt框架做很多既定的操作
2 、作为信号函数,这个函数的返回值,必须是void。该函数有没有参数都可以,也可以支持重载
signals:是Qt自己扩展出来的关键字,qmake的时候,调用一些代码的分析/生成工具,当扫描到类中包含signals这个关键字的时候,就会自动的把下面的函数声明认为是信号,并且给这些信号函数自动的生成函数定义。
信号和槽也可以带参数
当信号带有参数的时候,槽的参数必须和信号的参数一致。此时发射信号的时候,就可以给信号函数传递实参.与之对应的这个参数就会被传递到对应的槽函数中。

信号函数的参数个数,超过了槽函数的参数个数,此时,都是可以正常使用的。
信号函数的参数个数,少于槽函数的参数个数,此时代码无法编译通过 。
一个槽函数,有可能会绑定多个信号。如果我们严格要求参数个数一致,就意味看信号绑定到槽的要求就变高了。换而言之,当下这样的规则,就允许信号和槽之间的绑定更灵活了,更多的信号可以绑定到这个槽函数上了。
槽函数按照参数顺序,拿到信号的前n个参数。

1.5 关于信号和槽的其他知识点
1、使用disconnect来断开信号槽的连接
disconnect使用的方式和connect是非常类似的,disconnect用的比较少的。大部分的情况下,把
信号和槽连上了之后,就不必管了。主动断开往往是把信号重新绑定到另一个槽函数上。
cpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void handleClick();
void handleClick2();
private slots:
void on_pushButton_2_clicked();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
connect(ui->pushButton,&QPushButton::clicked,this,&Widget::handleClick);
}
Widget::~Widget()
{
delete ui;
}
void Widget::handleClick()
{
this->setWindowTitle("修改窗口标题");
}
void Widget::handleClick2()
{
this->setWindowTitle("修改窗口标题2");
}
void Widget::on_pushButton_2_clicked()
{
//1、先断开pushbutton原来的信号槽
disconnect(ui->pushButton,&QPushButton::clicked,this,&Widget::handleClick);
//2、重新绑定信号槽
connect(ui->pushButton,&QPushButton::clicked,this,&Widget::handleClick2);
//如果1步骤注销,也就是没有进行断开连接操作,点击按钮,就会发现信号连接了两个槽函数,即实现了一对多的效果
}
2、定义槽函数的时候,也可以使用lambda表达式,lambda表达式其本质就是一个匿名函数,主要运用在回调函数中,并且是一次性使用。
lambda表达式是一个回调函数,它无法直接获取上层作用域中的变量,lambda为了解决上述问题,引入了"变量捕获"语法。即通过变量捕获,获取到外层作用域中的变量。
=\]是把上层作用域中的所有变量捕获进来。
```cpp
#include "widget.h"
#include "ui_widget.h"
#include