目录
一、信号与槽机制原理
1.1 概述
在Qt中,用户和控件的每次交互过程称为一个事件。比如"用户关闭窗口"是⼀个事件。每个事件都会发出一个信号,例如用户关闭窗口会发出"窗口被关闭"的信号。
Qt中的所有控件都具有接收信号的能力,一个控件还可以接收多个不同的信号。对于接收到的每 个信号,控件都会做出相应的响应动作。在Qt中,对信号做出的响应动作就称之为槽。
信号和槽是Qt特有的消息传输机制,它能将相互独立的控件关联起来。比如,"按钮"和"窗口" 本身是两个独立的控件,点击"按钮"并不会对"窗口"造成任何影响。通过信号和槽机制,可以将"按 钮"和"窗口"关联起来,实现"点击按钮会使窗口关闭"的效果。
1.2 信号的本质
信号(signal):信号的本质是事件。信号展示方式就是函数。当某一个事件发生后,就发出一个信号。
那么在Qt中信号是通过什么形式呈现给使用者的呢?
- 我们对哪个窗口进行操作,哪个窗口就可以捕捉到这些被触发的事件。
- 对于使用者来说触发了⼀个事件我们就可以得到Qt框架给我们发出的某个特定信号。
- 某个事件产生了,Qt框架就会调用某个对应的信号函数,通知使用者。
在Qt中信号的发出者是某个实例化的类对象。
1.3 槽的本质
槽(slot):对信号相应的函数,槽就是一个函数。槽函数与一般的函数不同的是:槽函数可以与一个信号关联,当信号被发射时,关联的槽函数被自动执行。
1.4 说明
- 信号函数只需要声明,不需要定义(实现),槽函数需要定义(实现)。
- 信号和槽机制底层是通过信号之间相互调用实现的。每一个信号都可以用函数表示,称为信号函数;每个槽也可以用函数表示,称为槽函数。
- 信号函数用signals关键字修饰,槽函数用public slots、protected slots、private slots修饰。signals和slots是Qt在C++的基础上扩展的关键字,专门用来指明信号函数和槽函数。
二、信号和槽的使用
2.1 连接信号和槽
在Qt中,QObject类提供了一个静态成员函数 connect() ,该函数专门用来关联指定的信号函数和槽函数
connect()函数原型:
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,通常不需要手动设定。
信号和槽机制连接方式
- 一个信号可以跟另一个信号相连;
conect(object1, signal1, object2, signal1);
- 同一个信号可以跟多个槽相连;
conect(object1, signal2, object2, slot2);
conect(object1, signal2, object3, slot1);
- 同一个槽可以响应多个信号;
conect(object1, signal2, object2, slot2);
conect(object3, signal2, object2, slot2);
常用连接方案采用:conect(object1, signal, object2, slot);
代码示例
系统自带的信号和槽通常是通过"Qt帮助文档"来查询。
2.2 通过Qt Creator生成信号槽代码
示例:在窗口中设置一个按钮,当点击"按钮"时关闭"窗口"。
1、新建项目,如下图为新建完成之后所包含的所有文件;
2、双击 widget.ui 文件,进入UI设计界面;
3、在 UI 设计窗口中拖入⼀个 "按钮" ,并且修改 "按钮" 的名称及字体大小等;
4、可视化生成槽函数
当单击 "转到槽..." 之后,出现如下界面:对于按钮来说,当点击时发送的信号是:clicked(),所以此处选择:clicked() ;
5、自动生成槽函数原型框架
1)在 "widget.h" 头文件中自动添加槽函数的声明;
说明:
自动生成槽函数的名称有一定的规则。槽函数的命名规则为:on_XXX_SSS,其中:
- 以" on "开头,中间使用下划线连接起来;
- " XXX "表示的是对象名(控件的 objectName 属性)。
- " SSS "表示的是对应的信号。 如:" on_pushButton_clicked() " ,pushButton 代表的是对象名,clicked 是对应的信号。
按照这种命名风格定义的槽函数,就会被Qt自动的和对应的信号进行连接。但是最好还是不要依赖命名规则,而是显式使用 connect 更好
2)在 "widget.cpp" 中自动生成槽函数定义.
6、在槽函数函数定义中添加要实现的功能.。实现关闭窗口的效果.
三、自定义信号和槽
3.1 基本语法
1、自定义信号函数书写规范 (1)自定义信号函数必须写到 "signals" 下;(2)返回值为 void,只需要声明,不需要实现;(3)可以有参数,也可以发生重载;
2、自定义槽函数书写规范 (1)早期的 Qt 版本要求槽函数必须写到 "public slots" 下,但是现在高级版本的 Qt 允许写到类的 "public" 作用域中或者全局下;(2)返回值为 void,需要声明,也需要实现;(3)可以有参数,可以发生重载;
3、发送信号
使用 "emit" 关键字发送信号。"emit" 是⼀个空的宏。"emit" 其实是可选的,没有什么含义,只是为了提醒开发人员。
示例1:
1、在 widget.h 中声明自定义的信号和槽,如图所示
2、在 widget.cpp 中实现槽函数,并且关联信号和槽
注意:图中的 ① 和 ② 的顺序不能颠倒。原因是,首先关联信号和槽,一旦检测到信号发射之后就会立马执行关联的槽函数。反之,若先发射信号,此时还没有关联槽函数,当信号发射之后槽函数不会响应。
示例2:当老师说"上课了",学生们就 "回到座位,开始学习"
1、在源文件中新建两个类,一个是老师类,一个是学生类;首先选中项目名称,鼠标标右键-----> "add new..."
点击 "add new..." 之后,出现如下界面
选择 "choose" 出现如下界面
选择 "下一步",出现如下界面
对于 "学生类" 以上述同样的方式进行添加,添加完成之后,项目目录新增文件如下
在 teacher.h 中声明信号函数
在 student.h 中声明槽函数
在 widget.h 中实例化 "老师类对象" 和 "学生类对象"
在 student.cpp 中实现槽函数
在 widget.cpp 中连接自定义信号和槽;
3.2 带参数的信号和槽
Qt的信号和槽也支持带有参数,同时也可以支持重载。 此处我们要求,信号函数的参数列表要和对应连接的槽函数参数列表一致。此时信号触发,调用到槽函数的时候,信号函数中的实参就能够被传递到槽函数的形参当中。
通过这样的机制,就可以让信号给槽传递数据了。
示例1:重载信号槽
1)在 "widget.h" 头文件中声明重载的信号函数以及重载的槽函数;如下图所示
2)在 "Widget.cpp" 文件实现重载槽函数以及连接信号和槽。
注意:在定义函数指针时要指明函数指针的作用域。
3)执行结果如下图所示
示例2:信号槽参数列表匹配规则
1、在 "widget.h" 头文件中声明信号和槽函数;
2、在 "widget.cpp" 文件中实现槽函数以及连接信号和槽;
其实信号的参数个数可以多于槽函数的参数个数,但是槽的参数个数不能多于信号参数个数. 但是实际开发中最好还是保持参数个数也能匹配一致
示例3:
1、在 "widget.h" 头文件中声明信号和槽函数;
2、在 "widget.cpp" 文件中实现槽函数以及连接信号和槽;
四、信号和槽的其他说明
4.1 Qt4版本信号与槽的连接
Qt4中的 connect 用法和Qt5相比是更复杂的。需要搭配 SIGNAL 和 SLOT 宏来完成。而且缺少必要的函数类型的检查。使代码更容易出错。
示例:
在"widget.h"头文件中声明信号和槽
在"widget.cpp"文件中实现槽函数以及连接信号与槽
Qt4版本信号与槽连接的优缺点:
• 优点:参数直观;
• 缺点:参数类型不做检测;
4.2 信号与槽机制的优劣
优点:松散耦合。一个类若是要支持信号和槽,就必须从QObject或QObect的子类继承。Qt信号和槽机制不支持模板的使用。
缺点:效率较低。通过传递一个信号来调用槽函数会比直接调用非虚函数运行速度慢,主要原因:
- 多线程的时候,信号可能需要排队
- 编组/解组传递的参数
- 安全地遍历所有关联
- 需要定位接收信号的对象