QT之信号和槽

在刚刚了解Qt的时候,为了通过按钮显示 hello world 的时候曾说明过信号与槽,虽然没有细说,不过也算是接触过。

而本文就会细细说明什么是 Qt 的信号与槽。

概念初识

在 linux 学进程相关的内容的时候,曾了解过信号是操作系统控制进程的一种方式,可以看作是操作系统和进程通信的方式,而 Qt 的信号与槽实际上也差不多。

Qt中的信号三要素

  • 信号源:发出信号的控件
  • 信号类型:用户进行不同的操作会发送不同的信号,比如按钮被点击了,某个文本被复制了,鼠标光标被移动的信号,这些信号都需要区分
  • 槽:槽实际上就是一个信号到来时所需要执行的函数

在 Qt 中需要先通过 connect 函数将信号和槽关联起来,当特定的信号到来时,就触发特定的槽,执行特定的函数。

而 Qt 中一个类如果使用信号与槽,必须在类内部带一个宏。

这个宏会展开成很长一段的代码,想要使用信号与槽必须带它。 

connect函数

connect(      const QObject* sender,const char * signal,const QObject* receiver,const char * method,Qt::ConnectionType type = Qt::AutoConnection() );
  • sender : 信号源,即发出信号的控件
  • signal :信号,即信号源发出的信号类型
  • receiver : 接收者,即处理信号的控件
  • method : 槽,即接收者处理信号的动作/函数
  • type : 用于指定的关联方式,不过一般采用缺省的 AutoConnection 方式
小实验

我们做一个实验,即以代码的方式实现通过点击按钮关闭窗口。

在 MainWindow.cpp 中初始化一个按钮,并且通过 connect 函数将 QPushButton 中的 clicked 信号和 QMainWindow 中的 close 槽函数关联起来。

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPushButton>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);QPushButton* button = new QPushButton(this);button->setText("closed");button->move(300,300);connect(button,&QPushButton::clicked,this,&QMainWindow::close);
}MainWindow::~MainWindow()
{delete ui;
}

其中,在控件中都有类似的情况,比如 QPushButton 中出现了 click 和 clicked,这二者有什么不同呢?

实际上第一个 click 是一个槽函数,它的作用就是模拟一次点击的动作。

而第二个 clicked 则是一个信号,它是该按钮被点击之后所发送的信号。 

而区别二者就是通过前面的图标

 这个图标表示一个槽函数。

这个图标表示一个信号。

使用 connect 函数可以通过这个图标区别是信号还是槽函数。

QT5之后的connect函数

上述的connect函数第二个参数和第四个参数都是一个 const char* 类型的,但是我们使用时传入的却是函数,这实际上会引发类型错误。

实际上上述的 connect 函数是 QT5 之前的函数,当时传入信号和槽还需要通过 SIGNAL 和 SLOT 两个宏函数来进行转化。

现在更新后就可以直接传了。

 自定义槽函数

作为一个前端工具,只有类自带的槽函数是不够用的,因此我们需要自定义槽函数。

而自定义槽函数的连接有两种方式:通过 connect 函数进行连接和通过 图形化界面生成一个槽函数,QT自己通过 connectSlotByName 进行连接。

通过代码形式

可以自己初始化一个控件,然后定义一个函数后通过connect 函数绑定信号与槽。

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPushButton>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);QPushButton* button = new QPushButton(this);button->setText("hell world");connect(button,&QPushButton::clicked,this,&MainWindow::Handlerclicked);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::Handlerclicked()
{this->setWindowTitle("clicked!");
}

如果这个空间是在 ui 界面直接通过拖拽方式得到的话,就需要从 ui 这个对象中获取。

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPushButton>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);connect(ui->pushButton,&QPushButton::clicked,this,&MainWindow::Handlerclicked);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::Handlerclicked()
{ui->pushButton->setText("hellworld");
}

这样就能够通过代码形式 connect 自定义的槽函数。

通过图形化界面形式

当我们拖拽一个控件到画布上时,通过右键点击可以发现有一个 “转到槽” 的选项。

点击 “转到槽” 之后即可选择这个槽函数所连接的信号。

选择信号后,发现 QT 自动生成了一个函数。

其名字由发出信号的控件名以及发出的信号组成。

通过这种方式也可以将控件的信号与槽函数连接起来,而不用通过connect 函数连接。

并且这个生成的函数名不可修改,因为QT就是通过这个函数名和信号进行连接的,我们可以实验一下。

 其中 connectSlotsByName 是一个函数,表示通过函数名来连接槽函数与信号,因此函数名不可随便修改。

自定义信号

在QT中,我们也可以自定义信号,虽然在开发场景中很少用,不过还是需要了解一下。

QT中的信号实际上就是一个函数,我们看一看如何实现。

在 MainWindow.h 中声明一个函数,其中这个函数前面需要用 signals 关键字修饰一下。

然后在 MainWindow.cpp 中绑定即可。 

绑定的 HandlerSignal 函数会将窗口的标题修改成 "自定义信号"。

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPushButton>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);connect(this,&MainWindow::mySignal,this,&MainWindow::HandlerSignal);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::HandlerSignal()
{this->setWindowTitle("自定义信号");
}

 但是这里只是将信号和槽绑定了而已,但是这个信号并没有发出。

因此我们可以通过间接的方式来发送信号。

这里将 pushButton 的 clicked 信号和 HandlerPush 槽函数绑定,而这个槽函数内部会发送一个 mySigal 信号。

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPushButton>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);connect(this,&MainWindow::mySignal,this,&MainWindow::HandlerSignal);QPushButton* pushbutton = new QPushButton(this);pushbutton->setText("发送 mySignal 信号");connect(pushbutton,&QPushButton::clicked,this,&MainWindow::HandlerPush);pushbutton->move(200,200);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::HandlerSignal()
{this->setWindowTitle("自定义信号");
}void MainWindow::HandlerPush()
{emit mySignal();
}

虽然QT5之后可以不用带 emit 关键字就能发送信号了,但是一般还是带上,防止出现错误。 

                                        emit : 发送信号的关键字。

点击之后,发现 window 的标题改变了,证明确实发送了 mySignal 信号了。

带参数的信号与槽

Qt 的信号与槽函数都能够带参数,就像函数传参一样。

无论自定义的信号与槽还是 Qt 自带的信号与槽都有参数,不过它们都需要遵守一定的规则。

  • 信号的参数和槽的参数类型必须相同,即信号的参数1和槽的参数1类型必须相同
  • 信号的参数个数可以和槽的参数个数可以不同,即信号的参数个数可以比槽的参数个数多

当一个信号被发送时,如果槽函数在遵守上面的规则的情况下需要信号传参,那么 Qt 会将信号的参数作为实参发送给槽函数,我们可以试一试。

首先通过图形化界面设置两个按钮,并且通过图形化界面形式自定义槽函数。

然后再自己设置自己的信号和槽函数,它们都带有参数,并且形式符合规则。 

#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include <QString>
QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();void MyHandlerSignal(const QString& text);
signals:void mysignal(const QString& text);private slots:void on_pushButton_clicked();void on_pushButton_2_clicked();private:Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

两个按钮都会发送一个带有参数的信号,而这个信号对应的函数会修改窗口的名称。

接着我们运行一下看看会发生什么?

 可以看到窗口的标题确实修改了。并且修改成了信号所带的参数。

 除了自定义的槽函数和信号有参数之外,一些自带的槽函数和信号也有参数。

比如这个 PushButton 的控件,其 clikced 信号既有没参数的,也有带参数的。

 取消信号槽的连接

信号和槽不仅可以通过 connect 连接,也能够通过 disconnect 函数取消连接。

因为 Qt 的信号槽机制支持多对多连接,即一个信号可以绑定多个槽函数,一个槽函数也能够绑定多个信号,虽然这个机制不常用,但是如果忽略可能会出现错误。

因此如果有需求的话,可以通过 disconnect 先取消连接,再重新连接其他的信号与槽。

我们可以实验一下,这里有两个按钮,第一个按钮和 Handler1 建立连接,Handler1 会修改窗口名称为 Handler1.

第二个按钮会将第一个按钮和 Handler1 的连接断开,然后和 Handler3 建立连接。

Handler3 会修改窗口的名称为 Handler3.

#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);connect(ui->pushButton,&QPushButton::clicked,this,&MainWindow::Handler1);connect(ui->pushButton_2,&QPushButton::clicked,this,&MainWindow::Handler2);
}MainWindow::~MainWindow()
{delete ui;
}void MainWindow::Handler1()
{this->setWindowTitle("Handler1");
}void MainWindow::Handler2()
{//取消第一个按钮的连接disconnect(ui->pushButton,&QPushButton::clicked,this,&MainWindow::Handler1);//连接其他的槽connect(ui->pushButton,&QPushButton::clicked,this,&MainWindow::Handler3);
}void MainWindow::Handler3()
{this->setWindowTitle("Handler3");
}

 可以看到结果正如预期所料。

如果这里不提前 disconnect 的话,那么按钮1 就会同时和 Handler1 和 Handler3 建立连接,导致一对多的情况出现。

采用 lambada 表达式作为槽函数 

在 Qt 5 以及更高版本的 Qt 下, 一般默认采用的 C++11 编译,因此可以使用 lambada 表达式。

而如果是 Qt4 以及更低的版本,就需要添加指令来采用 C++11 编译。

 这里我们就是用 lambada 表达式作为槽函数。

#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QPushButton>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);QPushButton* button = new QPushButton(this);button->setText("按钮");connect(button,&QPushButton::clicked,this,[=](){button->move(300,300);});
}MainWindow::~MainWindow()
{delete ui;
}

发现确实执行了 lambada 表达式函数的内容。 

 总结

本文讲解了什么是信号与槽,也讲了信号槽的使用方法,比如自定义信号和槽函数,带参数的信号与槽,disconnect 的使用和 lambada 表达式的槽函数。

总的来说信号槽这个机制挺优秀,但是其他的 GUI 工具并没有使用信号槽的机制,比如 java 的GUI开发就是通过类似赋值的手段来将某一个函数与信号关联起来,而不是用 connect 函数来连接。

connect 函数连接可能比较繁琐,但是它实现了代码的低耦合,虽然也足够优秀,但是对比市场上的可能有点不足,这是由于时代的局限性造成的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/661367.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据仓库和数据仓库分层

一、数据仓库概念 数据仓库(Data Warehouse)&#xff0c;可简写为DW或DWH。数据仓库&#xff0c;是为企业所有级别的决策制定过程&#xff0c;提供所有类型数据支持的战略集合。它是单个数据存储&#xff0c;出于分析性报告和决策支持目的而创建。 为需要业务智能的企业&#…

基于STC12C5A60S2系列1T 8051单片机的Proteus中的单片机发送一帧或一串数据给串口调试助手软件接收区显示出来的串口通信应用

基于STC12C5A60S2系列1T 8051单片机的Proteus中的单片机发送一帧或一串数据给串口调试助手软件接收区显示出来的串口通信应用 STC12C5A60S2系列1T 8051单片机管脚图STC12C5A60S2系列1T 8051单片机串口通信介绍STC12C5A60S2系列1T 8051单片机串口通信的结构基于STC12C5A60S2系列…

动手学深度学习3.6 softmax回归的从零开始实现-笔记练习(PyTorch)

以下内容为结合李沐老师的课程和教材补充的学习笔记&#xff0c;以及对课后练习的一些思考&#xff0c;自留回顾&#xff0c;也供同学之人交流参考。 本节课程地址&#xff1a;Softmax 回归从零开始实现_哔哩哔哩_bilibili 本节教材地址&#xff1a;3.6. softmax回归的从零开…

【最大公约数 排序】2344. 使数组可以被整除的最少删除次数

本文涉及知识点 最大公约数 排序 LeetCode2344. 使数组可以被整除的最少删除次数 给你两个正整数数组 nums 和 numsDivide 。你可以从 nums 中删除任意数目的元素。 请你返回使 nums 中 最小 元素可以整除 numsDivide 中所有元素的 最少 删除次数。如果无法得到这样的元素&a…

讯饶科技 X2Modbus 敏感信息泄露

讯饶科技 X2Modbus 敏感信息泄露 文章目录 讯饶科技 X2Modbus 敏感信息泄露漏洞描述影响版本实现原理漏洞复现修复建议 漏洞描述 X2Modbus是一款功能很强大的协议转换网关&#xff0c; 这里的X代表各家不同 的通信协议&#xff0c;2是To的谐音表示转换&#xff0c;Modbus就是最…

企业家如何拥有良好口才的能力(3篇)

企业家如何拥有良好口才的能力&#xff08;3篇&#xff09; **篇&#xff1a;企业家提升口才能力的基石 一、引言 作为企业家&#xff0c;口才不仅是沟通的工具&#xff0c;更是展现领导魅力和企业形象的窗口。本文将探讨企业家如何拥有良好口才能力的基石。 二、清晰的目标与…

用Docker 创建并运行一个MySQL容器

可以在DockerHub官网上荡:mysql - Official Image | Docker Hub 指令是:docker pull mysql; 因为文件比较大可能时间比较长&#xff0c;我是跟着黑马的课走的 课程提供的有文件&#xff0c;我就用已有的资源了。 在tmp目录里放入mysql.tar包 然后cd进去 输入指令:docker lo…

Android binder死亡通知机制

在Andorid 的binder系统中&#xff0c;当Bn端由于种种原因死亡时&#xff0c;需要通知Bp端&#xff0c;Bp端感知Bn端死亡后&#xff0c;做相应的处理。 使用 Bp需要先注册一个死亡通知&#xff0c;当Bn端死亡时&#xff0c;回调到Bp端。 1&#xff0c;java代码注册死亡通知 …

Python | Leetcode Python题解之第61题旋转链表

题目&#xff1a; 题解&#xff1a; class Solution:def rotateRight(self, head: ListNode, k: int) -> ListNode:if k 0 or not head or not head.next:return headn 1cur headwhile cur.next:cur cur.nextn 1if (add : n - k % n) n:return headcur.next headwhi…

目标检测应用场景—数据集【NO.34】风车缺陷检测数据集

写在前面&#xff1a;数据集对应应用场景&#xff0c;不同的应用场景有不同的检测难点以及对应改进方法&#xff0c;本系列整理汇总领域内的数据集&#xff0c;方便大家下载数据集&#xff0c;若无法下载可关注后私信领取。关注免费领取整理好的数据集资料&#xff01;今天分享…

CogVLM/CogAgent环境搭建推理测试

引子 对于多模态大语言模型&#xff0c;一直没有怎么接触。刚巧一朋友有问到这方面的问题&#xff0c;也就顺手调研下。智谱AI的东西一直以来&#xff0c;还是很不错的。ChatGLM的忠实fans&#xff0c;看到白嫖网站github上有他们开源的多模态CogVLM/CogAgent&#xff0c;那就…

自学Java要到什么程度才足够能力去实习和就业?

引言 Java&#xff0c;作为当今软件开发领域的主流编程语言之一&#xff0c;对于初学者而言&#xff0c;明确掌握到什么程度才能开始寻找实习和入职机会是至关重要的。这涉及到对Java知识体系的理解深度、技能掌握程度以及实际项目经验的积累。 本文将分别从实习和入职两个不…