Qt信号槽机制以及自定义信号槽
1. 信号槽机制
1.1 信号槽简介
信号和槽是用于对象之间的通信的,这是 Qt 的核心。为此 Qt 引入了一些关键字,他们是slots、signals、emit,这些都不是 C++关键字,是 Qt 特有的,这些关键字会被 Qt 的 moc转换为标准的 C++语句。信号和槽其实是观察者模式的一种实现。
1.2 信号槽的白话理解
南昌起义:1927年8月1日,中共联合国民党左派,打响了武装反抗国民党反动派的第一枪,揭开了中国共产党独立领导武装斗争和创建革命军队的序幕。
8月1日2时,在周恩来、贺龙、叶挺、朱德、刘伯承的领导下,南昌起义开始。按照中共前委的作战计划,第20军第1、第2师向旧藩台衙门、大士院街、牛行车站等处守军发起进攻;第11军第24师向松柏巷天主教堂、新营房、百花洲等处守军发起进攻。激战至拂晓,全歼守军3000余人,缴获各种枪5000余支(挺),子弹70余万发,大炮数门。
当日下午,驻马回岭的第25师第73团全部、第75团3个营和第74团机枪连,在聂荣臻、周士第率领下起义,1927年8月2日到达南昌集中。
1927年8月2日,南昌市各界群众数万人集会,庆祝南昌起义的伟大胜利和革命委员会的成立。会后各界青年踊跃参军,仅报名的学生就有数百人。
1.3 信号槽格式
只有 QObject 及其派生类才能使用信号和槽机制,且在类之中还需要使用 Q_OBJECT 宏
1.3.1 信号需符合以下规则
💫 信号使用 signals 关键字声明,信号默认是 public 的;
💫 信号需要使用 emit 关键字发射;信号只需声明,不能对其进行定义,信号是由 moc 自动生成的;
💫 信号的返回值只能是 void 类型的。
1.3.2 声明槽需符合以下规则
💫 声明槽需要使用 slots 关键字,且槽需使用 public、private、protected 访问控制符之一;
💫 槽就是一个普通的函数,可以像使用普通函数一样进行使用,槽与普通函数的主要区别是:槽可以与信号关联。
因为信号位于类之中,因此发射信号的位置需要位于该类的成员函数中或该类能见到信号的标识符的位置。
1.3.3 信号和槽的关系
💫 槽的参数的类型需要与信号参数的类型相对应;
💫 一个信号可以与多个槽关联,多个信号也可以与同一个槽关联,信号也可以关联到另一个信号上;
💫 若一个信号关联到多个槽时,则发射信号时,槽函数按照关联的顺序依次执行;
💫 若信号连接到另一个信号,则当第一个信号发射时,会立即发射第二个信号。
1.4 信号和槽的关联(连接)
💫 C++ 连接信号槽 - Qt4 语法
connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(close()));
💫 C++ 连接信号槽 - Qt5 语法
connect(ui->pushButton, &QPushButton::clicked, this, &MainWindow::close);
💫 C++ 连接信号槽 - Lambda 表达式
connect(ui->pushButton, &QPushButton::clicked, this, [=](){ this->close(); });
2. 自定义信号槽
2.1 简单的自定义信号槽
设计一个老师类、一个学生类
老师饿了(信号)——>学生请客吃饭(槽函数响应)
目录结构:
student.h
#ifndef STUDENT_H
#define STUDENT_H
#include <QObject>
class Student : public QObject
{
Q_OBJECT
public:
explicit Student(QObject *parent = 0);
signals:
public slots:
//早期Qt版本 必须写道public slots下 高版本Qt可以写到public或者全局下
//需要声明 需要实现
//可以有参数,可以发生重载
void treat();
};
#endif // STUDENT_H
student.cpp
#include "student.h"
#include <QDebug>
Student::Student(QObject *parent) : QObject(parent)
{
}
void Student::treat()
{
qDebug()<<"请老师吃饭";
}
teacher.h
#ifndef TEACHER_H
#define TEACHER_H
#include <QObject>
class Teacher : public QObject
{
Q_OBJECT
public:
explicit Teacher(QObject *parent = 0);
signals:
void hungry();//自定义饿了信号
public slots:
};
#endif // TEACHER_H
teacher.cpp
#include "teacher.h"
Teacher::Teacher(QObject *parent) : QObject(parent)
{
}
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();
private:
Ui::Widget *ui;
Teacher *t;
Student *s;
void classisover();
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//创建一个老师对象
t = new Teacher(this);
//创建一个学生对象
s = new Student(this);
//老师饿了,学生请客吃饭连接
connect(t,&Teacher::hungry,s,&Student::treat);
//调用下课函数,发出信号
classisover();
}
Widget::~Widget()
{
delete ui;
}
void Widget::classisover()
{
emit t->hungry();
}
运行结果:
2.2 槽函数重载解决办法
信号增加一个有参数的:
teacher.h
signals:
void hungry();//自定义饿了信号
void hungry(QString foodName);//添加一个菜名
槽函数增加一个有参数的:
student.h
public slots:
//早期Qt版本 必须写道public slots下 高版本Qt可以写到public或者全局下
//需要声明 需要实现
//可以有参数,可以发生重载
void treat();
void treat(QString foodName);
运行报错,应用指针来说明是哪个信号,哪个槽函数
widget.cpp
// //老师饿了,学生请客吃饭连接
// connect(t,&Teacher::hungry,s,&Student::treat);
// //调用下课函数,发出信号
// classisover();
//第一种方法
//连接带参数的信号槽
//函数指针——>函数地址
void(Teacher:: *hungrysignal) (QString) = &Teacher::hungry;
void(Student:: *studentSlot) (QString) = &Student::treat;
connect(t,hungrysignal,s,studentSlot);
classisover();
//第二种方法
connect(t, static_cast<void (Teacher:: *)(QString)>(&Teacher::hungry), s, static_cast<void (Student:: *)(QString)>(&Student::treat));
classisover();
运行结果:
去掉引号:
student.cpp
void Student::treat(QString foodName)
{
qDebug()<<"请老师吃饭,老师要吃:"<<foodName.toUtf8().data();
// qDebug()<<"请老师吃饭,老师要吃:"<<foodName;
}
运行结果:
2.3 信号连接信号案例
widget.h
private:
Ui::Widget *ui;
Teacher *t;
Student *s;
QPushButton *btn;
void classisover();
widget.cpp
btn = new QPushButton("下课", this);
// connect(btn,&QPushButton::clicked,this,&Widget::classisover);
// //老师饿了,学生请客吃饭连接
// connect(t,&Teacher::hungry,s,&Student::treat);
// //调用下课函数,发出信号
// classisover();
//连接带参数的信号槽
//函数指针——>函数地址
// void(Teacher:: *hungrysignal) (QString) = &Teacher::hungry;
// void(Student:: *studentSlot) (QString) = &Student::treat;
// connect(t,hungrysignal,s,studentSlot);
//连接无参数的信号槽
void(Teacher:: *hungrysignal2) (void) = &Teacher::hungry;
void(Student:: *studentSlot2) (void) = &Student::treat;
connect(t,hungrysignal2,s,studentSlot2);
connect(btn,&QPushButton::clicked,t,hungrysignal2);
// classisover();