
文章目录
- 一、信号与槽的连接方式
-
- 1.1⼀对⼀
- [1.2 多对⼀](#1.2 多对⼀)
- 二、信号和槽的其他说明
-
- [2.1 信号与槽的断开](#2.1 信号与槽的断开)
- [2.2 Qt4版本信号与槽的连接](#2.2 Qt4版本信号与槽的连接)
- [2.3 使⽤Lambda表达式定义槽函数](#2.3 使⽤Lambda表达式定义槽函数)
- [三、 信号与槽的优缺点](#三、 信号与槽的优缺点)
- 🚩总结
一、信号与槽的连接方式
1.1⼀对⼀
主要有两种形式,分别是:⼀个信号连接⼀个槽和⼀个信号连接⼀个信号。
(1)⼀个信号连接⼀个槽
⽰例
⽰例:
1、在"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 EmitSignal();//信号发射函数
signals:
void MySignal();
public slots:
void MySlot();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H

2、在"widget.cpp"中实现槽函数,信号发射函数以及连接信号和槽;
(2)⼀个信号连接另⼀个信号
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
connect(this, &Widget::MySignal, this, &Widget::MySlot);
ui->setupUi(this);
}
void Widget::MySlot()
{
qDebug() << "好好学习,天天向上";
}
void Widget::EmitSignal()
{
emit MySignal();
}
Widget::~Widget()
{
delete ui;
}

(2)⼀个信号连接另⼀个信号
⽰例:
在上述⽰例的基础上,在"widget.cpp"⽂件中添加如下代码:
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
QPushButton *btn = new QPushButton("按钮", this);
resize(800, 600);
connect(this, &Widget::MySignal, this, &Widget::MySlot);
connect(btn, &QPushButton::clicked, this, &Widget::MySignal);
ui->setupUi(this);
}
void Widget::MySlot()
{
qDebug() << "好好学习,天天向上";
}
void Widget::EmitSignal()
{
emit MySignal();
}
Widget::~Widget()
{
delete ui;
}

4.2 ⼀对多
⼀个信号连接多个槽
⽰例:
(1)在"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 EmitSignal();
signals:
void MySignal();
public slots:
void MySlot1();
void MySlot2();
void MySlot3();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H

( 2)在"widget.cpp"文件中实现槽函数以及连接信号和槽;
cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
connect(this, &Widget::MySignal, this, &Widget::MySlot1);
connect(this, &Widget::MySignal, this, &Widget::MySlot2);
connect(this, &Widget::MySignal, this, &Widget::MySlot3);
ui->setupUi(this);
}
void Widget::MySlot1()
{
qDebug()<< "MySlot_1";
}
void Widget::MySlot2()
{
qDebug()<< "MySlot_2";
}
void Widget::MySlot3()
{
qDebug()<< "MySlot_3";
}
void Widget::EmitSignal()
{
emit MySignal();
}
Widget::~Widget()
{
delete ui;
}

1.2 多对⼀
多个信号连接⼀个槽函数
⽰例:
(1)在"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 EmitSignal();
signals:
void MySignal_1();
void MySignal_2();
public slots:
void MySlot();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H

(2)在"widget.cpp"⽂件中实现槽函数以及连接信号和槽;
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
QPushButton *btn = new QPushButton("按钮", this);
connect(this, &Widget::MySignal_1, this, &Widget::MySlot);
connect(this, &Widget::MySignal_2, this, &Widget::MySlot);
connect(btn, &QPushButton::clicked, this, &Widget::MySlot);
EmitSignal();
ui->setupUi(this);
}
void Widget::MySlot()
{
qDebug()<<"MySlot";
}
void Widget::EmitSignal()
{
emit MySignal_1();
emit MySignal_2();
}
Widget::~Widget()
{
delete ui;
}

二、信号和槽的其他说明
2.1 信号与槽的断开
使⽤disconnect
即可完成断开.
disconnect
的⽤法和connect
基本⼀致.
⽰例:
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
QPushButton* btn = new QPushButton("按钮", this);
btn->move(100, 100);
resize(800, 600);
//信号与槽连接
connect(btn, &QPushButton::clicked, this, &Widget::close);
//断开信号与槽连接
disconnect(btn, &QPushButton::clicked, this, &Widget::close);
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}

2.2 Qt4版本信号与槽的连接
Qt4
中的connect
用法和Qt5相比是更复杂的.需要搭配SIGNAL
和 SLOT
宏来完成.而且缺少必要的函数类型的检查.使代码更容易出错.
⽰例:
(1)在"widget.h"头⽂件中声明信号和槽
cpp
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
// Qt4版本的信号与槽连接
connect(this, SIGNAL(MySignal()), this, SLOT(MySlot()));
EmitSignal();
ui->setupUi(this);
}
void Widget::MySlot()
{
qDebug() << "MySlot()";
}
void Widget::EmitSignal()
{
emit MySignal();
}
Widget::~Widget()
{
delete ui;
}

(2)在"widget.cpp"⽂件中实现槽函数以及连接信号与槽;

Qt4版本信号与槽连接的优缺点:
- 优点:参数直观;
- 缺点:参数类型不做检测;
⽰例:
2.3 使⽤Lambda表达式定义槽函数
Qt5 在Qt4的基础上提⾼了信号与槽的灵活性,允许使⽤任意函数作为槽函数。
但如果想⽅便的编写槽函数,⽐如在编写函数时连函数名都不想定义,则可以通过Lambda
表达式来达到这个⽬的。
Lambda表达式是C++11增加的特性。C++11中的Lambda表达式⽤于定义并创建匿名的函数对象,以简化编程⼯作。
Lambda表达式的语法格式如下:
cpp
[ capture ] ( params ) opt -> ret {
Function body;
};
说明:
英文标识 | 中文说明 |
---|---|
capture | 捕获列表 |
params | 参数表 |
opt | 函数选项 |
ret | 返回值类型 |
Function body | 函数体 |
- 局部变量引⼊⽅式
[]
\]:标识⼀个Lambda表达式的开始。不可省略
|-------------|---------------------------------------|
| []
| 局部变量捕获列表。Lambda表达式不能访问外部函数体的任何局部变量 |
| [a]
| 在函数体内部使用值传递的方式访问a
变量 |
| [&b]
| 在函数体内部使用引用传递的方式访问b
变量 |
| [=]
| 函数外的所有局部变量都通过值传递的方式使用,函数体内使用的是副本 |
| [&]
| 以引用的方式使用Lambda表达式外部的所有变量 |
| [=, &foo]
| foo
使用引用方式,其余是值传递的方式 |
| [&, foo]
| foo
使用值传递方式,其余引用传递 |
| [this]
| 在函数内部可以使用类的成员函数和成员变量,=
和&
形式也都会默认引入 |
说明:
由于使⽤引⽤⽅式捕获对象会有局部变量释放了⽽Lambda
函数还没有被调⽤的情况。如果执⾏Lambda
函数,那么引⽤传递⽅式捕获进来的局部变量的值不可预知。所以绝⼤多数场合使⽤的形式为:[=] () { }
早期版本的Qt,若要使⽤Lambda表达式,要在".pro"⽂件中添加:
cpp
CONFIG += C++11
因为Lambda表达式是C++11标准提出的。Qt5以上的版本⽆需⼿动添加,在新建项⽬时会⾃动添加。
⽰例1:Lambda表达式的使⽤
⽰例2:以[=]⽅式传递,外部的所有变量在Lambda表达式中都可以使⽤
⽰例3:以[a]⽅式传递,在Lambda表达式中只能使⽤传递进来的a
- 函数参数
()
(params) 表⽰Lambda函数对象接收的参数,类似于函数定义中的⼩括号表⽰函数接收的参数类型和个数。参数可以通过按值(如:(inta,intb))和按引⽤(如:(int&a,int&b))两种⽅式进⾏传递。函数参数部分可以省略,省略后相当于⽆参的函数
⽰例:
cpp
auto f = [](int x, int y){return x + y; };
int result = f(2, 3);
qDebug() << "result: " << result;

- 选项
Opt
Opt
部分是可选项,最常⽤的是mutable
声明,这部分可以省略。
Lambda
表达式外部的局部变量通过值传递进来时,其默认是const
,所以不能修改这个局部变量的拷⻉,加上mutable
就可以修改。
QPushButton* Btn1 = new QPushButton("按钮1", this);
QPushButton* Btn2 = new QPushButton("按钮2", this);
int m = 10;
Btn1->move(800, 100);
Btn2->move(800, 100);
//添加mutable, 可以修改局部变量m的拷贝
connect(Btn1, &QPushButton::clicked, this, mmutable{m = 20; qDebug() << "m= " << m; });
connect(Btn1, &QPushButton::clicked, this, ={qDebug() << "m= " << m; });
ui->setupUi(this);

- Lambda表达式的返回值类型
->
可以指定Lambda
表达式返回值类型;如果不指定返回值类型,则编译器会根据代码实现为函数推导⼀个返回类型;如果没有返回值,则可忽略此部分。
⽰例1:
⽰例2:
- Lambda表达式的函数体{ }
Lambda表达式的函数体部分与普通函数体⼀致。⽤{ } 标识函数的实现,不能省略,但函数体可以为空
⽰例:
6、槽函数使⽤Lambda表达式来实现
⽰例1:点击按钮关闭窗⼝;

⽰例2:当"connect"函数第三个参数为"this"时,第四个参数使⽤Lambda表达式时,可以省略掉"this";
三、 信号与槽的优缺点
优点:松散耦合
信号发送者不需要知道发出的信号被哪个对象的槽函数接收,槽函数也不需要知道哪些信号关联了⾃⼰,Qt的信号槽机制保证了信号与槽函数的调⽤。⽀持信号槽机制的类或者⽗类必须继承于QObject
类。
缺点:效率较低
与回调函数相⽐,信号和槽稍微慢⼀些,因为它们提供了更⾼的灵活性,尽管在实际应⽤程序中差别不⼤。通过信号调⽤的槽函数⽐直接调⽤的速度慢约10倍(这是定位信号的接收对象所需的开销;遍历所有关联;编组/解组传递的参数;多线程时,信号可能需要排队),这种调⽤速度对性能要求不是⾮常⾼的场景是可以忽略的,是可以满⾜绝⼤部分场景。
⼀个客⼾端程序中,最慢的环节往往是"⼈"
假设本⾝基于回调的⽅式是10us,使⽤信号槽的⽅式是100us.对于使⽤程序的⼈来说,是感知不到的
🚩总结
