
🔥小叶-duck:个人主页
❄️个人专栏:《Data-Structure-Learning》《C++入门到进阶&自我学习过程记录》
《Linux操作系统从入门到实践》《Qt从入门到实践》
《算法题讲解指南》--优选算法
《算法题讲解指南》--递归、搜索与回溯算法
《算法题讲解指南》--动态规划算法
✨未择之路,不须回头
已择之路,纵是荆棘遍野,亦作花海遨游
目录
[1.1 核心概念](#1.1 核心概念)
[1.1.1 信号的本质](#1.1.1 信号的本质)
[1.1.2 槽的本质](#1.1.2 槽的本质)
[1.2 关键特性](#1.2 关键特性)
[二、connect 基础用法](#二、connect 基础用法)
[2.1 connect 函数原型与参数](#2.1 connect 函数原型与参数)
[2.2 实战案例:点击按钮关闭窗口](#2.2 实战案例:点击按钮关闭窗口)
[2.3 如何查看内置信号与槽的相关信息?](#2.3 如何查看内置信号与槽的相关信息?)
[3.1 基础自定义槽函数(手动实现)](#3.1 基础自定义槽函数(手动实现))
[3.2 可视化关联:Qt Creator 快速生成信号槽](#3.2 可视化关联:Qt Creator 快速生成信号槽)
[4.1 自定义规则](#4.1 自定义规则)
[4.2 自定义信号基础](#4.2 自定义信号基础)
[5.1 基础参数传递](#5.1 基础参数传递)
[5.2 多参数使用规则](#5.2 多参数使用规则)
[5.3 对 Q_OBJECT 作用的认识](#5.3 对 Q_OBJECT 作用的认识)
[6.1 一对一连接](#6.1 一对一连接)
[6.2 一对多连接](#6.2 一对多连接)
[6.3 多对一连接](#6.3 多对一连接)
[6.4 信号和槽断开连接](#6.4 信号和槽断开连接)
[七、Lambda 表达式进阶:简化槽函数](#七、Lambda 表达式进阶:简化槽函数)
[7.1 Lambda 表达式语法](#7.1 Lambda 表达式语法)
[7.2 实战示例:Lambda 作为槽函数](#7.2 实战示例:Lambda 作为槽函数)
[八、Qt4 与 Qt5 信号槽写法对比及常见避坑指南](#八、Qt4 与 Qt5 信号槽写法对比及常见避坑指南)
[8.1 信号槽写法对比](#8.1 信号槽写法对比)
[8.2 常见避坑指南](#8.2 常见避坑指南)
[9.1 信号与槽的优点](#9.1 信号与槽的优点)
[9.2 信号与槽的缺点](#9.2 信号与槽的缺点)
[9.3 信号与槽总结](#9.3 信号与槽总结)
前言
信号与槽 是 Qt 框架最核心、最具特色的通信机制,也是 Qt 界面开发的灵魂所在。无论是点击按钮关闭窗口,还是自定义事件响应,信号与槽都能轻松实现。本系列内容围绕信号和槽展开系统学习,从信号与槽的本质入手,逐步讲解
connect函数的多种用法、自定义信号槽,到Lambda表达式进阶、连接方式拓展,层层递进拆解核心逻辑,搭配实战代码和避坑指南,帮你彻底掌握这一 Qt 核心技术。
一、信号与槽的本质
1.1 核心概念
1.1.1 信号的本质
信号(Signal) 是由于用户对窗口或控件进行了某些操作,导致窗口或控件产生了某个特定事件,这时 Qt 对应的窗口类会发出某个信号,以此对用户的操作做出反应。因此,信号的本质就是事件。如:
- 按钮单击、双击
- 窗口刷新
- 鼠标移动、鼠标按下、鼠标释放
- 键盘输入
那么在 Qt 中信号是通过什么形式呈现给使用者的呢?
- 我们对哪个窗口进行操作,哪个窗口就可以捕捉到这些被触发的事件。
- 对于使用者来说触发了一个事件我们就可以得到 Qt 框架给我们发出的某个特定信号。
- 信号的呈现形式就是函数,也就是说某个事件产生了,Qt 框架就会调用某个对应的信号函数,通知使用者。
在 Qt 中信号的发出者是某个实例化的类对象。
1.1.2 槽的本质
槽(Slot) 就是对信号响应的函数 。槽就是一个函数,与一般的 C++ 函数是一样的,可以定义在类的任何位置(public、protected 或 private),可以具有任何参数,可以被重载,也可以被直接调用(但是不能有默认参数)。槽函数与一般的函数不同的是:槽函数可以与一个信号关联,当信号被发射时,关联的槽函数被自动执行。

1.2 关键特性
- 信号与槽均需依赖 QObject 类(Qt 所有核心类的父类),且类中必须添加
Q_OBJECT宏; - 信号仅需在
signals关键字下声明,无需实现;槽需在public slots/protected slots下声明并实现; - 支持多对多关联(一个信号连多个槽,多个信号连一个槽),灵活度极高。


二、connect 基础用法
Qt 自带大量预定义信号和槽(如按钮的clicked()信号、窗口的close()槽),通过QObject::connect()函数即可关联,无需自定义。
2.1 connect 函数原型与参数
cpp
// 静态函数,用于关联信号和槽
QObject::connect(
const QObject *sender, // 信号发送者(如按钮)
const char *signal, // 发送的信号(如clicked())
const QObject *receiver, // 信号接收者(如窗口)
const char *method, // 响应的槽函数(如close())
Qt::ConnectionType type = Qt::AutoConnection // 连接方式(默认自动)
);

2.2 实战案例:点击按钮关闭窗口
cpp
//widget.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("关闭");
int x = (this->width() - button->width()) / 2;
int y = (this->height() - button->height()) / 2;
button->move(x, y);
connect(button, &QPushButton::clicked, this, &Widget::close);
//close 是 QWidget 内置的槽函数,Widget继承自QWidget,自然也就继承了父亲的槽函数
//具体作用就是关闭当前窗口/控件
}
Widget::~Widget()
{
delete ui;
}

- 核心逻辑 :按钮(sender)的
clicked()信号,关联窗口(receiver)的close()槽; - 运行效果:点击按钮,窗口立即关闭。


2.3 如何查看内置信号与槽的相关信息?
通过 Qt 帮助文档查询(光标选中类名按F1):
- 信号 :查找
Signals关键字(如QPushButton的clicked()、pressed()); - 槽 :查找
Slots关键字(如QWidget的close()、show()); - 若当前类未找到,需查看其父类,若仍没有则继续查找父类的父类以此类推(如
QPushButton的信号在父类QAbstractButton中)。




三、实现自定义槽函数
3.1 基础自定义槽函数(手动实现)
cpp
//widget.h
#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
//widget.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("按钮");
int x = (this->width() - button->width()) / 2;
int y = (this->height() - button->height()) / 2;
button->move(x, y);
connect(button, &QPushButton::clicked, this, &Widget::handleClicked);
}
Widget::~Widget()
{
delete ui;
}
void Widget::handleClicked()
{
//按下按钮,修改窗口的标题
this->setWindowTitle("按钮已经按下");
}


3.2 可视化关联:Qt Creator 快速生成信号槽
除了手动写 connect ,Qt Creator 支持可视化生成信号槽代码,高效便捷:
- 双击
widget.ui进入设计模式,拖拽一个PushButton到界面; - 选中按钮,右键选择 "转到槽...",在弹出的窗口中选择
clicked()信号; - Qt Creator 自动生成槽函数声明(在
widget.h)和实现框架(在widget.cpp) - 在生成的槽函数中添加逻辑(如关闭窗口)

- 命名规则 :自动生成的槽函数名格式为
on_对象名_信号名(如on_pushButton_clicked),Qt 会自动关联,无需手动connect。
cpp
// widget.h中自动生成的槽函数声明
private slots:
void on_pushButton_clicked();
// widget.cpp中自动生成的槽函数定义
void Widget::on_pushButton_clicked()
{
this->setWindowTitle("按钮已经按下");
}

四、构建自定义信号
4.1 自定义规则
(1)信号声明规则
- 必须在 signals 关键字下声明;
- 返回值为 void,仅声明无需实现;
- 支持参数和重载(需注意参数匹配)。
(2)槽声明规则
- 可在 public slots / protected slots / private slots 下声明(Qt5+ 也可以直接放在 public 下)
- 返回值为 void ,需声明且实现;
- 支持参数和重载(需注意参数匹配)。
4.2 自定义信号基础


简单实现一个自定义信号(无参):
cpp
//widget.h
#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();
signals:
void mySignal();
public:
void handleMySignal();
private slots:
void on_pushButton_clicked();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
//widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
connect(this, &Widget::mySignal, this, &Widget::handleMySignal);
}
Widget::~Widget()
{
delete ui;
}
void Widget::handleMySignal()
{
this->setWindowTitle("处理自定义信号");
}
void Widget::on_pushButton_clicked()
{
//mySignal();
//即使不写emit,信号也能发出去
//即使如此,实际开发中,还是建议把emit加上
//加上代码可读性更高,更明显的标识出,这里是发射自定义的信号了.
emit mySignal();
//当点击按钮则会发送自定义的信号
//发送信号的操作,也可以在任意合适的代码中,不一定非要在构造函数中emit
}



五、带参信号槽的实现
5.1 基础参数传递

cpp
//widget.h
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
signals:
void mySignal(const QString& text);
public:
void handleMySignal(const QString& text);
private slots:
void on_pushButton_clicked();
void on_pushButton_2_clicked();
private:
Ui::Widget *ui;
};
//widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
connect(this, &Widget::mySignal, this, &Widget::handleMySignal);
}
Widget::~Widget()
{
delete ui;
}
void Widget::handleMySignal(const QString& text)
{
this->setWindowTitle(text);
}
void Widget::on_pushButton_clicked()
{
//传参可以起到复用代码的效果
//并且可以在不同的场景中传入不同的参数
emit mySignal("把标题设置为标题1");
}
void Widget::on_pushButton_2_clicked()
{
//按下不同的按钮就会调用不同的函数,但发射的信号是同一个只是传入的参数不同
emit mySignal("把标题设置为标题2");
}


5.2 多参数使用规则
- 信号参数个数 ≥ 槽参数个数;
- 信号和槽的参数类型必须一致(如
QString对应QString); - 示例:信号
void func(int, QString)可关联槽void slot(int)或void slot(int, QString),但不能关联void slot(QString, int)(类型顺序不匹配)。


5.3 对 Q_OBJECT 作用的认识

六、信号与槽的连接方式:多场景适配
信号与槽支持多种关联方式,满足不同场景需求,核心分为三类:
6.1 一对一连接
- 场景:一个信号关联一个槽,或一个信号关联另一个信号;
示例:
cpp
// 信号→槽:按钮点击→窗口最小化
connect(btn, &QPushButton::clicked, this, &QWidget::showMinimized);
// 信号→信号:按钮点击→触发自定义信号
connect(btn, &QPushButton::clicked, this, &Widget::mySignal);


6.2 一对多连接
- 场景:一个信号触发多个槽函数(执行顺序与关联顺序一致);
示例:
cpp
// 一个按钮点击,触发三个槽函数
connect(btn, &QPushButton::clicked, this, &Widget::slot1);
connect(btn, &QPushButton::clicked, this, &Widget::slot2);
connect(btn, &QPushButton::clicked, this, &Widget::slot3);

6.3 多对一连接
- 场景:多个信号触发同一个槽函数;
示例:
cpp
// 两个按钮点击,都触发同一个槽函数
connect(btn1, &QPushButton::clicked, this, &Widget::commonSlot);
connect(btn2, &QPushButton::clicked, this, &Widget::commonSlot);

当然,还支持我们前面提到过的多对多连接,这里就不展示了。
6.4 信号和槽断开连接

实战演示:
cpp
//widget.h
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;
int flag = 0;
};
//widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
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("修改窗口的标题1");
qDebug() << "handleClick";
}
void Widget::handleClick2()
{
this->setWindowTitle("修改窗口的标题2");
qDebug() << "handleClick2";
}
void Widget::on_pushButton_2_clicked()
{
if(flag == 0)
{
//1、先断开 pushbutton 原来的信号槽
disconnect(ui->pushButton, &QPushButton::clicked, this, &Widget::handleClick);
//2、再重新绑定新的信号槽
connect(ui->pushButton, &QPushButton::clicked, this, &Widget::handleClick2);
flag = 1;
}
else
{
disconnect(ui->pushButton, &QPushButton::clicked, this, &Widget::handleClick2);
connect(ui->pushButton, &QPushButton::clicked, this, &Widget::handleClick);
flag = 0;
}
}

七、Lambda 表达式进阶:简化槽函数
Qt5 + 支持使用 Lambda 表达式作为槽函数,无需单独声明和实现槽,代码更简洁,尤其适合简单逻辑。
7.1 Lambda 表达式语法
cpp
[捕获列表] (参数列表) 选项 -> 返回值类型 {
函数体;
}
核心部分说明:
-
捕获列表:控制 Lambda 能否访问外部变量(关键!):
[]:不捕获任何外部变量;[=]:值传递捕获所有外部变量(常用,避免悬空引用);[&]:引用传递捕获所有外部变量(慎用,可能出现野指针);[this]:捕获当前类的成员变量和函数;[btn]:值传递捕获指定变量btn;
-
选项 :常用
mutable(允许修改值传递的变量副本); -
返回值类型:可省略,编译器自动推导。
7.2 实战示例:Lambda 作为槽函数
cpp
//widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
#include <QRandomGenerator> //生成一个随机数
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
QPushButton *button = new QPushButton(this);
button->setText("按钮");
int x = 200;
int y = 200;
button->move(x, y);
//connect(button, &QPushButton::clicked, this, [button, this](){
connect(button, &QPushButton::clicked, this, [=]()mutable{
x = QRandomGenerator::global()->bounded(this->width() - button->width());
y = QRandomGenerator::global()->bounded(this->height() - button->height());
qDebug() << "lambda 被执行了!";
button->move(x, y);
//按一下按钮就会随机出现到界面其他位置
});
}
Widget::~Widget()
{
delete ui;
}
- 注意 :Qt5+ 默认支持 Lambda,若用之前的版本编译报错,需要在**.pro** 文件中添加CONFIG += c++11。



八、Qt4 与 Qt5 信号槽写法对比及常见避坑指南
8.1 信号槽写法对比
Qt4 使用 SIGNAL() 和 SLOT() 宏关联,Qt5 推荐使用函数指针,两者差异如下:
| 特性 | Qt4 写法(兼容旧版本) | Qt5 写法(推荐) |
|---|---|---|
| 语法 | connect(btn, SIGNAL(clicked()), this, SLOT(close())) | connect(btn, &QPushButton::clicked, this, &QWidget::close) |
| 类型检查 | 无(编译不报错,运行无效) | 有(编译时检查类型,减少错误) |
| 重载支持 | 不支持(无法区分重载信号 / 槽) | 支持(需用函数指针指定重载版本) |
| 灵活性 | 低 | 高(支持 Lambda、函数指针) |
8.2 常见避坑指南
- 忘记添加
Q_OBJECT宏:导致信号槽无法生效,编译可能报错 "undefined reference to vtable"; - 信号 / 槽参数不匹配:信号参数个数少于槽,或类型不匹配,Qt5 会编译报错,Qt4 无提示但运行无效;
- 关联顺序错误:先发射信号,后关联信号槽,导致信号无法触发槽;
- Lambda 捕获列表不当 :使用
[&]捕获局部变量,变量销毁后 Lambda 未执行,会出现野指针; - 未继承
QObject:信号槽依赖QObject,自定义类必须直接或间接继承。
九、信号与槽的优缺点及总结
9.1 信号与槽的优点
- 松散耦合:信号发送者无需知道接收者,接收者无需知道信号来源,修改一方不影响另一方;
- 灵活多样:支持多对多关联、带参数通信、Lambda 简化写法,适配所有 GUI 场景;
- 易于维护:代码逻辑清晰,信号与槽的关联关系一目了然。
9.2 信号与槽的缺点
- 效率略低:相比回调函数,信号槽存在遍历关联、参数编组等开销(日常场景可忽略);
- 调试难度:多对多关联时,信号触发后多个槽执行,排查问题需明确关联顺序。
9.3 信号与槽总结


结束语
信号与槽是 Qt 开发的核心基石,掌握后可轻松实现控件通信、事件响应等核心功能。本文系统梳理了 Qt 信号与槽的核心原理、各类连接方式及实战技巧,从基础语法到 Lambda 进阶用法,同时对比了 Qt4 与 Qt5 的写法差异,帮大家厘清开发中的常见误区。信号与槽作为 Qt 的核心通信机制,是实现界面交互的关键,熟练掌握后可大幅提升 GUI 开发效率。希望本篇内容能帮助你夯实 Qt 交互开发基础,为后续复杂项目开发做好铺垫。