1. 信号和槽概述
- 信号三个要素
- 信号源:由哪个控件发出的信号
- 信号的类型:用户进行不同的操作,就可能触发不同的信号
- 信号的处理方式:槽(slot)=》函数
- Qt中可以使用connect这样的函数,把一个信号和槽关联起来;后续只要信号出发了,Qt就会自动地执行槽信号
- 一定是先把信号的处理方式准备好,再触发信号
2. connect
- 和Linux TCP socket中建立连接的函数,没有任何关系,只是名字恰巧一样,是 QObject 提供的静态的成员函数
cpp
复制代码
connect(const QObject *sender,const char *signal,const QObject *receiver,const char *method,Qt::ConnectionType type = Qt::AutoConnection);
- 参数列表:
- sender:描述了当前信号是哪个控件发出来的
- signal:信号的类型
- receiver,method:信号如何处理
- receiver:哪个对象负责处理
- method:如何处理
- type:暂不考虑,很少使用
- 注意:connect 要求 sender 和 signal 参数是匹配的,sender是QPushButton,那么 signal 必须是 QPushButton 内置的信号(父类的信号)
- 下面实现了一个点击按钮会关闭窗口的功能
cpp
复制代码
QPushButton *button=new QPushButton(this);
button->setText("关闭窗口");
button->move(300,500);
connect(button,&QPushButton::clicked,this,&Widget::close);
//close是QWidget内置的槽函数,Widget继承自QWidget,也就继承了父类的槽函数
为什么要求传的是const char*类型的指针我传其他类型也可以?
- Qt 5 开始,给 connect 提供了重载版本,在该版本中,第二个和第四个参数成了泛型参数,允许传入任意类型函数指针了
3. 自定义槽函数
3.1 第一种
- 自定义槽:所谓的 slot 就是一个普通的成员函数
- 所谓的自定义一个槽函数,操作过程和自定义一个普通的成员函数,没有区别
- slots 是 Qt 自己扩展的关键字,在 Qt 5 之前槽函数要在 public slots: 后面写
- Qt 里广泛使用了 元编程 技术(基于代码,生成代码),qmake 构建 Qt 项目的时候,就回调用专门的扫描器,扫描代码中特定的关键字(slots这种),基于关键字自动生成一大堆代码
cpp
复制代码
//widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QPushButton>
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui {
class Widget;
}
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = nullptr);
~Widget() override;
void handleClicked();
private:
Ui::Widget *ui;
QPushButton* button;
};
#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);
button=new QPushButton(this);
button->setText("按钮");
button->move(600,300);
connect(button,&QPushButton::clicked,this,&Widget::handleClicked);
}
Widget::~Widget()
{
delete ui;
}
void Widget::handleClicked()
{
//按下按钮,修改一下窗口标题
this->setWindowTitle("按钮已经按下");
}
3.2 第二种
- 除了通过 connect 来连接信号槽之外,还可以通过函数名字的方式来自动连接!
- 这个窗口列出了 QPushButton 给我们提供的所有的信号(还包含了 QPushButton 父类的信号)

- 点击要进行的操作后给我们直接生成好了一个函数,在里面直接编写对应代码即可
- 生成的函数命名比较有特点,当函数名符合规则之后,Qt就能自动把信号和槽建立上联系
- on 是固定前缀
- pushButton是按钮的 objectName,clicked 是接收什么信号

- 直接运行

4. 自定义信号
- 用的比较少,Qt 内置的信号就足以应付大部分的开发场景了
- 信号是一类特殊的函数,程序员只要写出函数声明,并且告诉 Qt,这是一个"信号"即可
- 这个函数的定义,是 Qt 在编译过程中,自动生成的
- 作为信号函数,这个函数的返回值,必须是 void
- signals 是 Qt 自己扩展出来的关键字
- Qt在编译的时候,扫描到 signals 这个关键字的时候,此时,就回自动的把下面的函数声明认为是信号,并且给这些写好函数自动的生成函数定义
- emit 发射信号,也是 Qt 自己扩展出来的关键字
- 其实不写emit,信号也能发射出去,不过为了可读性建议加上
- 下方代码流程:
- 点击按钮 =》QPushButton::clicked => Widget::on_pushButton_clicked() => emit mySignal() => void Widget::handleMySignal()
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:
explicit Widget(QWidget *parent = nullptr);
~Widget() override;
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()
{
// 发送出自定义的信号
// 发送信号的操作,也可以在任意合适的代码中,不一定非得在构造函数里
emit mySignal();
}
5. 带参数的信号槽
- 信号和槽也可以带参数,可以只用写参数类型
- 当信号带有参数的时候,槽的参数必须和信号的参数一致
- 此时发射信号时,就可以给信号函数传递实参,与之对应的这个参数就会被传递到对应的槽函数中
- 这里的参数必须一致
- 一致主要是要求类型,个数如果不一致也可以
- 不一致的时候,要求信号的参数必须要比槽的参数个数要更多
- 传参可以起到代码复用的效果,在不同场景中写入不同参数即可
- 一个槽函数可能绑定多个信号
- 如果严格要求参数个数一致,就意味着信号绑定到槽的要求就变高了
- 不严格要求就能允许信号和槽之间的绑定更灵活了,更多的信号可以绑定到这个槽上
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:
explicit Widget(QWidget *parent = nullptr);
~Widget() override;
signals:
void mySignal(const QString &text,const QString &text2);
public:
void handleMySignal(const QString &text);
private slots:
void on_pushButton_clicked();
void on_pushButton_2_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(const QString& text)
{
this->setWindowTitle(text);
}
void Widget::on_pushButton_clicked()
{
// 发送出自定义的信号
// 发送信号的操作,也可以在任意合适的代码中,不一定非得在构造函数里
emit mySignal("把标题设置为标题1","");
}
void Widget::on_pushButton_2_clicked()
{
emit mySignal("把标题设置为标题2","");
}
6. 信号和槽存在的意义
- 设想很好
- 1)解耦合:把触发 用户操作的控件 和 处理对应用户的操作逻辑 解耦合
- 2)"多对多"效果
- 一个信号,可以 connect 到多个槽函数上
- 一个槽函数,也可以被多个信号 connect
- 为了能够让信号和槽之间按照"多对多"的方式来进行关联
7. 信号槽补充
- 使用 disconnect 来断开信号槽的连接
- 使用方式和 connect 差不多
- 大部分情况下,把信号和槽连上之后,就不必管了
- 主动断开往往是把信号重新绑定到另一个槽函数上
cpp
复制代码
// 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("修改窗口标题");
qDebug()<<"handleClick";
}
void Widget::handleClick2()
{
this->setWindowTitle("修改窗口标题2");
qDebug()<<"handleClick2";
}
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);
}
- 使用lambda表达式定义槽函数
- 需要使用变量捕获,永远不要使用[&] 捕获局部变量
cpp
复制代码
QPushButton *button=new QPushButton(this);
button->setText("按钮");
button->move(200,200);
connect(button,&QPushButton::clicked,this,[=](){
qDebug()<<"lambda 被执行了!";
button->move(300,300);
this->move(100,100);
});
8. 总结
- 信号槽是啥 ~~ 尤其是和Linux的信号进行了对比
- 信号槽 使用
- 如何查阅文档
- 一个控件,内置了哪些信号,信号都是何时触发
- 一个控件,内置了哪些槽,槽都是什么作用
- 自定义槽函数
- 本质上就是自定义一个普通的成员函数
- 还可以让 Qt Creatir自动生成,虽然没有显示connect,但是可以通过函数名字特定规则来完成自动连接
- 自定义信号
- 信号本质就是成员函数(函数定义是Qt自己生成的,咱们只需要写函数声明)
- signals:自定义关键字
- emit:完成信号的发射
- 信号和槽还可以带参数
- 发射信号的时候,把参数传递给对应的槽
- 信号的参数和槽的参数要一致
- 信号槽存在的意义
- disconnect使用方式
- lambda 表达式,简化槽函数的定义
小知识
click和clicked
- click 是一个 slot 函数,作用就是在调用的时候相当于点击了一下按钮
- clicked(过去分词形式,点完了),才是要触发的点击信号
- 无参数的,clicked
- 带参数的,clicked(bool),bool表示是否是被勾选的状态