目录
回顾C 语言信号
1、信号与槽
2、关联信号与槽
2.1自动关联信号与槽
2.2手动关联信号与槽
2.3断开信号与槽
3、自定义信号
3.1自定义信号使用条件
3.2自定义槽函数使用条件
4、信号与槽参数传递
4.1自定义一个带参的信号
4.2关联带参的信号与槽
4.3发送一个带参的信号
5、信号与槽的相互关联
6、lambda表达式
lambda 表达式的应用
7、lambda 与 信号和槽
回顾C 语言信号
signal(设置信号处理方式)
功能:注册一个信号处理函数,当收到该信号后,就会触发handler 函数
表头文件 #include<signal.h>
定义函数 void *signal(int signum,void(* handler)(int));
signum : 需要捕捉的信号。
handler : 收到信号后执行的函数
1.捕捉的信号能自定义吗? 不可以,只能使用系统预定义好的信号
2.信号可以传输数据吗? 不可以,信号只负责通知
所以QT 在 C 语言的信号基础进行封装,把上述两个问题都解决了。 在QT 中用户可以自定义信号, 在QT 中用户可以通过信号与槽传递参数。
信号:各种事件
槽: 响应信号的动作
1、信号与槽
当某个事件发生后,如某个按钮被点击了一下,它就会发出一个被点击的信号(signal)。
某个对象接收到这个信号之后,就会做一些相关的处理动作(称为槽slot)。
但是Qt对象不会无故收到某个信号,要想让一个对象收到另一个对象发出的信号,这时候需要建立连接(connect)
//QT的四个信号
- pressed():鼠标按下时触发。对应的函数是 mousePressEvent()。
- clicked():鼠标松开时触发。如果鼠标拖拽到按钮区域之外释放则不会触发。对应的函数是 mouseReleaseEvent()。一般情况下 connect 槽函数时使用该信号。
- released():鼠标松开时触发。即使鼠标拖拽到按钮区域之外释放也会触发。对应的函数是 mouseReleaseEvent()。
- toggled():设置 setCheckable(true) 后再单击按钮才会触发该信号。一般用于多个按钮组成 QButtonGroup 并且 setExclusive(true) 设置按钮间互斥。 正常情况下单击按钮,响应顺序为:pressed() — about 215ms — released() — almost 0ms — clicked()。
2、关联信号与槽
关联的方法有两种:自动关联,手动关联。下面对此进行一次次介绍
2.1自动关联信号与槽
在项目 增加信号和槽的方法一:在UI设计师中增加
第一步:右击控件,在弹出的对话框,
在下拉列表中,选择"转到槽",会弹出选择对话框
第二步:选择自己信号
会自动生成槽函数(.h和.cpp)
第三步:当点击相应的控件时,对应的槽函数就会被调用,从面是实现动态交互的效果
槽函数
2.2手动关联信号与槽
QMetaObject::Connection QObject::connect(
const QObject *sender,
const char *signal,
const QObject *receiver,
const char *method,
Qt::ConnectionType type = Qt::AutoConnection
);
其中,sender 表示信号发送者,signal 表示信号名,receiver 表示信号接收者,method 表示槽函数名,type 表示连接类型
type参数的值 | 描述 | 解释 |
Qt::DirectConnection | 直接连接 | 即在信号发出时直接调用槽函数,槽函数会立即执行,而不管当前的线程是否与信号发出者在同一个线程 |
Qt::QueuedConnection | 排队连接 | 将信号事件放入接收对象的事件队列中,槽函数会在事件循环处理时被执行,适用于跨线程的连接 |
Qt::BlockingQueuedConnection | 阻塞排队连接 | 槽函数会在接收对象的线程中执行,并且当前线程会阻塞,直到槽函数执行完成 |
Qt::AutoConnection | 自动连接 | 如果信号发送者和接收者在同一个线程,使用直接连接,否则使用排队连接 |
Qt::UniqueConnection | 唯一连接 | 已经存在相同的连接,则不会创建新的连接,可以避免重复连接导致的问题,如重复执行槽函数等 |
在QT4和QT5中,手动关联有着意义重大的调整,QT4不会检查信号与槽的参数是否匹配,而QT5会自动检查信号与槽的参数是否匹配。
下面给出QT4和QT5的关联例子:
QT4:
//手动关联信号与槽 this 表示的是当前窗体,MainWindow
connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(on_pushButton_clicked()));
QT5:
connect(ui->pushButton,&QPushButton::clicked,this,&MainWindow::show_msg);
2.3断开信号与槽
语法:
语法: QT4: disconnect(信号发送者地址,SIGNAL(信号名(参数列表)),信号接收者地址,SLOT(槽名称(参数列表)));
QT5: disconnect(信号发送者地址,&发送者类名::信号名,信号接收者地址,&接受者类名::槽名称);
void MainWindow::on_pushButton_2_clicked()
{
//取消信号与槽的关联 QT4
// disconnect(ui->pushButton,SIGNAL(clicked()),this,SLOT(on_pushButton_clicked()));
//取消信号与槽的关联 QT5
disconnect(ui->pushButton,&QPushButton::clicked,this,&MainWindow::on_pushButton_clicked);
//因为自动关联,QT软件采用的是QT4的语法关联,所以取消只能用QT4 。
//关联时使用了那种方法,取消时要一致!!
qDebug() << "取消关联";
}
注意:使用自动关联的时候,QT软件采用的是QT4的语法关联,所以取消关联只能用QT4;使用手动关联的时候,关联时使用了那种方法,取消时要一致
3、自定义信号
在QT中,信号是可以自定义的,语法如下:
//在xxx.h头文件中 声明信号
signals:
void 信号名(参数列表);
注意:信号只需要声明不需要定义!!!
但是可以看到定义信号的时候可以参数,参数的类型在信号定义的时候必须已经确定,而且不能改变,信号默认只能传递基本的类型如下(列举部分):
int:整数类型
double:双精度浮点数类型
QString:字符串类型
QDate:日期类型
QTime:时间类型
QColor:颜色类型
在C++中,参数支持默认参数,那么在QT的信号定义时,当然也可以为参数设置默认值。
定义完自定义信号之后,需要代码来控制发送信号,发送语法如下:
//使用 emit 信号名(参数列表); 发送信号
emit mysig(); //发送一个mysig信号
在发出信号时,如果没有指定参数,则使用默认值。
自定义信号与槽的关联和上面的关联是类似的:
//信号在那个类中定义的,该类就是发送者
connect(this,SIGNAL(mysig()),this,SLOT(getsig()));
//发送信号
emit mysig();
//温馨提示:信号的发送必须在"关联后"发送,否则该信号失效
3.1自定义信号使用条件
- 声明在类的signals域下
- 没有返回值,void类型的函数
- 只有函数声明,没有定义
- 可以有参数,可以重载
- 通过emit关键字来触发信号,形式:emit object->sig(参数);
3.2自定义槽函数使用条件
- qt4 必须声明在 private/public/protected slots域下面,qt5之后可以声明public下,同时还可以是静态的成员函数,全局函数,lambda表达式
- 没有返回值,void类型的函数
- 不仅有声明,还得要有实现
- 可以有参数,可以重载
4、信号与槽参数传递
4.1自定义一个带参的信号
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
//自定义一个带参的信号
signals:
void mysig(int a);
//注意:信号与槽的参数类型必须匹配!!
//声明一个带参的槽函数
public slots:
void getsig(int a);
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
4.2关联带参的信号与槽
#include "mainwindow.h"
#include "ui_mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//关联带参的信号与槽,信号与槽的参数都需要在关联是列举出来
connect(this,SIGNAL(mysig(int)),this,SLOT(getsig(int)));
}
MainWindow::~MainWindow()
{
delete ui;
}
4.3发送一个带参的信号
emit mysig(10010);
注意事项:
- 信号参数的类型必须要与槽函数的类型匹配 参数类型不匹配会发生报错: MainWindow::mysig(QString) --> MainWindow::myslot(int)
- 信号参数的个数必须大于槽函数参数的个数(理解:能少接受发来的信号,不能多接受发来的信号) 槽参数的个数大于信号参数个数 : MainWindow::mysig(int) --> MainWindow::myslot(int,int,int)
5、信号与槽的相互关联
6、lambda表达式
- capture 子句(在 C++ 规范中也称为 Lambda 引导。)
- 参数列表(可选)。 (也称为 Lambda 声明符)
- mutable 规范(可选)。
- 异常规范(可选)。
- 后面的-返回值-类型(可选)。
- Lambda 体。
语法:
auto func = [capture] (params) opt -> ret
{ func_body; };
func是可以当作lambda表达式的名字,作为一个函数使用
capture是捕获列表
params是参数表
opt是函数选项(mutable之类)
ret是返回值类型
func_body是函数体。
capture是捕获列表:
[]不捕获任何变量
[&]引用捕获,捕获外部作用域所有变量,在函数体内当作引用使用,可以修改值
[=]值捕获,捕获外部作用域所有变量,在函数内内有个副本使用 ,不可以修改值
[=, &a]值捕获外部作用域所有变量,按引用捕获a变量
[a]只值捕获a变量,不捕获其它变量
[this]捕获当前类中的this指针
opt选择:
int a = 0;
auto f1 = [=](){ return a; }; // 值捕获a
cout << f1() << endl;
auto f2 = [=]() { return a++; }; // 修改按值捕获的外部变量,error
auto f3 = [=]() mutable { return a++; }; //添加mutable 选项可以修改
lambda 表达式的应用
#include <iostream>
#include <list>
using namespace std;
int main() {
list<int> vec;
vec.push_back(10);
vec.push_back(45);
vec.push_back(4);
vec.push_back(48);
vec.sort();
for(int i:vec)
{
cout << i << endl;
}
//自定义排序的规则
vec.sort([](int a,int b){return a>b;});
for(int i:vec)
{
cout << i << endl;
}
}
基础写法
数据捕获问题
7、lambda 与 信号和槽
QT 中的一些简单功能的槽函数可以直接设计为 lambda 表达式,这样就不用在头文件声明槽,在源文件定义槽。
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
//手动关联信号与槽 , 去头文件声明 myslot ,再定义 myslot 很麻烦
//connect(ui->pushButton,&QPushButton::clicked,this,&MainWindow::myslot);
//把槽函数修改为 lambda表达式
connect(ui->pushButton,&QPushButton::clicked,this,[](){qDebug() << "按钮点击";});
}