Linux也有信号的概念,他是系统内部的通知机制,进程间的通信方式。
- 信号源:谁发的信号
- 信号的类型:哪种类别的信号
- 信号的处理方式:注册信号处理函数,在信号被触发的时候自动调用执行
qt虽然和Linux不一样,但也有相似的地方。
- 信号源:由哪个控件发出的信号
- 信号的类型:用户进行不同的操作,就可能触发不同的信号(勾选复选框,选择下拉框)
- 信号的处理方式:槽(slot)=>函数。qt中可以使用connect这样的函数,把一个信号和一个槽关联起来,在触发执行对应的操作。(Linux中,connect是tcp建立通信连接的,连接成功才能通信,这里面是qt里的,只是把信号和函数绑定,只要信号触发了,qt就自动执行槽函数,有点像回调函数,两个并没有什么关系)
connect
connect是QObject提供的静态的成员函数,qt提供的这些类,本身是存在一定的继承关系父类是QWidget,而QWidget又继承于QObject,QObject里面又不止QWidget一个子类。
cpp
connect (const QObject *sender,
const char * signal ,
const QObject * receiver ,
const char * method ,
Qt::ConnectionType type = Qt::AutoConnection )
- sender:信号的发送者;
- signal:发送的信号(信号函数);
- receiver:信号的接收者;
- method:接收信号的槽函数;
- type: 用于指定关联⽅式,默认的关联方式为 Qt::AutoConnection,通常不需要手动设定。
总结:信号又sender发出,发出的信号类型是signal,而信号由receiver接收,最后执行的处理函数是merhod
用法列如:
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(200,200);
connect(button,&QPushButton::clicked,this,&Widget::close);
}
Widget::~Widget()
{
delete ui;
}
注意:connect要求,前两个参数是匹配的,我第一个参数是QPushButton 类型,但QPushButton继承于QWidget,QWidget继承于QObject,所以可以,第二个信号的参数必须是QPushButton内置的信号(父类的信号),不能是其他的类,如QLineEdit的信号,而close是Widget是自己内部有的槽函数,不需要我们自己在定义了,效果就是点击按钮实现窗口关闭。用法好像bind。
clicked和click的区别:clicked是点击完,click传入相当于调用
第二个和第四个参数是char* 但是我们传的是函数指针,这不是不一样的类型吗,是的这个是现在的传参方式,以前的时候得搭配一个给信号传参转成char*的SIGNA宏,给槽传参,搭配一个SLOT宏转成char*,写法就是connect(button,SIGNA(&QPushButton::clicked),this,SLOT(&Widget::close));,但是现在qt5就开始不这样写了,qt5对上述代码进行简化,给connect进行重载,重载版本就可以传入任何类型的参数

自定义槽
自定义槽函数非常关键,开发中大部分情况需要自定义槽函数,槽函数,就是用户触发某个操作后要进行的业务逻辑
手动代码创建,就是定义一个普通的成员函数,在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("按钮");
button->move(100,100);
connect(button,&QPushButton::clicked,this,&Widget::handleClicked);
}
Widget::~Widget()
{
delete ui;
}
void Widget::handleClicked()
{
//按下按钮就修改窗口的标题
this->setWindowTitle("按钮已经按下!");
}

在以前版本得用slots如这样public slots : xxxx 元编程,qmake构建qt项目的时候就是调用扫描器,扫描关键字,基于关键字再生成代码
第二种图形化生成
拖动pushButton按钮,然后右键转到槽,接着选择clicked,然后点击ok

此时就是生成一个函数,编写即可
cpp
void Widget::on_pushButton_clicked()
{
this->setWindowTitle("按钮已经按下!");
}
他在Widget里面有,但是没有调用connect建立关联,他在文件目录的上一级的ui文件下有on_pushButtom_clicked,qt在调用connectSlotsByName函数自动连接,

比较推荐使用图形化界面的方式创建,比较快速,也自动连接,如果通过代码的方式的话,还得手动connect连接(代码里没有connectSlotsByName)
自定义信号
qt中也允许自定义信号,自定义信号比较少见,实际开发中很少需要自定义信号,在GUI中,用户能够进行哪些操作是可以穷举的,qt内置信号中,基本上已经覆盖了上述所有可能的用户操作,因此使用qt内置的信号可以应付大部分场景。
- 信号是一个特殊的函数,我们只需要写出函数声明告诉qt这是一个信号即可,函数的定义是qt在编译过程中自动生成的,程序员无法干预
- 作为信号函数,函数的返回值必须是void
- 有没有参数都可以,甚至可以支持重载
- 声明时,用signals,qt特有的关键字(元编程技术,代码生成代码)
emit XXX 发出XXX信号
手动代码创建


图形化界面创建
.h不变,改cpp

带参信号和槽
信号和槽也可以带参数,当信号带有参数的时候,槽的参数必须和信号的参数一致,此时发射信号的时候,就可以给信号函数传递实参,与之对应的这个阐述就会被传递到对应的槽函数中,就可以达到给让信号给槽传参的效果了
.h


传参可以起到复用代码的效果,有多个逻辑,逻辑上整体一致,但是涉及到的数据不同,就可以通过函数-参数来复用代码,并且在不同的场景中传入不同的参数即可

上述代码两个都是点击事件,触发的都是同一个信号槽,然后在信号槽里发一个自定义信号,而接收到自定义信号后,又去调用自定义槽函数,达到随意点击按钮,切换标题的效果
qt中有很多内置的信号也是带参的,如:

注意了,参数类型必须一致,信号的参数必要要大于等于槽的参数个数,信号槽参数比信号多的话无法编译!这就意味着,一个信号可以绑定一个槽,而一个槽可以对应多个信号,可以应对多个情况,但是个数不一致的话,槽函数只能拿到按顺序的前N个参数
Q_OBJECT
qt中如果要让某个类能够使用槽函数,必须在类最开始的地方写下Q_OBJECT宏,会展开一系列复杂的代码

信号和槽存在的意义
信号槽目的就是解决响应用户的操作

信号槽的断开
使用disconnect来断开信号槽的连接,disconnect使用的方式和connect非常类似,大部分情况下把信号和槽连上了之后,就不必管了,主动断开往往是把信号重新绑定到另一个槽函数上。
单击修改标题窗口修改标题,单击修改窗口2,断开槽连接从新绑定,再次单击修改窗口直接把窗口改成窗口修改标题2,如果没有断开的话,就是两个槽绑定同一个信号,就执行两次
cpp
#include "widget.h"
#include "ui_widget.h"
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("修改窗口标题");
}
void Widget::handleClick2()
{
this->setWindowTitle("修改窗口的标题2");
}
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);
}
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 handleClick();
void handleClick2();
private slots:
void on_pushButton_2_clicked();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
用lambda定义槽函数
