(二)Qt的信号和槽机制
介绍
信号和槽之间的关系就是一个事件发生触发另一个事件的发生。使用这种机制的最好例子就是按钮的点击事件。但按钮发生了点击,可能触发“取消""确认""关闭"等等。
信号:就是相当于”起因",发送出去后,类似于广播,所以我们之后需要定义一个东西将“因"与"果"进行连接在一起;
槽函数:就是相当于”果",当之前绑定信号发出时,被连接的槽函数会自动被回调,做出相应的反应
系统自带的信号和槽函数的例子(按钮点击)
- 定义好按钮
QPushButton * quitBtn = new QPushButton("关闭窗口",this);
- 确定好系统对于按钮的信号
在帮助文档查找信号
到其父类中查找信号,其中包含了我们需要的点击的信号
-
确定好系统对应界面的槽函数
-
建立信号和槽函数之间的绑定关系
绑定信号和槽函数之间关系使用connect函数来实现
其中connect函数包括四个参数:
sender:发出信号的对象(发送者)
signal:发出的具体信号(信号)
receiver:接收信号的对象(接收者)
slot:接收对象在接收到信号之后所需要调用的函数(槽函数)
connect(quitBtn,&QPushButton::clicked,this,&MyWidget::close);
- 运行测试
自定义信号和槽
需求介绍
下课后,老师饿了,学生请老师吃饭。
所以之间的信号和槽函数的绑定是:connect(老师,饿了,学生,请老师吃饭)
无参信号与槽实现流程
- 分别定义老师类和学生类
- 声明和实现分别的信号和槽
首先在老师类中声明信号
定义声明信号的注意事项:
1.信号的返回值是void类型
2.信号只需要声明不需要实现
3.编写在信号一栏
#ifndef TEACHER_H
#define TEACHER_H
#include <QObject>
class Teacher : public QObject
{
Q_OBJECT
public:
explicit Teacher(QObject *parent = nullptr);
signals://编写再此///////////////////////////////////////////////////////////////
void hungry();
public slots:
};
#endif // TEACHER_H
接着在学生类中声明槽函数并在源文件实现
定义槽函数的注意事项:
1.槽函数的返回值是void类型
2.信号需要声明也需要实现
3.编写在类的槽函数一栏
//在学生类的头文件中声明槽函数
void treat();
可以快速在源文件中定义实现函数
//在学生类的源文件中实现槽函数
void Student::treat()
{
qDebug()<<"请老师吃饭";
}
- 在widget.h中引入刚刚定义的两个类,分别声明两个实例和定义一个下课的方法
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <teacher.h>
#include <student.h>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
void classIsOver();//声明下课函数
private:
Ui::Widget *ui;
Teacher * te;//定义实例对象
Student * st;//定义实例对象
};
#endif // WIDGET_H
- 在widget.cpp中分别实例化两个实例和实现一个下课的方法,最重要的便是通过connect函数,实现信号和槽函数的绑定
connect(老师,饿了,学生,请吃饭)
#include "widget.h"
#include "ui_widget.h"
#include <teacher.h>
#include <student.h>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
this->te = new Teacher(this);//实例对象
this->st = new Student(this);//实例对象
connect(te,&Teacher::hungry,st,&Student::treat);//绑定connect
classIsOver();
}
void Widget::classIsOver(){
emit this->te->hungry();//使用emit发送出老师饿了的信号
}
Widget::~Widget()
{
delete ui;
}
- 运行测试
带参信号与槽实现重载
- 教师类添加新的信号
void hungry(QString foodname);
- 学生类添加新的槽函数
void treat(QString foodname);
void Student::treat(QString foodname)
{
qDebug()<<"请老师吃饭,老师想吃"<<foodname.toUtf8().data();//将qstring->char *
}
- 定义函数指针,解决重载问题
//定义函数指针
void(Teacher::* teacherSingle)(QString) = &Teacher::hungry;
void(Student::* studentSingle)(QString) = &Student::treat;
//建立连接
connect(te,teacherSingle,st,studentSingle);
//修改下课函数的触发信号
void Widget::classIsOver(){
emit this->te->hungry("宫保鸡丁");
}
- 运行测试
- 再添加一个下课按钮,点击下课,自动触发
//定义下课按钮
QPushButton * btn = new QPushButton("下课",this);
connect(btn,&QPushButton::clicked,this,&Widget::classIsOver);
- 运行展示
使用信号激活信号,再激活槽函数
- 定义无参函数指针
void(Teacher::* teacherSingle2)(void) = &Teacher::hungry;
void(Student::* studentSingle2)(void) = &Student::treat;
- 绑定按钮点击与老师饿了之间的关系
connect(btn,&QPushButton::clicked,this->te,teacherSingle2);
- 绑定老师饿了与学生请吃饭之间的关系
connect(te,teacherSingle2,st,studentSingle2);
- 运行测试
扩展
- 一个信号可以和多个槽相连
如果是这种情况,这些槽会一个接一个的被调用,但是它们的调用顺序是不确定的。 - 多个信号可以连接到一个槽
只要任意一个信号发出,这个槽就会被调用。 - 一个信号可以连接到另外的一个信号
当第一个信号发出时,第二个信号被发出。除此之外,这种信号-信号的形式和信号-槽的形式没有什么区别。 - 槽可以被取消链接
这种情况并不经常出现,因为当一个对象delete之后,Qt自动取消所有连接到这个对象上面的槽。 - 信号槽可以断开
利用disconnect关键字是可以断开信号槽的 - 槽函数支持使用Lambda 表达式
QPushButton * btn2 = new QPushButton("切换",this);
btn2->move(200,0);
//如果接收对象是this,可以直接省略。
//使用Lambda 表达式,有时候就可以在函数体内直接触发多个信号
connect(btn2,&QPushButton::clicked,[=](){
btn2->setText("success!");
});
- 使用Qt4版本写法
connect(te,SIGNAL(hungry(QString)),st,SLOT(treat(QString)));
看起来更加直观,不安全!