信号与槽的基本概念
信号(signals)
在Qt框架中,用户与控件的每次交互都会触发相应的事件,并产生对应的信号。例如,当用户点击按钮时,按钮会发出"按钮被按下"的信号。
在QT中要注意信号的三个要素:
1.信号源:哪个控件发出的信号。
2.信号类型:不同用户操作会触发不同信号。例如:按钮点击信号、窗口关闭信号。
本质:GUI程序的核心在于实现用户交互,需要准确识别并响应各类用户操作。
3.信号处理的方式:槽(slot)函数,后续只要信号发出了,qt会自动执行槽函数。
槽(slot)
Qt 控件天生具备接收信号的能力,且单个控件可以同时响应多个不同信号。当控件接收到特定信号时,就会执行对应的响应操作,这种响应机制在 Qt 中被称为"槽"。槽函数在做的本质上对未发生的事件写应对方案,事件发生了(信号发出)就执行先前写好的方案(自动调槽函数)。举个实际的例子:设置手机闹钟------你现在定义"早上7点响铃"(写槽函数),当时间到达7点时(信号发出),系统自动执行响铃操作(调用槽函数)。
槽函数本身是一种回调函数------------回调函数是一种编程模式,指将函数A作为参数传递给函数B,由函数B在特定条件满足时调用函数A。这种机制实现了"调用权"的转移。回调函数是编程中常用的机制,例如c++的仿函数,linux中的线程入口函数。
为了对信号和槽有更深刻的理解我们来画图:

connect函数
从上图中我们可以知道,如果不把信号和槽函数关联到一起,自然槽函数是不能在信号发出时被自动调用的。qt提供了connect函数,我们可以通过connect函数将信号和槽关联到一起。
QObject
connect是QObject类提供的静态成员函数。由于Qt中所有类都继承自QObject,且遵循高内聚、低耦合的设计原则,子类可以自然地继承并使用父类的成员函数。QT中的继承关系如图:

connect
选中QObject按F1在文档中查询,我们会在成员函数这一栏看到connect。

cpp
connect(const QObject *sender,//哪个控件发的信号
const char *signal,//信号的类型
const QObject * receiver ,//接收信号的对象,普通槽函数必须写,Lambda表达式不用写
const char *method, //怎么处理信号
Qt::ConnectionType type = Qt::AutoConnection) const//第四个参数后面学到在解释
使用QT创建槽函数
接下来我们来使用connect函数连接信号和槽:
widget.cpp
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(300,200);//设置坐标
connect(button,&QPushButton::clicked,this,&Widget::close);
}
Widget::~Widget()
{
delete ui;
}
其他代码就用QT自动生成的不变。

click
是一个槽函数,用于编程方式触发按钮点击,调用一下相当于点击一下按钮。
clicked
是一个信号,当按钮被实际点击时发出,相当于按钮已经被触发了。
close
close是QWidget内置的槽函数,Widget继承了QWidet的槽函数;调用close可以关闭窗口。
运行
运行之后出现一下界面:

点击button之后,界面销毁。
connect的要求
信号的类型必须是发送信号的控件的。这里的button类型是QPushbutton *那么第二个参数必须是QPushButton内置的信号(或者从父类继承的信号也行)。
QT提供了哪些信号与槽
qt中有非常多的槽函数,我们不可能全部记得,需要时在文档查询即可。

在文档中查询时找不到时,要找的可能在父类中。
close这个槽就在QWidget当中:

不同版本的类型处理
cpp
connect(const QObject *sender,//哪个控件发的信号
const char *signal,//信号的类型
const QObject * receiver ,//接收信号的对象,普通槽函数必须写,Lambda表达式不用写
const char *method, //怎么处理信号
Qt::ConnectionType type = Qt::AutoConnection) const//第四个参数后面学到在解释
cpp
connect(button,&QPushButton::clicked,this,&Widget::close);
仔细观察connect函数的声明我们会发现,我们传入的&QPushButton::clicked和&Widget::close的类型是函数指针,并不是声明中的const char *。
旧版本
cpp
connect(button, SIGNAL(&QPushButton::clicked), this, SLOT(&Widget::close));
这是旧版 Qt 中 connect 函数的声明方式。在早期版本中,参数传递方式与现在有所不同:信号参数需要使用 SIGNAL 宏进行包装,槽函数参数则需要使用 SLOT 宏。此时,传入的函数指针会被转换为 char* 类型。
QT5以后
从 Qt 5 开始,这种写法得到了简化。不再需要使用 SIGNAL 和 SLOT 宏,connect 方法新增了重载版本。在重载版本中,第二个和第四个参数改为泛型参数,允许我们传入任意类型的函数指针。
connect函数具备参数校验功能,当参数不匹配时会触发错误提示。
在Qt5中,connect通过模板类型推导在编译阶段执行以下检查:
-
验证发送者是否为QObject派生类(确保支持信号槽机制);(只有QObject的派生类才支持信号与槽绑定)
-
检查信号函数签名合法性(确认是signals区域声明且参数类型匹配);
-
校验槽函数签名(确保参数类型和数量与信号兼容);
-
自动识别槽函数类型(普通成员函数、lambda表达式或函数指针),并适配相应的调用逻辑。
自定义槽函数
在QT5之前,自定义槽函数需要声明在类的public slots/protected slots/private slots区域中,并且必须配合SLOT宏使用。而QT5版本之后,自定义槽函数与普通成员函数已完全等同。接下来我们来自己定义槽函数;可以通过图形化的方式也可以纯代码。
纯代码
widget.h:
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 handleclicked();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
选中handleclicked然后alt+enter就可以在.cpp文件中添加定义了。
widget.cpp:
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(400,300);
connect(button,&QPushButton::clicked,this,&Widget::handleclicked);
}
Widget::~Widget()
{
delete ui;
}
void Widget::handleclicked()
{
// 按下按钮, 修改窗口标题.
this->setWindowTitle("按钮已经按下!");
}
运行:

点击按钮

图形化
在UI中拖出button,右键button,点击转到槽。这个窗口提供QPushbutton的所有信号,以及QPushbutton父类的信号。

widget.cpp
QT会自动生成槽函数
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_pushButton_clicked()
{
}
cpp
void Widget::on_pushButton_clicked()
{
this->setWindowTitle("按钮按下了!");
}
运行结果:

按下按钮,槽函数自动调用:

我们发现,图形化的方式并没有使用connect函数将信号与槽关联起来,这是为什么呢?QT中不仅可以通过connect关联信号与槽函数,还可以通过函数名的方式自动连接。
注意on_pushButton_clicked()这个生成的函数名on是固定前缀,pushButton是类的名字clicked是信号类型,只要函数名符合规则就能自动关联信号与槽。pushButton和clicked一起就确定了信号,槽函数就是我们正在写的on_pushButton_clicked(),QT会把它们自动关联。