文章目录
- 信号和槽概念
- 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
内部生成的函数定义了,就算不写,其实也可以发送,但是在实际当中,还是建议加上,这样可读性更高,表明是自定义发射的信号