文章目录
- 信号和槽概念
- connect函数
- 自定义信号和槽
- 自定义槽
- 自定义信号
信号和槽概念
在Linux当中有信号signal,是系统内部的通知机制,也可以认为是进程的通知机制。这里需要注意三要素:
- 信号源:谁发的信号
- 信号的类型:哪种类型的信号
- 信号的处理方式:注册信号处理函数,信号触发的时候,自动调用
Qt里的信号虽然和Linux的信号没有什么联系,但是有些相似之处。
Qt中的信号,也涉及要素:
- 信号源:哪个控件发的信号
- 信号的类型:当前信号是哪种信号,用户进行不同的操作,触发不同的信号
- 信号的处理方式:槽(slot),其实就是一个函数。使用
connect这样的函数,将信号和槽关联起来,后续只要信号触发了,就会执行槽函数。所谓的槽函数,本质上也是一种“回调函数”
Qt中,一定是要先将信号和槽进行关联,然后再触发,这个顺序不能颠倒,否则信号触发的时候,不知道怎么处理了
connect函数
这个
connect函数和Linux TCP socket中建立的函数,没有任何关系,只是名字一样而已
connect是QObject中提供的静态成员函数
Qt中提供的这些类,本身存在一定的继承关系,
QObject是Qt内置类的“祖宗类”,而connect是QObject的静态成员函数,则许多类,都可以直接调用connect
connect函数原型(老版本):
connect(const QObject *sender,
const char *signal,
const QObject *receiver,
const char *method,
Qt::ConnectionType type)
sender:描述当前信号是哪个控件发出的(信号源)signal:发出的哪种信号(信号类型)receiver和method:信号如何处理
receiver描述哪个对象进行处理
method怎么处理(要处理信号的对象提供的成员函数)type:指定关联的方式,默认方式为Qt::AutoConnection,通常不需要手动设定
connect要求信号源和信号类型这两参数类型匹配,比如说信号源类型是QPushButton,那么信号类型必须是QPushButton内置的信号,不能是其他的类
代码示例:
界面包含一个按钮,如果用户点击按钮,则关闭窗口
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(200, 300);
connect(button, &QPushButton::clicked, this, &Widget::close);
}
Widget::~Widget()
{
delete ui;
}
close是QWidget内置槽函数,Widget继承QWidget,也就继承了父亲的槽函数
类似
clicked信号,close槽这些函数,可以查阅文档:
connect第二个和第四个参数是char*类型的,而我们传的是&QPushButton::clicked和&Widget::close函数指针
这两个形参类型是函数指针,而且还都不是
char(*)(),这在C++显然是不行的这其实是老版本的函数声明,以前给信号传参需要搭配
SIGNAL宏,给槽传参数搭配SLOT宏,它们会将传入的函数指针,转成char*connect(button, SIGNAL(&QPushButton::clicked), this, SLOT(&Widget::close));在Qt5中,对上述写法做出简化,不需要写
SIGNAL和SLOT了,给connect提供了重载的版本,重载版本中,第二个和第四个参数成了泛型参数,运行传入任意的函数指针
此时如果传入的第一次参数和第二个参数不匹配,或者第三个和第四个参数不匹配,代码直接编译出错,这样就检查更加严格了
自定义信号和槽
自定义槽
所谓的slot,其实就是一个普通的成员函数;那么所谓的自定义槽函数,其实就是定义一个普通的成员函数

在以前的Qt版本中,槽函数必须放到当中
public/ private/ protected slots:此处的
slot这是Qt拓展出的关键字,不属于C++的标准语法
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(200,200);
//链接信号和槽
connect(button, &QPushButton::clicked, this, &Widget::handleClicked);
}
Widget::~Widget()
{
delete ui;
}
void Widget::handleClicked()
{
//按下按钮, 修改窗口标题
this->setWindowTitle("按钮点击完毕");
}
上面是通过代码方式创建的,还能通过图形化界面的方式创建

创建之后,函数的定义和声明就自动写好了,在定义里面就可以写我们自己的槽函数了

这里有一点,信号和槽并未conncet,widget.h和widget.cpp没有,编译生成之后的ui_widget.h也没有
这是因为Qt当中除了通过connect连接信号槽之外,还可以通过函数名字的方式来自动连接

这些函数名符合上述规则之后,Qt会自动把信号和槽建立连接
编译的时候,会自动调用这个函数,然后触发自动连接规则

如果是代码方式创建控件,建议手动
connect;如果是图形化方式,建议采用自动的
自定义信号
自定义信号比较少见,因为信号对于用户的操作,用户的操作是可以穷举的
Qt内置的信号,基本覆盖了所有可能的用户操作
Qt的信号,本质上也是“函数”,但是信号比较特殊:
- 程序员只需要写出函数声明,告诉Qt这是一个信号即可;这个函数的定义是在编译的时候自动生成的(自动生成的过程,无法干预)
- 作为信号函数,返回值必须是
void,有没有参数都行,也支持重载

在头文件当中使用signals关键字,这个关键字是Qt自己拓展的,qmake的时候,会调用一些代码的分析和生成工具,扫描signals的时候,就会把下面的函数声明认为是信号,并且给函数自动生成定义。
#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("处理自定义信号");
}
运行之后,发现槽函数并未执行到,标题没有改变

这是因为目前只建立了连接,不代表信号发了出来。
如何触发信号?
Qt内置的信号,都不需要手动提供代码触发。用户在GUI进行某些操作的时候,就会触发对应信号(发射信号的代码内置到了Qt框架当中)
Qt提供了emit关键字可以触发信号,这个也是Qt自己拓展的关键字
emit mySignal();

这个可以在任意合适的代码中触发,不一定要在构造函数当中:

Tips:
在Qt5中,
emit其实什么也没做了,真正的操作都包含在mySignal内部生成的函数定义了,就算不写,其实也可以发送,但是在实际当中,还是建议加上,这样可读性更高,表明是自定义发射的信号























