Qt 开发:深入详解 Qt 的信号与槽机制——彻底搞懂QT信号与槽
一、概念
Qt 的信号与槽(Signals and Slots)机制是一个用于对象间通信的核心特性。这个机制使得对象能以松散耦合的方式进行通信,从而提升了代码的模块化和可维护性。
- 信号(Signal):对象状态的变化或事件发生时发出的通知。
- 槽(Slot):信号发出时调用的函数,用于处理信号。
- 连接(Connect):将信号和槽关联起来,使得信号发出时自动调用对应的槽。
二、原理机制
信号与槽机制依赖于 Qt 的元对象系统(Meta-Object System),该系统通过 Q_OBJECT
宏和 MOC(Meta-Object Compiler)生成额外的代码来支持信号与槽功能。
三、工作流程
- 信号声明:在类中使用
signals
关键字声明信号。 - 槽声明和定义:在类中用
public slots
、private slots
声明槽,并实现槽函数。 - 连接信号和槽:使用
QObject::connect()
函数将信号和槽连接起来。 - 发射信号:在适当时机调用信号,触发相应的槽函数。
四、实际项目示例
我们将通过一个简单的 Qt 应用程序来演示信号与槽机制。这个程序包含一个按钮和一个标签,当点击按钮时,标签显示的文字会改变。
步骤 1:创建 Qt 项目
- 打开 Qt Creator,选择 "File" -> "New File or Project"。
- 选择 "Application" -> "Qt Widgets Application"。
- 输入项目名称(例如 "SignalSlotExample")。
- 选择项目路径并点击 "Next"。
- 选择适合的平台和 Qt 版本,点击 "Next"。
- 点击 "Finish" 创建项目。
步骤 2:设计用户界面
- 在项目树中,打开
mainwindow.ui
文件。 - 拖动一个
QPushButton
和一个QLabel
到窗口中。 - 设置按钮和标签的文本为 "Click Me!" 和 "Hello, Qt!"。
步骤 3:添加按钮功能
1. 使用“转到槽”功能
“转到槽”功能允许你快速为UI控件添加槽函数,并自动生成必要的代码。
步骤
- 打开 UI 设计器:在项目树中,双击打开
mainwindow.ui
文件。 - 选择控件:在 UI 设计器中,选择想要添加槽函数的控件,比如
QPushButton
。 - 右键菜单:右键点击选择的控件,并在弹出的右键菜单中选择“转到槽...”(Go to Slot...)。
- 选择信号:在弹出的对话框中,选择你希望处理的信号,例如
clicked()
,然后点击“确定”或“添加”(Add)。
Qt Creator 将自动生成槽函数的声明和实现代码,并将你带到实现代码的位置。
执行上述步骤后,mainwindow.h
文件将自动添加新槽函数的声明,如下:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:void on_pushButton_clicked();private:Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
对应的槽函数实现也会自动添加到 mainwindow.cpp
文件中,修改 mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_pushButton_clicked()
{// 修改标签的文本ui->label->setText("Button Clicked!");
}
2. 自定义“添加信号”到槽功能
虽然“自定义添加信号”功能没有像“转到槽”那样的快捷选项,但你可以手动添加信号声明,并在代码中进行连接。
手动添加信号
- 修改
mainwindow.h
文件:在类中添加新的信号声明。#ifndef MAINWINDOW_H #define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACEclass MainWindow : public QMainWindow {Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();signals:void customSignal(); // 新的信号声明private slots:void on_pushButton_clicked();private:Ui::MainWindow *ui; };#endif // MAINWINDOW_H
- 发射信号:在
mainwindow.cpp
文件中,发射自定义信号。我们可以在按钮点击槽函数中发射这个信号:#include "mainwindow.h" #include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow) {ui->setupUi(this);// 连接自定义信号到某个槽connect(this, &MainWindow::customSignal, this, &MainWindow::handleCustomSignal); }MainWindow::~MainWindow() {delete ui; }void MainWindow::on_pushButton_clicked() {// 修改标签的文本ui->label->setText("Button Clicked!");// 发射自定义信号emit customSignal(); }void MainWindow::handleCustomSignal() {// 处理自定义信号ui->label->setText("Custom Signal Emitted!"); }
自定义添加信号到槽关键步骤:
自定义槽
handleCustomSignal
,并在mainwindow.cpp
中实现:1、声明信号:
在
mainwindow.h
中声明新的信号signals:void customSignal(); // 新的信号声明
2、声明槽:
在
mainwindow.h
中声明新的槽private slots:void on_pushButton_clicked();void handleCustomSignal(); // 新的槽声明
3、处理自定义信号(槽的实现)
void MainWindow::handleCustomSignal() {// 处理自定义信号ui->label->setText("Custom Signal Emitted!"); }
4、连接自定义信号到指定槽:
// 连接自定义信号到指定槽connect(this, &MainWindow::customSignal, this, &MainWindow::handleCustomSignal);
5、发射信号
// 发射自定义信号emit customSignal();
步骤 4:运行程序
- 点击 Qt Creator 工具栏上的 "Run" 按钮。
- 在弹出的窗口中,点击按钮,观察标签的文本变化。
详细解释
- 信号声明:在 Qt 中,信号是通过在类中使用
signals
关键字声明的。在这个例子中,QPushButton
已经有了一个名为clicked()
的信号。 - 槽声明和定义:槽是一个普通的成员函数,可以在类中用
public slots
、private slots
声明。在这个例子中,我们在MainWindow
类中声明并定义了一个槽handleButtonClick()
。 - 连接信号和槽:使用
QObject::connect()
函数将信号和槽连接起来。参数包括发出信号的对象、信号名称、接收信号的对象和槽名称。 - 发射信号:当按钮被点击时,
QPushButton
内部自动发射clicked()
信号,触发handleButtonClick()
槽。
五、常见问题和调试
- 未使用
Q_OBJECT
宏:信号与槽机制依赖于 Qt 的元对象系统,必须在类中加入Q_OBJECT
宏。 - 信号未正确连接:确保
connect()
函数的参数类型匹配。 - 对象未正确管理:确保信号发出者和槽接收者对象的生命周期被正确管理,避免提前销毁。
通过以上详细步骤和解释,我们实现了一个简单的 Qt 应用程序,并深入理解了信号与槽机制的工作原理和应用方式。希望这能帮助你更好地掌握 Qt 开发。
六、带参数的信号和槽
上面的实列中信号和槽是没有带参数传递的,那么信号和槽可以带参数传递吗?那是肯定的,在 Qt 的信号与槽机制中,信号和槽可以带参数。这种功能非常强大,可以让你在发射信号时传递数据到槽函数中进行处理。接下来,我们将演示如何为信号和槽添加参数。
修改示例带参数
假设我们要修改前面的示例,使得自定义信号 customSignal
可以传递一个字符串参数,并在槽函数中处理这个参数。
修改 mainwindow.h
首先,我们需要修改信号和槽的声明,使其带有参数:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();signals:void customSignal(const QString &message); // 带参数的信号声明private slots:void on_pushButton_clicked();void handleCustomSignal(const QString &message); // 带参数的槽声明private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_H
在这里信号和槽的声明都已经带了参数,如下
signals:void customSignal(const QString &message); // 带参数的信号声明private slots:void handleCustomSignal(const QString &message); // 带参数的槽声明
修改 mainwindow.cpp
接下来,我们需要在合适的位置发射信号,并在槽函数中处理传递的参数。
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);// 连接自定义信号到槽,注意这里的参数类型需要匹配connect(this, &MainWindow::customSignal, this, &MainWindow::handleCustomSignal);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_pushButton_clicked()
{// 修改标签的文本ui->label->setText("Button Clicked!");// 发射自定义信号,传递参数emit customSignal("Custom Signal Emitted with Parameter!");
}void MainWindow::handleCustomSignal(const QString &message)
{// 处理自定义信号,接收参数ui->label->setText(message);
}
运行程序
- 点击 Qt Creator 工具栏上的 "Run" 按钮。
- 在弹出的窗口中,点击按钮,观察标签的文本变化。
详细解释
- 信号声明:在类中使用
signals
关键字声明带参数的信号。例:void customSignal(const QString &message);
- 槽声明和定义:在类中用
private slots
或public slots
声明带参数的槽,并实现槽函数。例:void handleCustomSignal(const QString &message);
- 连接信号和槽:使用
QObject::connect()
函数将带参数的信号和槽连接起来。注意信号和槽的参数类型必须匹配。 - 发射信号:在适当时机调用信号,并传递参数。例:
emit customSignal("Custom Signal Emitted with Parameter!");
- 处理参数:在槽函数中接收并处理传递的参数。
通过以上步骤和详细解释,我们实现了一个带参数的信号与槽示例,并深入理解了 Qt 信号与槽机制中带参数的用法。
七、信号(signals)的 访问权限
上面的例子中,我们看到信号的声明
signals:void customSignal(const QString &message); // 带参数的信号声明
并没有带public
、protected
或 private关键字,那么它到底是什么权限呢?下面我们进行讲解。
在 Qt 中,信号(signals)在类中是默认的 protected
访问权限,而不是 private
。这意味着信号通常只能从类内部或其派生类中发射(emit)。然而,在实际使用中,你可以将信号声明为 public
、protected
或 private
,以控制其访问权限。
默认情况
如果你不显式声明信号的访问权限,信号默认是 protected
:
signals:void customSignal(const QString &message); // 带参数的信号,默认是protected
显式声明访问权限
你可以显式地声明信号为 public
、protected
或 private
:
如 public 信号:
### 示例如果我们希望信号能够从类的外部发射,可以将信号声明为 `public`:#### 修改 `mainwindow.h````cpp
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();public signals: // 显式声明信号为publicvoid customSignal(const QString &message); // 带参数的信号声明private slots:void on_pushButton_clicked();void handleCustomSignal(const QString &message); // 带参数的槽声明private:Ui::MainWindow *ui;
};#endif // MAINWINDOW_H
修改 mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);// 连接自定义信号到槽,注意这里的参数类型需要匹配connect(this, &MainWindow::customSignal, this, &MainWindow::handleCustomSignal);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::on_pushButton_clicked()
{// 修改标签的文本ui->label->setText("Button Clicked!");// 发射自定义信号,传递参数emit customSignal("Custom Signal Emitted with Parameter!");
}void MainWindow::handleCustomSignal(const QString &message)
{// 处理自定义信号,接收参数ui->label->setText(message);
}
- 关键说明
- 默认情况下,
signals
关键字下声明的信号是protected
。- 你可以根据需要显式地将信号声明为
public
、protected
或private
。- 选择合适的访问权限取决于你希望信号从哪里发射和使用。
八、信号和槽的优点
- 松耦合:信号和槽使对象之间的通信更加松散,无需对象彼此了解。
- 类型安全:编译时检查信号和槽的签名是否匹配,避免了运行时错误。
- 灵活性:可以动态连接和断开信号和槽,支持多种连接模式(例如,一个信号连接多个槽,或多个信号连接一个槽)。
总结
Qt 的信号和槽机制为开发者提供了一种优雅、灵活且类型安全的方式来处理对象间的通信。通过理解和利用这一机制,可以显著提高应用程序的模块化、可维护性和可扩展性。
相关文章:

Qt 开发:深入详解 Qt 的信号与槽机制——彻底搞懂QT信号与槽
一、概念 Qt 的信号与槽(Signals and Slots)机制是一个用于对象间通信的核心特性。这个机制使得对象能以松散耦合的方式进行通信,从而提升了代码的模块化和可维护性。 信号(Signal):对象状态的变化或事件…...

民间故事推广系统小程序的设计
管理员账户功能包括:系统首页,个人中心,用户管理,民族文化管理,节日类型管理,传统节日管理,故事类型管理,民间故事管理,系统管理 微信端账号功能包括:系统首…...

关于武汉芯景科技有限公司的IIC缓冲器芯片XJ4307开发指南(兼容LTC4307)
一、芯片引脚介绍 1.芯片引脚 2.引脚描述 二、系统结构图 三、功能描述 1.总线超时,自动断开连接 当 SDAOUT 或 SCLOUT 为低电平时,将启动内部定时器。定时器仅在相应输入变为高电平时重置。如果在 30ms (典型值) 内没有变为高…...

C++ 异常
这里写目录标题 1.C语言传统的处理错误的方式2.C异常概念3.异常的用法3.1 异常的抛出和捕获3.2 异常的重新抛出3.3异常安全3.4 异常规范 4.自定义异常体系5.标准库异常体系6.异常的优缺点 1.C语言传统的处理错误的方式 传统的错误处理机制: 1. 终止程序,…...

ST官方 VSCode 插件安装及配置工程参考
写在前头 VSCode的用法和插件是月初参加ST官方北京站举办的线下培训中,厂家AE工程师给我们讲的,不同于已经很多人用的(并且一直在吵的)keil assistant什么的,用的是CMake编译,抛弃了原有的keil,…...
使用Pandas读取和写入数据库的Python函数实现
使用Pandas读取和写入数据库的Python函数实现 Pandas是一个强大的数据处理和分析库,广泛应用于数据科学和机器学习领域。结合数据库操作,Pandas可以极大地简化数据的读取和写入过程。本文将详细介绍如何使用Pandas实现读取和写入数据库的函数,涵盖数据库连接、数据读取、数…...

Redis——常用数据类型hash
目录 hash常用命令hsethgethdelhkeyshvalshgetallhmgethlenhsetnxhincrbyhdecrby 哈希的编码方式哈希的应用 hash 常用命令 hset HSET key field value [field value ...]//时间复杂度O(1) //返回值:设置成功的键值对的个数hget HGET key field//hdel HDEL key…...
基于Python实现一个庆祝中秋节的小程序
功能包括: 使用复杂的库来计算农历日期:可以使用 lunarcalendar 库来计算农历日期。提供更多的祝福语:可以通过随机选择祝福语来增加趣味性。加入图形用户界面 (GUI):可以使用 tkinter 库来创建一个简单的图形用户界面。 我们可…...

近期最值得买的数码好物有什么?2024兼具功能和实用性的好物分享
在如今这个科技飞速发展的时代,数码好物层出不穷,它们为我们带来了前所未有的便捷与乐趣。而对于学生党和上班族来说,挑选到既实用又性价比高的数码好物,更是能为学习和办公生活增添一抹亮丽的色彩。其中,电容笔便是备…...
云服务器中的MinIO 配置 HTTPS 过程(图文)
目录 1. 基本知识2. 实战3. 彩蛋1. 基本知识 具体证书的格式如下: 私钥:private.key公钥:public.crt (公钥以pem格式结尾,可直接改为crt格式)证书和私钥文件的命名和路径应该是: 证书文件: ~/.minio/certs/public.crt 私钥文件: ~/.minio/certs/private.key使用了自定义…...

注册安全分析报告:熊猫频道
前言 由于网站注册入口容易被黑客攻击,存在如下安全问题: 暴力破解密码,造成用户信息泄露短信盗刷的安全问题,影响业务及导致用户投诉带来经济损失,尤其是后付费客户,风险巨大,造成亏损无底洞…...

计算机毕业设计 自习室座位预约系统的设计与实现 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试
🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点…...

2000-2021年3月海关数据库
2000-2021年3月海关数据库 1、时间:2000-2021年3月 2、指标:2000-2015数据变量包括:年份、截止日期、进出口分类代码、进出口分类名称、HS商品编码、HS商品名称、金额_美元、数量、价格、经营单位代码、经营单位名称、经营单位地址、电话、…...

【YashanDB知识库】archivelog磁盘满导致数据库abnormal
本文转自YashanDB官网,具体内容可见archivelog磁盘满导致数据库abnormal 【问题分类】功能使用 【关键字】磁盘空间满,archivelog日志,archivelog自动清理 【问题描述】数据库状态变更为abnormal,检查V$DIAG_INCIDENT视图&#…...

远程跨境传输大文件如何做到安全又稳定?
在当今全球化的商业环境中,企业跨境传输大文件的需求日益增长。这不仅涉及到数据的快速迁移,还包括了安全性、稳定性和合规性等多重挑战。本文将探讨企业在跨境传输大文件时可能遇到的问题,以及在传输过程中应注意的事项,并重点介…...
JSON报文根据正则过滤消息
有时候业务系统在接收外部传过来的JSON报文,可能需要根据某个标识来判断是否是自己系统的消息,不是需要过滤。正常我们可能是先将JSON反序列化为具体实体类(例: A a JSON.parseObject(body,A.class)),然后获取具体字段来判断。此方法面对接收…...
BOM编程
什么是 BOM? BOM(Browser Object Model)是浏览器提供的对象和方法的集合,允许开发者操作浏览器窗口、页面跳转、URL、浏览器历史记录、用户设备信息等。window 对象是 BOM 的顶层对象,所有 BOM API 都直接或间接作为 …...

【C++ Primer Plus习题】16.1
大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream> #include <string> usin…...

音视频入门基础:AAC专题(1)——AAC官方文档下载
一、AAC简介 高级音频编码(英语:Advanced Audio Coding,AAC)是有损音频压缩的专利数字音频编码标准,由Fraunhofer IIS、杜比实验室、贝尔实验室、Sony、Nokia等公司共同开发。出现于1997年,为一种基于MPEG…...
RAG与LLM原理及实践(17)---Docker Redis Python Usage
目录 背景 Redis 环境 download 修改镜像 Run Redis Coding python redis download 基本使用 描述 完整代码 运行结果 高阶用法 序列化的方式 Snapshot 与 AOF 快照(RDB) AOF(Append-Only File) 代码 总结 发布与订阅 描述 代码 运行结果 注…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...

【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...

el-switch文字内置
el-switch文字内置 效果 vue <div style"color:#ffffff;font-size:14px;float:left;margin-bottom:5px;margin-right:5px;">自动加载</div> <el-switch v-model"value" active-color"#3E99FB" inactive-color"#DCDFE6"…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...

USB Over IP专用硬件的5个特点
USB over IP技术通过将USB协议数据封装在标准TCP/IP网络数据包中,从根本上改变了USB连接。这允许客户端通过局域网或广域网远程访问和控制物理连接到服务器的USB设备(如专用硬件设备),从而消除了直接物理连接的需要。USB over IP的…...

基于Springboot+Vue的办公管理系统
角色: 管理员、员工 技术: 后端: SpringBoot, Vue2, MySQL, Mybatis-Plus 前端: Vue2, Element-UI, Axios, Echarts, Vue-Router 核心功能: 该办公管理系统是一个综合性的企业内部管理平台,旨在提升企业运营效率和员工管理水…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用
一、方案背景 在现代生产与生活场景中,如工厂高危作业区、医院手术室、公共场景等,人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式,存在效率低、覆盖面不足、判断主观性强等问题,难以满足对人员打手机行为精…...

(一)单例模式
一、前言 单例模式属于六大创建型模式,即在软件设计过程中,主要关注创建对象的结果,并不关心创建对象的过程及细节。创建型设计模式将类对象的实例化过程进行抽象化接口设计,从而隐藏了类对象的实例是如何被创建的,封装了软件系统使用的具体对象类型。 六大创建型模式包括…...

【Post-process】【VBA】ETABS VBA FrameObj.GetNameList and write to EXCEL
ETABS API实战:导出框架元素数据到Excel 在结构工程师的日常工作中,经常需要从ETABS模型中提取框架元素信息进行后续分析。手动复制粘贴不仅耗时,还容易出错。今天我们来用简单的VBA代码实现自动化导出。 🎯 我们要实现什么? 一键点击,就能将ETABS中所有框架元素的基…...