QT 信号和槽 通过自定义信号和槽沟通 如何自定义槽和信号的业务,让它们自动关联 自定义信号功能

2024-06-10 17:28

本文主要是介绍QT 信号和槽 通过自定义信号和槽沟通 如何自定义槽和信号的业务,让它们自动关联 自定义信号功能,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

通过信号和槽机制通信,通信的源头和接收端之间是松耦合的:

  • 源头只需要顾自己发信号就行,不用管谁会接收信号;
  • 接收端只需要关联自己感兴趣的信号,其他的信号都不管;
  • 只要源头发了信号,关联该信号的接收端全都会收到该信号,并执行相应的槽函数。

源头和接收端是非常自由的,connect 函数决定源头和接收端的关联关系,并会自动根据信号里的参数传递给接收端的槽函数。
因为源头是不关心谁接收信号的,所以 connect 函数一般放在接收端类的代码中,或者放在能同时访问源端和接收端对象的代码位置。

编辑好之后保存界面,回到代码编辑模式,打开 widget.h,添加处理按钮 clicked 信号的槽函数,和新的自定义的信号 SendMsg: 

 

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>namespace Ui {
class Widget;
}class Widget : public QWidget
{Q_OBJECTpublic:explicit Widget(QWidget *parent = 0);~Widget();signals:    //添加自定义的信号void SendMsg(QString str);  //信号只需要声明,不要给信号写实体代码public slots:   //接收按钮信号的槽函数void ButtonClicked();private:Ui::Widget *ui;
};#endif // WIDGET_H

signals: 就是信号代码段的标志,这个标志不带 public 、protected、private 等前缀,那是因为信号默认强制规定为公有类型,这样才能保证其他对象能接收到信号。
我们定义了 SendMsg 信号,带一个 QString 参数,这个声明与普通函数声明类似。注意信号只是一个空壳,只需要声明它,而不要给它写实体代码。自定义信号的全部代码就是头文件这里的两行(包括 signals: 行),不需要其他的。signals: 标识的代码段只能放置信号声明,不能放其他任何东西,普通的函数或变量、槽函数都不要放在这里。

public slots: 是公有槽函数代码段的标志,定义了 ButtonClicked 槽函数,接收按钮被点击的信号,这个槽函数以后会触发我们自定义的信号。槽函数代码段也只能放槽函数声明的代码,不要把其他的东西放在这个代码段里。

下面来编写 widget.cpp 里面的代码,实现发送我们自定义信号的槽函数,并和按钮的信号关联起来:

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent) :QWidget(parent),ui(new Ui::Widget)
{ui->setupUi(this);//关联connect(ui->pushButton, SIGNAL(clicked()), this, SLOT(ButtonClicked()));
}Widget::~Widget()
{delete ui;
}
//槽函数
void Widget::ButtonClicked()
{//用 emit 发信号emit SendMsg( tr("This is the message!") );
}

 在 Widget 构造函数里,我们将按钮的 clicked 信号关联到槽函数 ButtonClicked,当按钮被点击时,ButtonClicked 会自动被调用。
ButtonClicked 里面只有一句代码,就是

emit SendMsg( tr("This is the message!") );

emit 是发信号的关键字,然后接下来就与调用函数是一样的格式,SendMsg 里面放置我们想传递的字符串参数。除了 emit 字样,触发信号就与函数调用一样。这样简单一句就实现了触发信号的过程,同之前所说的,源端就顾自己发信号,至于谁接收 SendMsg 信号,源端是不管的。
Widget 窗体代码就是上面那么多,发送我们自定义的 SendMsg 信号的过程如下图所示:

 对于上图左半段,按钮的点击信号和相应的槽函数我们之前两节已经用过多次了,这个不是关注的重点。重点就是在主窗体槽函数里面,我们发出了自定义的信号 SendMsg ,谁来接收它呢?

接下来我们造一个接收 SendMsg 信号的类对象和槽函数,并将收到的字符串参数弹窗显示。我们先为该项目添加一个新的类,并编写接收 SendMsg 信号的槽函数。
打开 QtCreator 菜单“文件”-->“新建文件或项目”,在“新建”对话框里,左边部分选择“C++”,中间部分选 “C++ Class”,如下图所示:

 然后点击右下角 Choose,进入新建 C++ 类的向导界面,将 Class name 修改为 ShowMsg,基类选择 QObject,其他的就用自动填充的,选择基类 QObject 之后,会自动包含相应头文件。要使用信号和槽机制,必须直接或间接从 QObject 类派生,我们这里是直接从 QObject 派生了子类 ShowMsg:

 然后点击“下一步”,进入项目管理界面:

 这里就按照默认的值,不用修改,自动添加到项目 qobjcom.pro 里面,版本控制默认是没有。点击“完成”,稍等一会,QtCreator 就会生成好 ShowMsg 类的两个文件 showmsg.h 和 showmsg.cpp,并添加到项目里。

接下来,我们编辑 showmsg.h ,声明接收 SendMsg 信号的槽函数 RecvMsg:

#ifndef SHOWMSG_H
#define SHOWMSG_H#include <QObject>class ShowMsg : public QObject
{Q_OBJECT
public:explicit ShowMsg(QObject *parent = 0);~ShowMsg();signals:public slots://接收 SendMsg 信号的槽函数void RecvMsg(QString str);
};#endif // SHOWMSG_H

 

RecvMsg 槽函数声明的参数类型和返回类型要与 SendMsg 信号保持一致,所以参数是 QString,返回 void。然后我们编辑 showmsg.cpp,实现 RecvMsg 槽函数:
#include "showmsg.h"
#include <QMessageBox>ShowMsg::ShowMsg(QObject *parent) : QObject(parent)
{}ShowMsg::~ShowMsg()
{}//str 就是从信号里发过来的字符串
void ShowMsg::RecvMsg(QString str)
{QMessageBox::information(NULL, tr("Show"), str);
}

添加头文件 <QMessageBox> 包含之后,我们添加槽函数 RecvMsg 的实体代码,里面就是一句弹窗的代码,显示收到的字符串。QMessageBox::information 函数第一个参数是父窗口指针,设置为 NULL,代表没有父窗口,就是在系统桌面直接弹窗的意思。
信号和槽机制有三步,一是有源头对象发信号,我们完成了;第二步是要有接收对象和槽函数,注意,上面只是类的声明,并没有定义对象。我们必须定义一个接收端的对 象,然后才能进行第三步 connect。

编辑项目里 main.cpp,向其中添加代码,定义接收端对象,然后进行 connect:

#include "widget.h"
#include <QApplication>
#include "showmsg.h"int main(int argc, char *argv[])
{QApplication a(argc, argv);Widget w;   //①主窗体对象,内部会发送 SendMsg 信号ShowMsg s;  //②接收端对象,有槽函数 RecvMsg//③关联,信号里的字符串参数会自动传递给槽函数QObject::connect(&w, SIGNAL(SendMsg(QString)), &s, SLOT(RecvMsg(QString)));//显示主界面w.show();return a.exec();
}

 首先添加 "showmsg.h" 头文件包含,然后在主窗体对象 w 定义之后,定义了接收端对象 s。主窗体对象 w 会发 SendMsg 信号,接收端 s 有对应的槽函数 RecvMsg,这样完成了信号和槽机制的头两步。接下来第三步就是调用关联函数 QObject::connect,将源头对象、信号、接收端对象、槽函数关联。connect 函数是通用基类 QObject 里面定义的,之前用 connect 函数都没有加类前缀,是因为在 QObject 派生类里面自动继承了 connect 函数,不需要额外的前缀。在 main 函数里,需要手动加 QObject:: 前缀来调用 connect 函数。
关联完成之后,一旦用户点击主窗体里的按钮,我们自定义的 SendMsg 信号就会发出去,然后 接收端对象 s 里的槽函数就会执行,并且信号里的字符串也会自动传递给 RecvMsg 槽函数,然后会出现弹窗显示传递的字符串。
这个示例的运行效果如下图所示:

 

本小节需要大家学习的就是右半段的部分,我们在主窗体 ButtonClicked 函数里触发自定义的信号 SendMsg,然后通过 connect 函数关联,自动调用了接收端对象 s 的槽函数 RecvMsg,并弹窗显示了传递的字符串。

也许有读者会问,费这么大劲,为什么不直接在 ButtonClicked 里面弹窗?那不简单多了?
因为本小节的目的不是弹窗,而是为了展现自定义信号和槽函数的代码写法,理解信号和槽机制的运行流程。以后遇到复杂多窗口的界面程序,在多个窗体对象之间就可以用 上图示范的流程,来进行通信、传递数据。 

这篇关于QT 信号和槽 通过自定义信号和槽沟通 如何自定义槽和信号的业务,让它们自动关联 自定义信号功能的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1048803

相关文章

Android实现图片浏览功能的示例详解(附带源码)

《Android实现图片浏览功能的示例详解(附带源码)》在许多应用中,都需要展示图片并支持用户进行浏览,本文主要为大家介绍了如何通过Android实现图片浏览功能,感兴趣的小伙伴可以跟随小编一起学习一... 目录一、项目背景详细介绍二、项目需求详细介绍三、相关技术详细介绍四、实现思路详细介绍五、完整实现代码

SpringBoot AspectJ切面配合自定义注解实现权限校验的示例详解

《SpringBootAspectJ切面配合自定义注解实现权限校验的示例详解》本文章介绍了如何通过创建自定义的权限校验注解,配合AspectJ切面拦截注解实现权限校验,本文结合实例代码给大家介绍的非... 目录1. 创建权限校验注解2. 创建ASPectJ切面拦截注解校验权限3. 用法示例A. 参考文章本文

Python使用FastAPI实现大文件分片上传与断点续传功能

《Python使用FastAPI实现大文件分片上传与断点续传功能》大文件直传常遇到超时、网络抖动失败、失败后只能重传的问题,分片上传+断点续传可以把大文件拆成若干小块逐个上传,并在中断后从已完成分片继... 目录一、接口设计二、服务端实现(FastAPI)2.1 运行环境2.2 目录结构建议2.3 serv

MyBatis Plus实现时间字段自动填充的完整方案

《MyBatisPlus实现时间字段自动填充的完整方案》在日常开发中,我们经常需要记录数据的创建时间和更新时间,传统的做法是在每次插入或更新操作时手动设置这些时间字段,这种方式不仅繁琐,还容易遗漏,... 目录前言解决目标技术栈实现步骤1. 实体类注解配置2. 创建元数据处理器3. 服务层代码优化填充机制详

Vite 打包目录结构自定义配置小结

《Vite打包目录结构自定义配置小结》在Vite工程开发中,默认打包后的dist目录资源常集中在asset目录下,不利于资源管理,本文基于Rollup配置原理,本文就来介绍一下通过Vite配置自定义... 目录一、实现原理二、具体配置步骤1. 基础配置文件2. 配置说明(1)js 资源分离(2)非 JS 资

QT Creator配置Kit的实现示例

《QTCreator配置Kit的实现示例》本文主要介绍了使用Qt5.12.12与VS2022时,因MSVC编译器版本不匹配及WindowsSDK缺失导致配置错误的问题解决,感兴趣的可以了解一下... 目录0、背景:qt5.12.12+vs2022一、症状:二、原因:(可以跳过,直奔后面的解决方法)三、解决方

深入浅出Spring中的@Autowired自动注入的工作原理及实践应用

《深入浅出Spring中的@Autowired自动注入的工作原理及实践应用》在Spring框架的学习旅程中,@Autowired无疑是一个高频出现却又让初学者头疼的注解,它看似简单,却蕴含着Sprin... 目录深入浅出Spring中的@Autowired:自动注入的奥秘什么是依赖注入?@Autowired

Debian 13升级后网络转发等功能异常怎么办? 并非错误而是管理机制变更

《Debian13升级后网络转发等功能异常怎么办?并非错误而是管理机制变更》很多朋友反馈,更新到Debian13后网络转发等功能异常,这并非BUG而是Debian13Trixie调整... 日前 Debian 13 Trixie 发布后已经有众多网友升级到新版本,只不过升级后发现某些功能存在异常,例如网络转

基于Redis自动过期的流处理暂停机制

《基于Redis自动过期的流处理暂停机制》基于Redis自动过期的流处理暂停机制是一种高效、可靠且易于实现的解决方案,防止延时过大的数据影响实时处理自动恢复处理,以避免积压的数据影响实时性,下面就来详... 目录核心思路代码实现1. 初始化Redis连接和键前缀2. 接收数据时检查暂停状态3. 检测到延时过

基于Java和FFmpeg实现视频压缩和剪辑功能

《基于Java和FFmpeg实现视频压缩和剪辑功能》在视频处理开发中,压缩和剪辑是常见的需求,本文将介绍如何使用Java结合FFmpeg实现视频压缩和剪辑功能,同时去除数据库操作,仅专注于视频处理,需... 目录引言1. 环境准备1.1 项目依赖1.2 安装 FFmpeg2. 视频压缩功能实现2.1 主要功