当前位置:网站首页>Qt 信号和槽机制( 详解 )
Qt 信号和槽机制( 详解 )
2022-07-28 11:51:00 【比特冬哥】
一、什么是信号槽?
信号槽是 Qt 框架引以为豪的机制之一。所谓信号槽,实际就是观察者模式。当某个事件发生之后,比如,按钮检测到自己被点击了一下,它就会发出一个信号(signal)。这种发出是没有目的的,类似广播。如果有对象对这个信号感兴趣,它就会使用连接(connect)函数,意思是,将想要处理的信号和自己的一个函数(称为槽(slot))绑定来处理这个信号。也就是说,当信号发出时,被连接的槽函数会自动被回调。这就类似观察者模式:当发生了感兴趣的事件,某一个操作就会被自动触发。
二、信号槽的使用方式
connect()函数最常用的一般形式:
connect(sender, signal, receiver, slot);
参数解释:
sender:发出信号的对象
signal:发送对象发出的信号
receiver:接收信号的对象
slot:接收对象在接收到信号之后所需要调用的函数(槽函数)
系统自带的信号有如下几个
void clicked(bool checked = false)
void pressed()
void released()
void toggled(bool checked )
signals inherited from Qwidget
signals inherited from Q0bject
三、自定义信号和槽
使用connect()可以让我们连接系统提供的信号和槽。但是,Qt 的信号槽机制并不仅仅是使用系统提供的那部分,还会允许我们自己设计自己的信号和槽。
下面看看使用 Qt 的信号槽:
- 首先定义一个学生类和老师类:
老师类中声明信号 饿了 hungry
signals:
void hungury();
- 学生类中声明槽 请客 treat
public slots:
void treat();
- 在窗口中声明一个公共方法下课,这个方法的调用会触发老师饿了这个信号,而响应槽函数学生请客
void MyWidget::ClassIsOver()
{
//发送信号
emit teacher->hungury();
}
- 学生响应了槽函数,并且打印信息
//自定义槽函数 实现
void Student::eat()
{
qDebug() << "该吃饭了!";
}
- 在窗口中连接信号槽
teacher = new Teacher(this);
student = new Student(this);
connect(teacher,&Teacher::hungury,student,&Student::treat);
并且调用下课函数,测试打印出 “该吃饭了”
- 自定义的信号 hungry带参数,需要提供重载的自定义信号和 自定义槽
void hungury(QString name); 自定义信号
void treat(QString name ); 自定义槽
- 但是由于有两个重名的自定义信号和自定义的槽,直接连接会报错,所以需要利用函数指针来指向函数地址, 然后在做连接
void (Teacher:: * teacherSingal)(QString) = &Teacher::hungury;
void (Student:: * studentSlot)(QString) = &Student::treat;
connect(teacher,teacherSingal,student,studentSlot);
自定义信号槽需要注意的事项:
- 发送者和接收者都需要是QObject的子类(当然,槽函数是全局函数、Lambda 表达式等无需接收者的时候除外);
- 信号和槽函数返回值是 void
- 信号只需要声明,不需要实现
- 槽函数需要声明也需要实现
- 槽函数是普通的成员函数,作为成员函数,会受到 public、private、protected 的影响;
- 使用 emit 在恰当的位置发送信号;
- 使用connect()函数连接信号和槽。
- 任何成员函数、static 函数、全局函数和 Lambda 表达式都可以作为槽函数
- 信号槽要求信号和槽的参数一致,所谓一致,是参数类型一致。
- 如果信号和槽的参数不一致,允许的情况是,槽函数的参数可以比信号的少,即便如此,槽函数存在的那些参数的顺序也必须和信号的前面几个一致起来。这是因为,你可以在槽函数中选择忽略信号传来的数据(也就是槽函数的参数比信号的少)。
四、完整例程
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:
//早期Qt版本,必须要写到public slots 下,高级版本可以写到 public或者全局下
//返回值 void,需要声明,也需要实现
//可以有参数,可以发生重载
void treat(); // treat 请客的意思
void treat(QString foodName);
};
#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:
// 自定义信号,写道signals下
// 返回值是void 只需要声明,不需要实现
// 可以有参数,可以重载
void hungry();
void hungry(QString foodName);
public slots:
};
#endif // TEACHER_H
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 = nullptr);
~Widget();
private:
Ui::Widget *ui;
Teacher * zt;
Student * st;
void classIsOver();
};
#endif // WIDGET_H
student.cpp
#include "student.h"
#include<QDebug>
Student::Student(QObject *parent) : QObject(parent)
{
}
void Student::treat()
{
qDebug()<<"请老师吃饭";
}
void Student::treat(QString foodName)
{
//qDebug()<<"请老师吃饭,老师要吃:"<<foodName ; //这行代码输出的是“请老师吃饭,老师要吃:"宫保鸡丁"”
// 即 宫保鸡丁 旁边会有引号显示出来
//若想去掉引号 需要将变量的QString型 转化为 char *型 先转成QByteArray(.toUtf8()) 再转char *(.data())
qDebug()<<"请老师吃饭,老师要吃:"<<foodName.toUtf8().data(); //这样就没有引号了
}
teacher.cpp
#include "teacher.h"
Teacher::Teacher(QObject *parent) : QObject(parent)
{
}
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
#include<QDebug>
//Teacher 类 老师类
//Student 类 学生类
//下课后,老师会触发一个信号,饿了,学生相应信号,请客吃饭
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//创建一个老师对象
this->zt=new Teacher(this);
//创建一个学生对象
this->st=new Student(this);
// //老师饿了 学生请客吃饭的连接
// connect(zt,&Teacher::hungry,st,&Student::treat);
// //先用 connect 将老师和学生信号连接起来,再调用下课函数 使老师发出触发信号
// //调用下课函数
// classIsOver();
//连接带参数的信号和槽
//指针->地址 函数指针->函数地址
//函数指针:函数返回值类型(*指针变量名)(函数参数列表)
void(Teacher::*teacherSignal)(QString)=&Teacher::hungry;
void(Student::*studentSlot)(QString)=&Student::treat;
connect(zt,teacherSignal,st,studentSlot);
// classIsOver(); //此函数使老师发出 饿了信号
//点击一个 下课的按钮,再触发下课
QPushButton * btn1 =new QPushButton("下课",this);
this->resize(600,400); //重置窗口大小
//点击按钮 触发下课
// connect(btn,&QPushButton::clicked,this,&Widget::classIsOver);
/* //无参信号和槽连接 void(Teacher::*teacherSignal2)(void)=&Teacher::hungry; void(Student::*studentSlot2)(void)=&Student::treat; connect(zt,teacherSignal2,st,studentSlot2); //老师发出饿了信号后 学生做出反应 */
//信号连接信号
// connect(btn,&QPushButton::clicked,zt,teacherSignal2); //点击按钮后 老师响应饿了的信号
//断开信号
// disconnect(zt,teacherSignal2,st,studentSlot2); //此行代码可以断开 老师和同学之间的连接信号
//即老师发出饿了信号后,学生不会做出反应
//Lambda表达式
/* QPushButton *btn3=new QPushButton("Over",this); btn3->move(100,0); [btn](){ //[]里面只添加了btn 表示下面{}里只会识别到btn的相关操作 btn->setText("aaaa"); // btn3->setText("bbbb"); //识别不到 }(); //最后面加一个() 表示调用此表达式 */
//mutable关键字 用于修饰值传递的变量,修改的是拷贝,而不是本体
/* QPushButton *mybtn=new QPushButton("one",this); QPushButton *mybtn2=new QPushButton("two",this); mybtn->move(100,0); mybtn2->move(100,100); int m=10; connect(mybtn,&QPushButton::clicked,this,[m]()mutable{m=100+10;qDebug()<<m;}); //点击“one” 输出m=110,即修改的是拷贝值 connect(mybtn2,&QPushButton::clicked,this,[=](){qDebug()<<m;}); //点击“two” 输出m=10,即m的本体没有被修改 qDebug()<<m; */
// int ret=[]()->int{return 1000;}();
// qDebug()<<"ret = "<<ret;
//利用Lambda表达式 实现点击按钮 关闭窗口 最常用的表达式是: [=](){}
QPushButton *btn2=new QPushButton("关闭",this);
btn2->move(100,0);
connect(btn2,&QPushButton::clicked,this,[=](){
// this->close(); //关闭窗口
emit zt->hungry("宫保鸡丁"); //点击“关闭”按钮 触发老师饿了信号
});
}
void Widget::classIsOver()
{
//下课函数,调用后 出发老师饿了的信号
//emit zt->hungry(); //emit 触发自定义信号关键字
emit zt->hungry("宫保鸡丁");
}
Widget::~Widget()
{
delete ui;
}
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
边栏推荐
- Sliding Window
- Merge table rows - three levels of for loop traversal data
- Quick read in
- GMT installation and use
- New progress in the implementation of the industry | the openatom openharmony sub forum of the 2022 open atom global open source summit was successfully held
- 与元素类型 “item” 相关联的 “name” 属性值不能包含'&lt;” 字符解决办法
- MSP430 开发中遇到的坑(待续)
- 机器学习实战-神经网络-21
- 通过Jenkins 拉取服务器代码 权限不足问题及其他注意事项
- Four authentic postures after suffering and trauma, Zizek
猜你喜欢
随机推荐
十三. 实战——常用依赖的作用
STM32F103 几个特殊引脚做普通io使用注意事项以及备份寄存器丢失数据问题1,2
Zurich Federal Institute of technology | reference based image super resolution with deformable attention transformer (eccv2022))
Markdown concise grammar manual
How can non-standard automation equipment enterprises do well in product quality management with the help of ERP system?
[base] what is the optimization of optimization performance?
Leetcode206 reverse linked list
MMA8452Q几种模式的初始化实例
归并排序
Which big model is better? Openbmb releases bmlist to give you the answer!
合并表格行---三层for循环遍历数据
云原生—运行时环境
Aopmai biological has passed the registration: the half year revenue is 147million, and Guoshou Chengda and Dachen are shareholders
试用copilot过程中问题解决
The openatom openharmony sub forum was successfully held, and ecological and industrial development entered a new journey
Design a thread pool
IO流再回顾,深入理解序列化和反序列化
leetcode 1518. 换酒问题
New Oriental's single quarter revenue was 524million US dollars, a year-on-year decrease of 56.8%, and 925 learning centers were reduced
Introduction to border border attribute









