当前位置:网站首页>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();
}
边栏推荐
- 力扣315计算右侧小于当前元素的个数
- 通过Jenkins 拉取服务器代码 权限不足问题及其他注意事项
- C structure use
- 大模型哪家强?OpenBMB发布BMList给你答案!
- 【Base】优化性能到底在优化啥?
- [half understood] zero value copy
- MSP430 开发中遇到的坑(待续)
- BA autoboot plug-in of uniapp application boot
- How to add PDF virtual printer in win11
- Solution to using json.tojsonstring to display question marks in Chinese in Servlet
猜你喜欢

Leetcode 1518. wine change

云原生—运行时环境

Interface control telerik UI for WPF - how to use radspreadsheet to record or comment

SQL most commonly used basic operation syntax

New Oriental's single quarter revenue was 524million US dollars, a year-on-year decrease of 56.8%, and 925 learning centers were reduced

Science heavyweight: AI design protein has made another breakthrough, and it can design specific functional proteins

大模型哪家强?OpenBMB发布BMList给你答案!

Science 重磅:AI设计蛋白质再获突破,可设计特定功能性蛋白质

FutureWarning: Indexing with multiple keys (implicitly converted to a tuple of keys) will be depreca

机器学习基础-支持向量机 SVM-17
随机推荐
A brief introduction to the for loop. Some of the code involves arrays
[graduation design] heart rate detection system based on single chip microcomputer - STM32 embedded Internet of things
Solution to the binary tree problem of niuke.com
LeetCode84 柱状图中最大的矩形
力扣315计算右侧小于当前元素的个数
Design a thread pool
一台电脑上 多个项目公用一个 公私钥对拉取gerrit服务器代码
DART 三维辐射传输模型申请及下载
MMA8452Q几种模式的初始化实例
Fundamentals of machine learning - support vector machine svm-17
Under what circumstances can the company dismiss employees
SQL most commonly used basic operation syntax
Quick read in
Block reversal (summer vacation daily question 7)
C# 泛型是什么、泛型缓存、泛型约束
Multiple items on a computer share a public-private key pair to pull the Gerrit server code
MySQL is always installed unsuccessfully. Just do it like this
LeetCode206 反转链表
Markdown concise grammar manual
Which big model is better? Openbmb releases bmlist to give you the answer!