三、信号与槽

news/2025/2/21 10:52:24/

1. 信号槽的定义

信号函数和槽函数是Qt在C++的基础上新增的功能,功能是实现对象之间的通信。
实现信号槽需要有两个先决条件:

  • 通信的对象必须是从QObject派生出来的

    QObject是Qt所有类的基类。

  • 类中要有Q_OBJECT

2. 信号槽的使用

2.1 函数原型

最常用且最基础的信号槽连接函数如下所示:
// 参数1:发送者,信号槽触发的来源的对象
// 参数2:信号函数,发送者的触发动作,使用SIGNAL()包裹
// 参数3:接收者,信号槽触发后执行动作的对象
// 参数4:槽函数,接收者执行的动作,使用SLOT()包裹
QObject::connect(const QObject * sender, const char * signal, const QObject * receiver, const char * method) [static]

按照不同的情况,分为三种情况进行学习:

  • 方式一:自带信号→自带槽
  • 方式二:自带信号→自定义槽
  • 方式三:自定义信号

可以使用disconnect函数断开已经连接的信号槽,参数与connect连接时保持一致。返回值为是否断开成功,如果已经不连接了,则会断开失败,此时不会有任何影响。

2.2 自带信号→自带槽

这种情况下信号函数和槽函数都是Qt内置的,程序员只需要找到对应关系后连接即可。

【例子】点击按钮,关闭窗口。
分析:
发射者:按钮
信号函数:点击
接收者:窗口
槽函数:关闭

#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
#include <QPushButton>class Dialog : public QDialog
{Q_OBJECTpublic:Dialog(QWidget *parent = 0);~Dialog();private:QPushButton* btn;
};#endif // DIALOG_H
#include "dialog.h"Dialog::Dialog(QWidget *parent): QDialog(parent)
{resize(300,300);btn = new QPushButton("关闭",this);btn->move(100,150);//  发射者:按钮
//	信号函数:点击
//	接收者:窗口
//	槽函数:关闭connect(btn,SIGNAL(clicked()),this,SLOT(close()));
}Dialog::~Dialog()
{delete btn;
}

2.3 自带信号→自定义槽

【例子】点击按钮,窗口向右侧移动10个像素,向下移动10个像素,同时输出当前的窗口坐标。
分析:
发射者:按钮
信号函数:点击
接收者:窗口
槽函数:自定义

#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
#include <QPushButton>
#include <QDebug>class Dialog : public QDialog
{Q_OBJECTpublic:Dialog(QWidget *parent = 0);~Dialog();private:QPushButton* btn;private slots: // 槽函数void mySlot(); // 头文件声明
};#endif // DIALOG_H
#include "dialog.h"Dialog::Dialog(QWidget *parent): QDialog(parent)
{resize(300,300);btn = new QPushButton("关闭",this);btn->move(100,150);// 发射者:按钮
//    信号函数:点击
//	接收者:窗口
//	槽函数:自定义connect(btn,SIGNAL(clicked()),this,SLOT(mySlot()));
}void Dialog::mySlot() // 源文件定义
{// 先获得当前的窗口坐标int x = this->x();int y = this->y();// 窗口向右侧移动10个像素,向下移动10个像素move(x+10,y+10);// 同时输出当前的窗口坐标。qDebug() << x+10 << y+10;
}Dialog::~Dialog()
{delete btn;
}

2.4 自定义信号

这种方式主要用于解决复杂问题,所以在本节强行使用。

信号函数具有以下特点:👇

  • 信号函数只有声明,没有定义
  • 信号函数没有权限
  • 声明后只用emit关键字发射
  • 可携带参数

【例子】点击按钮,关闭窗口。
image.png
image.png

#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
#include <QPushButton>class Dialog : public QDialog
{Q_OBJECTpublic:Dialog(QWidget *parent = 0);~Dialog();private:QPushButton* btn;private slots:void mySlot();// 声明信号函数
signals:void mySignal(); // 自定义信号
};#endif // DIALOG_H
#include "dialog.h"Dialog::Dialog(QWidget *parent): QDialog(parent)
{resize(400,400);btn = new QPushButton("关闭",this);btn->move(100,150);connect(btn,SIGNAL(clicked()),this,SLOT(mySlot()));connect(this,SIGNAL(mySignal()),this,SLOT(close()));
}void Dialog::mySlot()
{// 发射自定义信号emit mySignal();
}Dialog::~Dialog()
{delete btn;
}

3. 信号槽传参

信号槽支持参数传递,信号函数可携带参数发送给槽函数。

【例子】点击按钮,按钮上显示点击的次数。
正常的做法不使用信号槽传参:
image.png
dialog.h

#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
#include <QPushButton>class Dialog : public QDialog
{Q_OBJECTpublic:Dialog(QWidget *parent = 0);~Dialog();private:QPushButton* btn;int count; // 点击的次数private slots:void btnClickedSlot(); // 点击按钮的槽函数
};#endif // DIALOG_H

dialog.cpp

#include "dialog.h"Dialog::Dialog(QWidget *parent): QDialog(parent),count(0) // 构造初始化列表
{resize(400,400);btn = new QPushButton("0",this);btn->move(100,200);connect(btn,SIGNAL(clicked()),this,SLOT(btnClickedSlot()));
}void Dialog::btnClickedSlot()
{// 显示点击的次数count++; // count是成员变量// int → 字符串QString text =  QString::number(count);// 给按钮设置显示内容btn->setText(text);
}Dialog::~Dialog()
{delete btn;
}

上面的例子强行增加信号槽传参的语法。
image.png
dialog.h

#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
#include <QPushButton>class Dialog : public QDialog
{Q_OBJECTpublic:Dialog(QWidget *parent = 0);~Dialog();private:QPushButton* btn;int count; // 点击的次数private slots:void btnClickedSlot(); // 点击按钮的槽函数void countSlot(int); // 自定义槽函数2signals:// 能发参数的自定义信号void countSignal(int);
};#endif // DIALOG_H

dialog.cpp

#include "dialog.h"Dialog::Dialog(QWidget *parent): QDialog(parent),count(0) // 构造初始化列表
{resize(400,400);btn = new QPushButton("0",this);btn->move(100,200);connect(btn,SIGNAL(clicked()),this,SLOT(btnClickedSlot()));connect(this,SIGNAL(countSignal(int)),this,SLOT(countSlot(int)));
}void Dialog::btnClickedSlot()
{count++;// 发射带参数的自定义信号emit countSignal(count);
}/*** @brief Dialog::countSlot* @param count此数值并非成员变量,而是信号函数发射来的*/
void Dialog::countSlot(int count)
{// int → 字符串QString text = QString::number(count);// 设置显示btn->setText(text);
}Dialog::~Dialog()
{delete btn;
}

4. 总结

  • 槽函数是一种特殊的成员函数
  • 信号函数只有声明,没有定义,因此不能调用
  • 理论上可以传递任意多个信号槽参数
  • 信号的参数个数必须大于等于槽函数的参数个数
  • 信号槽传参的参数类型必须匹配
  • 一个信号函数可以连接多个槽函数,多个信号函数也可以连接看一个槽函数(一对多,多对一)

下面是一个一对多和多对一的例子:
dialog.h

#ifndef DIALOG_H
#define DIALOG_H#include <QDialog>
#include <QPushButton>
#include <QDebug>class Dialog : public QDialog
{Q_OBJECTpublic:Dialog(QWidget *parent = 0);~Dialog();private:QPushButton *btn1; // 一对多QPushButton* btn2;private slots:void btnClickedSlot1();void btnClickedSlot2();void btnClickedSlot3();void btnClickedSlot4();
};#endif // DIALOG_H

dialog.cpp

#include "dialog.h"Dialog::Dialog(QWidget *parent): QDialog(parent)
{resize(400,400);btn1 = new QPushButton("1",this);btn1->move(100,100);btn2 = new QPushButton("2",this);btn2->move(200,200);// 一对多connect(btn1,SIGNAL(clicked()),this,SLOT(btnClickedSlot1()));connect(btn1,SIGNAL(clicked()),this,SLOT(btnClickedSlot2()));// 一对一connect(btn2,SIGNAL(clicked()),this,SLOT(btnClickedSlot3()));// 多对一connect(btn1,SIGNAL(clicked()),this,SLOT(btnClickedSlot4()));connect(btn2,SIGNAL(clicked()),this,SLOT(btnClickedSlot4()));
}void Dialog::btnClickedSlot1()
{qDebug() << "a";
}void Dialog::btnClickedSlot2()
{qDebug() << "b";
}void Dialog::btnClickedSlot3()
{// 槽函数也是成员函数this->btnClickedSlot1();btnClickedSlot2();
}void Dialog::btnClickedSlot4()
{qDebug() << "发射者是谁?";// 多对一的情况下如何区分发射者:if(btn1 == sender()){qDebug() << "发射者是btn1";}else if(btn2 == sender()){qDebug() << "发射者是btn2";}
}Dialog::~Dialog()
{delete btn1;delete btn2;
}

📢上面要注意的点使用了sender()函数,该函数用来返回信号发送者对象的首地址,当有多个发送者对应同一个槽函数时,可以用来判断是谁发的消息。


http://www.ppmy.cn/news/1167746.html

相关文章

【LeetCode】67. 二进制求和

1 问题 给你两个二进制字符串 a 和 b &#xff0c;以二进制字符串的形式返回它们的和。 示例 1&#xff1a; 输入:a “11”, b “1” 输出&#xff1a;“100” 示例 2&#xff1a; 输入&#xff1a;a “1010”, b “1011” 输出&#xff1a;“10101” 2 答案 自己写…

Linux_Shell运行原理(命令行解释器)

一般我们叫Linux操作系统&#xff0c;狭义上就是指Linux内核&#xff08;kernel&#xff09;&#xff0c;广义上就是Linux内核Linux外壳程序对应的配套程序&#xff0c;这里我们来详细介绍一下这个“外壳程序”。 在我们使用指令时&#xff0c;这个外壳程序会将我们的解释指令并…

USRP-2944 配件讲解,如何选择对应的配件

USRP-2944 产品图片 产品官网价格信息 查看附件和价格 硬件服务 NI硬件服务计划通过简化物流&#xff0c;延长正常运行时间以及根据业界标准维护数据的可追溯性&#xff0c;帮助您节省系统组装、设置和维护所需的时间和金钱。这些计划涵盖多年期维修服务&#xff0c;同时还提…

sqlmap防御以及文件读写

一.防御 过滤 1.使用过滤函数 $email filter_var($_POST[email], FILTER_VALIDATE_EMAIL); if ($email) { // input is a valid email address } else { // input is not a valid email address 使用 filter_var() 函数和 FILTER_VALIDATE_EMAIL 过滤器来验证用户输…

Java - 多进程编程(对比线程、API 操作)

目录 一、多进程编程 1.1、为什么要使用多进程编程 1.2、Java 中多进程编程的实现 1.2.1、前言 1.2.2、进程创建 1.2.3、进程等待 1.2.4、封装操作到一个工具类中 一、多进程编程 1.1、为什么要使用多进程编程 一个 .exe 文件执行以后&#xff0c;就会变成一个进程. 多…

Ubuntu22.04安装nvidia-docker

安装docker 参考这篇文章&#xff1a;Ubuntu22.04安装docker - 掘金 安装nvidia-docker 参考这篇文章&#xff1a;Ubuntu 22.04 LTS : NVIDIA Container Toolkit : Install : Server World 流程&#xff1a; curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | …

统计学习方法 感知机

文章目录 统计学习方法 感知机模型定义学习策略学习算法原始算法对偶算法 学习算法的收敛性 统计学习方法 感知机 读李航的《统计学习方法》时&#xff0c;关于感知机的笔记。 感知机&#xff08;perceptron&#xff09;是一种二元分类的线性分类模型&#xff0c;属于判别模型…

创新的营销模式与线上商城的完美结合

分享购&#xff0c;一个与众不同的电商平台&#xff0c;以一种全新的营销模式和独特的商业运营模式&#xff0c;颠覆了传统电商的观念&#xff0c;让每个人都能拥有属于自己的线上商城。它集自营品牌、供应链管理和CPS等多种优势于一身&#xff0c;形成了一种创新的交易和共享生…