Qt_信号与槽_connect_自定义槽函数_自定义信号_2

news/2024/11/17 5:27:26/

文章目录

  • 一、信号和槽的概述
  • 二、connect
    • 1.函数体
    • 2.使用样例
    • 3.问题
      • 1.问题1
      • 2.问题2
  • 三、自定义槽函数和信号
    • 1.代码自定义槽函数
    • 2.图形化一键创建槽函数
    • 3.自定义信号 - signals - emit
    • 4.信号和槽存在的意义
    • 5.关于信号槽两补充知识点
      • 1.使用disconnect来断开信号槽的连接。
      • 2.定义槽函数的时候,也是可以使用lambda表达式
  • 四、小结


一、信号和槽的概述

Linux信号Signal,系统内部的通知机制,进程间通信的方式。
信号源:谁发的信号
信号的类型:哪种类别的信号
信号的处理方式:注册信号处理函数,在信号被触发的时候自动调用执行

Qt中的信号和Linux中的信号,虽然不是一样的概念,但是确实有相似之处。

Qt中,谈到信号,也是涉及到了三个要素。

  • 信号源:由哪个控件发出的信号。
  • 信号的类型:用户进行不同的操作,就可能触发不同的信号。
  • 点击按钮,触发点击信号。
    在输入框移动光标,触发移动光标的信号。
    咱们写的GUI程序,就是要让用户进行操作,就是要和用户进行交互,这个过程中就需要关注,用户当前的操作具体是个什么样的操作。

信号的处理方式:槽(slot)=>函数
Qt中可以使用connect这样的函数,把一个信号和一个槽关联起来。
后续只要信号触发了,Qt就会自动的执行槽函数。
所谓的“槽函数”本质上也是一种“回调函数”(callback)

Qt中,一定是先关联信号和槽,然后再触发这个信号,顺序不能颠倒,否则信号就不知道如何处理了。

二、connect

这个函数和LinuxTCP socket中建立连接的函数,没有任何关系,只是名字恰巧一样了
是QObject提供的静态的成员函数。
Qt中提供的的这些类,本身是存在一定继承关系的:

Widget的子类:
QPushButton
QLineEdit
QTextEdit
QRadioButton

Widget是QObject的子类,而connect是QObject的。

1.函数体

connect()函数原型:
最后一个参数暂时不考虑,也很少用到这个。

QObject::connect(const QObject *sender,const char *signal,const QObject *receiver,const char *method,Qt::ConnectionType type = Qt::AutoConnection)
  • sender:信号的发送者
  • signal:信号的类型
  • receiver:哪个对象负责处理
  • method:这个对象该怎么处理

2.使用样例

界面上包含一个按钮,用户点击按钮,则关闭窗口。
所谓“信号”也是Qt中的对象,内部提供的一些成员函数。
在这里插入图片描述

click是一个slot函数,作用就是调用的时候相当于点击了一下按钮
clicked(过去分词形式,完事了,点完了),才是要触发的点击信号。

Widget::close():是QWidget内置的槽函数,Widget继承自QWidget,也就继承了父亲的槽函数。

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPushButton* button = new QPushButton(this);button->setText("关闭");button->move(200,200);connect(button,&QPushButton::clicked,this,&Widget::close);
}Widget::~Widget()
{delete ui;
}

在这里插入图片描述
点击后关闭。

3.问题

1.问题1

1.咋知道的QPushButton有个clicked信号?咋知道的Qwigget有一个close槽?Qt里面到底提供了哪些内置的信号和槽可以让我们直接使用?

多看文档!!!
如果在翻阅文档的时候,如果在当前类中没有找到对应的线索,不妨看看这个类的父类。
在这里插入图片描述
abstract抽象的
Qt中会提供好几种按钮
这些按钮之间存在一些“共性”内容
就把这些共性的东西,提取出来,放到了QAbstractButton类里面。

在这里插入图片描述
在这里插入图片描述
当这个按钮被激活,就会发送这个信号。
emitted:发送
activated:激活

2.问题2

const char *signal 和const char method
在实际上用的 :
connect(button,&QPushButton::clicked,this,&Widget::close);
,&QPushButton::clicked ->函数指针void(*)()
&Widget::close->函数指针bool(*)()
char
和函数指针是同一个东西吗?
不是一个东西。

C++中,不允许你使用两个不同的指针类型,互相赋值
这个函数声明,是以前的旧版本的Qt的connect函数的声明。以前版本中,传参的写法和现在其实也是有区别的。
此时,给信号参数传参,要搭配一个SIGNAL宏,给槽参数传参,搭配一个SLOT宏。
connect(button,SIGNAL(&QPushButton::clicked),this,SLOT(&Widget::close));

Qt 5 开始,对上诉写法做出了简化,不在需要写宏了
给connect提供了重载版本,重载版本中,第二个参数和第四个参数达成了泛型参数,允许我们传入任意类型的函数指针了。

在这里插入图片描述

三、自定义槽函数和信号

1.代码自定义槽函数

所谓的自定义一个槽函数,操作过程和自定义一个普通的成员函数,没啥区别。
在以前Qt中,槽函数必须放到
public/private/protected slots;

widget.h

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();void  handleClicked();
private:Ui::Widget *ui;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPushButton* button = new QPushButton(this);button->setText("按钮");button->move(100,100);connect(button,&QPushButton::clicked,this,&Widget::handleClicked);
}Widget::~Widget()
{delete ui;
}void Widget::handleClicked()
{//按下按钮,修改一下窗口标题。this->setWindowTitle("按钮已经按下");
}

在这里插入图片描述

2.图形化一键创建槽函数

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);}Widget::~Widget()
{delete ui;
}void Widget::on_pushButton_clicked()
{this->setWindowTitle("按钮已经按下");
}

在这里插入图片描述
问题没有connect是如何连接?
在Qt中除了通过connect来连接信号槽之外,还可以通过函数名的方式来自动连接。
如示例:void Widget::on_pushButton_clicked()
on_按钮的objectName_信号的名字
当函数名符合上述规则之后,Qt就能自动的把信号和槽给建立上联系。

3.自定义信号 - signals - emit

Qt中也允许自定义信号。
自定义槽函数,非常关键,开发中大部分情况都需要自定义槽函数的。
槽函数,就是用户触发某个操作之后,要进行的业务逻辑。
自定义信号,比较少见,实际开发中很少需要自定义信号。
信号就对应到用户的某个操作,在GUI,用户能够进行哪些操作,是可以穷举的,Qt内置的信号,基本上已经覆盖到了上述所有可能的用户操作。
因此使用Qt内置的信号,就足以应对大部分的开发场景。
自定义信号,本身代码比较简单的。

所谓的Qt的信号,本质上也就是一个“函数”
Qt5以及更高版本中,槽函数和普通的成员函数之间,没啥差别了
但是信号则是一类非常特殊的函数。
程序员只要写出函数声明,并且告诉Qt,这是一个“信号”即可。
这个函数的定义,是Qt在编译过程中,自动生成的。(自动生成的过程,程序员无法干预)
信号在Qt中是特殊的机制,Qt生成的信号函数的实现,要配合Qt框架做很多既定的操作。

作为信号函数,这个函数的返回值,必须是void
有没有参数都可以,甚至也可以支持重载。

signals:
这个也是Qt自己扩展出来的关键字,qmake的时候,调用一些代码的分析/生成工具,扫描到类中包含signals这个关键字的时候,此时,就会自动的把下面的函数声明认为是信号,并且给这些信号函数自动的生成函数定义。

如何才能触发出自定义的信号?
Qt内置的信号,都不需要我们手动通过代码来触发,用户在GUI,进行某些操作,就会自动触发对应的信号(发射信号的代码已经内置到Qt框架中了)

关键字 emit - 发射
emit 信号函数()
发送自定义的信号。
widget.h

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();signals:void  mySignal();public:void handleMySignal();private:Ui::Widget *ui;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);connect(this,&Widget::mySignal,this,&Widget::handleMySignal);emit mySignal();
}Widget::~Widget()
{delete ui;
}void Widget::handleMySignal()
{this->setWindowTitle("处理自定义信号");
}

在这里插入图片描述

信号和槽也可以带参数:
widget.h

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();signals:void  mySignal(const QString& text,const QString& text2);public:void handleMySignal(const QString& text );private:Ui::Widget *ui;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);connect(this,&Widget::mySignal,this,&Widget::handleMySignal);emit mySignal("把标题设置为标题2");
}Widget::~Widget()
{delete ui;
}void Widget::handleMySignal(const QString& text)
{this->setWindowTitle(text);
}

在这里插入图片描述

注意:
信号函数参数可以比槽函数参数多,但反过来不行。
在这里插入图片描述
Qt中如果要让某个类能够使用信号槽,(可以在类中定义信号和槽函数),则必须要在类最开始的地方,写下Q_OBJECt宏。

4.信号和槽存在的意义

Qt中谈到的信号和槽“多对多”就和数据库的多对多非常类似
学生(学号 姓名…)
1 张三
2 李四
3 王五
课程(课程编号,课程名字…)
100 语文
101 数学
102 英语

一个学生,可以选择多门课程来学习,一门课程,也可以被多个学生来选择。
connect的作用,就相当于关联表,将学生和课程关联起来。

综上Qt引入信号槽机制,最本质的目的(初心)就是为了能够让信号和槽之间按照“多对多”的方式来进行关联,其他的GUI开发的过程中,“多对多”这件事,其实是一个“伪需求”,实际开发很少会用到,绝大部分情况,一对一就够用了。

5.关于信号槽两补充知识点

1.使用disconnect来断开信号槽的连接。

disconnect使用的方式和connect是非常类似的。
在这里插入图片描述
widget.h

#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();void handleClick();void handleClick2();
private slots:void on_pushButton_2_clicked();private:Ui::Widget *ui;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include<QDebug>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);connect(ui->pushButton,&QPushButton::clicked,this,&Widget::handleClick);
}Widget::~Widget()
{delete ui;
}void Widget::handleClick()
{this->setWindowTitle("修改窗口的标题");qDebug()<<"handleClick";
}void Widget::handleClick2()
{this->setWindowTitle("修改窗口的标题2");qDebug()<<"handleClick2";
}void Widget::on_pushButton_2_clicked()
{//1.先断开pushButton原理的信号槽disconnect(ui->pushButton,&QPushButton::clicked,this,&Widget::handleClick);connect(ui->pushButton,&QPushButton::clicked,this,&Widget::handleClick2);
}

在这里插入图片描述

2.定义槽函数的时候,也是可以使用lambda表达式

#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>
#include<QDebug>
Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget)
{ui->setupUi(this);QPushButton* button = new QPushButton(this);button->setText("按钮");button->move(200,200);connect(button,&QPushButton::clicked,this,[button,this](){qDebug()<<"lambda被执行了!";button->move(300,300);this->setWindowTitle("我被按了");});}Widget::~Widget()
{delete ui;
}

在这里插入图片描述
注:
如果当前lambda里面想使用更多的外层变量咋办?
写作[=]
这个写法的含义就是把上层作用域中所有的变量名都给捕获进来。

另外也要确认捕获的lambda内部的变量是有意义的,回调函数执行时机是不确定的(用户啥时候点击按钮不知道的),无论何时用户点击了按钮,捕获到的变量都能正常使用。

lambda语法是C++中引入的,对于Qt5以及跟高版本,默认就是按照C++11来编译的。如果使用Qt4或者更老的版本,就需要手动在.pro文件中加上C++11的编译选项: CONFIG+=C++11

四、小结

1.信号槽是啥,尤其是和Linux中信号进行对比。
a.信号源
b.信号的类型
c.信号的处理方式
2.信号槽使用
connect
3.如何查阅文档
一个控件,内置了哪些信号,信号都是何时触发。
一个控件,内置了哪些槽,槽都是什么作用
很有可能需要的信号槽,还得到这个类的父类/爷爷类/祖宗类去进行查询
4.自定义槽函数
本质上就是自定义一个普通的成员函数
还可以让Qt Creator自动生成,(虽然没有显示connect,但是可以通过函数名字特定规则来完成自动连接)
5.自定义信号
信号本质就是成员函数(函数的定义是Qt自己生成的,咱们只需要写函数声明)
signals:定义关键字中
emit来完成信号的发射(emit也可以省略)
6.信号和槽还可以带参数
发送信号的时候,把参数传递给对应的槽
信号参数和槽的参数要一致
a.类型匹配
b.个数,信号的参数要多于槽的参数
7.信号槽存在的意义
解耦合
多对多效果(非常类似于mysql中的多对多)
8.disconnect使用方式
9.lambda表达式,简化槽函数的定义


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

相关文章

大厂面试sql手撕题目总结

文章目录 1. 常用函数1. 日期函数 1. 常用函数 1. 日期函数 常用日期函数&#xff1a; -- 返回当前日期 select curdate(); # 2024-05-06 -- 返回当前时间 select curtime(); # 21:41:58 -- 返回当前日期加时间 select now(); # 2024-05-06 21:40:53 -- 获取指定date的年…

Obsidium v1.5.4壳 逆向分析

样本是使用这个壳进行打包的 来进行逆向 进入X32 DBG中 进来一看就是经典花指令 混淆解析器 可以在CE中查找指令地址 来得到当前真正执行的指令 对SEH链进行劫持 马上就来个int 1进入异常处理函数 无视异常 直接进去 观察Zwcontinue函数传进去的Context结构体 结构体偏移…

SFP、SFP+、SFP28之间的区别是什么?

随着网络技术的不断发展&#xff0c;光纤通信在各个领域中得到了广泛的应用。SFP、SFP和SFP28光模块作为三种常见的光模块类型&#xff0c;具有不同的特点和适用范围。本文将从尺寸、传输速率、兼容性、功耗、应用等方面来比较SFP、SFP和SFP28。 什么是SFP? SFP&#xff1a;&a…

华为OD机试【最大N个数与最小N个数的和】(java)(100分)

1、题目描述 给定一个数组&#xff0c;编写一个函数来计算它的最大N个数与最小N个数的和&#xff0c;需要对数组进行去重。 说明&#xff1a; ● 数组中数字范围[0, 1000] ● 最大N个数与最小N个数不能有重叠&#xff0c;如有重叠&#xff0c;输入非法返回-1 ● 输入非法返回-…

滑块槽位最优寻找

说明 如果图像中存在多个干扰槽,并且我们只想找到与滑块最匹配的槽,我们可以通过一些额外的策略来实现。一种方法是计算每个槽与滑块的距离,并选择距离最近的那个作为最匹配的槽 代码 import cv2 import numpy as npdef calculate_distance(cx_slider, cy_slider, cx_slot, …

SparkStructuredStreaming状态编程

spark官网关于spark有状态编程介绍比较少&#xff0c;本文是一篇个人理解关于spark状态编程。 官网关于状态编程代码例子: spark/examples/src/main/scala/org/apache/spark/examples/sql/streaming/StructuredComplexSessionization.scala at v3.5.0 apache/spark (github…

​「Python绘图」绘制五角星

python 绘制五角星 一、预期结果 二、核心代码 import turtle print("开始绘制五角星")# 设置画布尺寸 # screen turtle.Screen() # screen.setup(width500, height500)# 创建Turtle对象 pen turtle.Turtle() pen.shape("turtle")# 移动画笔到起始位置 …

【docker】常用的把springboot打包为docker镜像的maven插件

Spring Boot Maven Plugin: Spring Boot 自带的 Maven 插件 (spring-boot-maven-plugin) 支持直接生成 Docker 镜像。通过配置&#xff0c;可以在 Maven 构建过程中自动构建 Docker 镜像&#xff0c;而无需单独编写 Dockerfile。这种方法简化了将应用打包为 Docker 镜像的过程。…