信号和槽机制
信号:
各种事件
槽:
响应信号的动作
当某个事件发生后,如某个按钮被点击了以下,它就会发出一个被点击的信号(signal)
某个对象接收到这个信号之后,就会做一些相关的处理(称为槽Slot)
要想让一个对象接收到另一个对象发出的信号,这时候就需要建立连接(connect)
信号发送者
信号
信号接收者
槽:信号的处理动作
默认情况下,他们四者没有关系,通过connect建立四者的关系
connect(信号发送者,信号,信号接收者,槽)
自定义信号和槽
自定义信号:
1. 函数声明在类头文件的signals域下
2. void类型的函数,没有返回值
3. 可以有参数,也可以重载
4. 只有声明,没有实现
5. 触发信号:emit obj->sign(参数....)
自定义槽
1. 函数声明在类的任何位置,还可以是静态成员函数,全局函数,lambda表达式
2. void类型的函数,没有返回值
3. 可以有参数,也可以重载
4. 不仅有声明,还得有实现
场景一:下课了,老师说他饿了,学生就请吃饭
信号发送者:老师
信号:老师饿了
信号接收者:学生
槽:学生请老师吃饭
创建类:Teacher 和 Student
接着点击下一步,完成就可以创建一个类。
信号:hungry 一个 Teacher
槽:treat 一个 Student
代码:
student.h
#ifndef STUDENT_H
#define STUDENT_H
#include <QObject>
class student : public QObject
{
Q_OBJECT
public:
explicit student(QObject *parent = nullptr);
signals:
public slots:
//自定义槽
//1. 函数声明在类的任何位置,还可以是静态成员函数,全局函数,lambda表达式
//2. void类型的函数,没有返回值
//3. 可以有参数,也可以重载
//4. 不仅有声明,还得有实现
//学生请吃饭
void treat();
};
#endif // STUDENT_H
teacher.h
#ifndef TEACHER_H
#define TEACHER_H
#include <QObject>
class teacher : public QObject
{
Q_OBJECT
public:
explicit teacher(QObject *parent = nullptr);
signals:
//自定义信号
//1. 函数声明在类头文件的signals域下
//2. void类型的函数,没有返回值
//3. 可以有参数,也可以重载
//4. 只有声明,没有实现
//5. 触发信号:emit obj->sign(参数....)
void hungry();
public slots:
};
#endif // TEACHER_H
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "teacher.h"
#include "student.h"
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
void classIsOver();
private:
teacher *pTeacher;
student *pStudent;
};
#endif // WIDGET_H
student.cpp
#include "student.h"
#include <QDebug>
student::student(QObject *parent) : QObject(parent)
{
}
void student::treat()
{
qDebug()<<"Student treat teacher";
}
teacher.cpp
#include "teacher.h"
teacher::teacher(QObject *parent) : QObject(parent)
{
}
widget.cpp
#include "widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
//创建pTeacher和pStudent两个对象,并放到对象树上
pTeacher = new teacher(this);
pStudent = new student(this);
//建立连接
connect(pTeacher,&teacher::hungry,pStudent,&student::treat);
//下课
this->classIsOver();
}
void Widget::classIsOver()
{
//触发老师饿了的信号
emit pTeacher->hungry();
}
Widget::~Widget()
{
}
场景二:下课了,老师说他饿了,要吃黄焖鸡,学生就请吃黄焖鸡
与场景一的唯一区别就是信号与槽是带参函数
在student.h中加入
void treat(QString what);
在teacher.h中加入
void hungry(QString whart);
在student.cpp中加入
void student::treat(QString what)
{
qDebug()<<"student treat teacher eat "<<what<<endl;
}
widget.cpp
#include "widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
//创建pTeacher和pStudent两个对象,并放到对象树上
pTeacher = new teacher(this);
pStudent = new student(this);
//建立连接
//connect(pTeacher,&teacher::hungry,pStudent,&student::treat);
//因为hungry和treat发生了重载,所以在建立连接时信号和槽具有二义性
//解决办法:
//1.使用函数指针赋值,让编译器自动挑选符合类型的函数
void (teacher::*teacher_qString)(QString) = &teacher::hungry;
void (student::*student_qString)(QString) = &student::treat;
connect(pTeacher,teacher_qString,pStudent,student_qString);
//2.使用static_cast 强制转换,让编译器自动挑选复合类型的函数
connect(pTeacher,
static_cast<void (teacher::*)()>(&teacher::hungry),
pStudent,
static_cast<void (student::*)()>(&student::treat));
//下课
this->classIsOver();
}
void Widget::classIsOver()
{
//触发老师饿了的信号
emit pTeacher->hungry();
//请老师吃黄焖鸡
emit pTeacher->hungry("黄焖鸡");
}
Widget::~Widget()
{
}
信号和槽的拓展
- 一个信号可以连接多个槽
一个信号可以建立多个connect,那么当信号发射时,槽函数的调用顺序是随机的 - 一个槽可以连接多个信号
- 信号可以连接信号
connect(第一个信号发送者,第一个信号,第二个信号发送者,第二个信号) - 信号可以断开连接,disconnect
connect参数怎么填,disconnect就怎么填 - 信号和槽的参数关系,必须同时满足以下两点
- 信号和槽函数的参数类型必须对应
- 信号和槽函数的参数个数不需要一致,信号函数参数个数>=槽函数个数
lambda表达式
[capture] (parameters) opt -> retType
{
…;
}
[capture] 捕获外部局部变量的列表。有两种捕获方式,一是值传递,直接填变量名,例如[a,b,c…];另外一种是引用传递,&变量名,例如[&a,&b,&c…]
使用 = ,值传递捕获所有局部变量
使用 & ,引用传递捕获所有局部变量
有特殊的捕获方式的变量在后边另外指定,例如:[&.b…]表示除了b以外,全部的局部变量都采用引用传递;[=,&b…]表示除了b以外,全部的局部变量都采用值传递。
如果是值传递捕获进来的变量默认是const,需要修改的话要使用mutable选项。
返回值可以省略,编译器自动计算。
以后都推荐使用[=] () {} 的形式。