【Qt初识】信号槽(二):自定义信号函数与槽函数


大家好,我是Halcyon.平安

欢迎文末添加好友交流,共同进步!


目录

一、自定义槽函数

1.1 背景

在以前的Qt版本中,槽函数必须放在 public /procted /privated slots;

slots是Qt自己扩展出来的关键字,它并不是C++的标准语法。Qt是广泛使用元变成技术(基于代码生成代码)。

qmake 在构建Qt项目的时候,就会调用专门的扫描机器,识别代码中的特定关键词(如slots),然后根据特定的关键字去生成一大段相关的代码。

但是在Qt5这个新版本中我们则可以不用这样,很大的简洁了代码(毕竟谁不喜欢哪样简单用哪样呢?!),但是我还是推荐大家使用关键字,因为这样写起来是更加的规范!

1.2 实现

方式一:

我们这里通过自定义槽函数来实现通过点击按钮然后改变窗口标题来看看效果!首先我们自己新建一个项目,然后去widget.h进行声明,然后去.cpp进行函数的实现,并用connect进行连接。

cpp 复制代码
public slots:
    void handleclicked();

接下来进行槽函数的实现:

cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include<QPushButton>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);

    QPushButton* pushbutton = new QPushButton(this);
    pushbutton->setText("按钮");

    connect(pushbutton,&QPushButton::clicked,this,&Widget::handleclicked);

}

Widget::~Widget()
{
    delete ui;
}

void Widget::handleclicked()
{
    this->setWindowTitle("恭喜你,转换成功!");
}

因为我使用的版本是Qt5.14的,所以如果我不在widget.hpublic slots声明这个函数也是可以运行实现的,但是为了代码的规范性所以是加上了!

方式二:

我们其实还可以用第二种方式,那句是在widget.ui中拖动按钮,然后右键选择转到槽,选择后自动生成好一个函数(定义+声明)然后我们自己去函数里编写代码即可!

因为我们的信号类型是点击按钮,所以选择clicked即可,随后会自动跳到自动生成的函数编写代码处,我们来验证一下!

我们可以看到自动生成的是使用private slots进行声明限定

我们来编写自定义槽函数代码试试!

cpp 复制代码
#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来连接信号槽之外,还可以通过函数名字来进行信号和槽的连接,所以我们来看看自动生成的槽函数的名称on_pushButton_clicked

  • on是固定写法
  • pushButton是Qt Designer给我们自动生成的按钮的属性objectName对应的值pushButton(关于按钮属性之前是讲过的)
  • clicked信号函数的名称

这就刚好符合connect函数要的前两个参数(信号源,信号类型),那么第三个参数就是当前on_pushButton_clicked所在的类,第四个参数就是on_pushButton_clicked这个自定义槽函数

这必须一一对应,如果哪一个参数你修改了的话,就都会运行失败!

相对于自己写代码完成信号和槽函数的连接的话我更推荐图形化创建控件,因为图形化创建控件更加的快速,能够自动连接,不需要我们自己手动去建立联系!

二、自定义信号函数

信号其实就相当于对应的是用户的操作,比如点击按钮等。我们上面讲的自定义槽函数,其实在开发中是占很大部分的,很多情况这个触发某个信号(操作)后的业务逻辑都需要我们自己去写!但是信号不一样,Qt内置的信号已经基本覆盖了这些操作!

Qt信号,本质上也是一个函数,它有两个比较特殊的点:

  • 我们只需要写函数声明,并告诉Qt这是一个"信号",定义会在Qt的编译中自动生成。
  • 作为信号函数,返回类型必须是void,对于参数的话,有没有都可以,甚至是可重载的!

我们说需要告诉Qt这是一个信号,那么该如何告诉?对!signals,我们需要用到这个关键字,但是我们建立连接其实并不代表发射信号,那么我们如何发出信号呢!那就需要emit来进行发射(其实内置信号也是需要发射的,但是发射信号已经内置到了Qt的框架中,不需要我们自己去手动触发)!

接下来我们来自定义一个信号函数来加深理解!

cpp 复制代码
signals:
    void mysignal();

public slots:
    void handleMysignal();
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);
}

Widget::~Widget()
{
    delete ui;
}

void Widget::handleMysignal()
{
    this->setWindowTitle("自定义信号函数关联自定义槽函数成功!");
}

有没有发现什么问题!那就是我们上面说的建立连接其实并不代表发射信号,需要emit来进行发射

cpp 复制代码
{
    ui->setupUi(this);

    connect(this, &Widget::mysignal, this, &Widget::handleMysignal);

    emit mysignal();
}

接下来我们来看看效果!

三、带参的信号函数和槽函数

信号函数带参数的时候(起到复用代码的作用),是必须要求和槽函数的参数类型必须与信号一致,但是个数可以不一致。

虽然个数可以不一致,但是信号函数的参数个数必须大于槽函数的参数个数。

那么为什么个数可以不一致呢?

  • 因为一个槽可以绑定多个信号
  • 非要要求参数个数一致的话,信号绑定槽的要求更高,没那么灵活

3.1 为什么要带参数

我们前面写的信号和槽都是不带参数的,但是大家想想,在实际开发中,光靠一个"信号来了"这种干巴巴的通知是远远不够的!比如我们窗口中有两个按钮,点击哪一个都要触发同一个槽函数去修改窗口标题,那到底要改成什么内容?这时候光靠信号本身已经不够用了,必须让信号把"具体的内容"也一起带过去!

这就是信号和槽带参数的意义------让信号在触发的同时,把数据顺带传递给槽函数,这样槽函数就能根据传过来的参数去执行不同的逻辑,从而起到复用代码的作用!

3.2 参数规则补充说明

我们把上面的规则再换个角度理解一下!信号是"发送方",槽是"接收方",发送方发送出去的东西,接收方可以选择全部接收,也可以选择只接收前面几个;但是发送方都没发出去的东西,接收方又怎么可能凭空收得到呢?所以槽函数的参数个数绝对不能超过信号函数的参数个数,这也就是为什么必须是"信号 >= 槽"!

另外参数类型也得对得上,比如信号传的是 QString,槽也得用 QString 接收,类型不匹配的话,编译是会报错的!

3.3 代码演示

我们来自定义一个带参数的信号函数和槽函数,让信号把一个字符串带过去,槽函数接收后把它设置成窗口标题!

首先是 widget.h 中的声明:

cpp 复制代码
signals:
    void mysignal(QString text);

public slots:
    void handleMysignal(QString text);

然后是 widget.cpp 中的实现和连接:

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(QString text)
{
    this->setWindowTitle(text);
}

运行后我们就能看到窗口标题被成功修改了!这说明信号在发射的时候,确实把字符串 text 这个参数顺带传给了槽函数,槽函数接收到之后用它来设置窗口标题!

3.4 参数个数不一致的情况

上面是参数个数一致的情况,那么我们前面说的"个数可以不一致"又是怎样的呢?我们把槽函数的参数省略掉试试!

cpp 复制代码
signals:
    void mysignal(QString text);

public slots:
    void handleMysignal();   // 槽函数不接收参数

这样同样是合法的!信号带了参数,槽函数可以"选择性失明",只接收信号触发的这个时机,不接收信号带的数据,这就是 Qt 信号槽给我们提供的灵活之处!

但是要注意:如果反过来,信号没有参数,而槽函数却有参数,那就是不行的了!因为槽函数需要的参数,信号根本就没传过来,这就违背了我们前面讲的"信号 >= 槽"的规则,运行时也就不会得到我们想要的结果!

相关推荐
r_oo_ki_e_1 小时前
Java 线程池
java·开发语言
秋田君2 小时前
Qt_QVariant
开发语言·qt
江华森2 小时前
Python 实现高德地图找房(三):地图可视化与高德 JS API
开发语言·javascript·python
weixin_471383032 小时前
args,...args与Parameters<T>
开发语言·前端
杜子不疼.3 小时前
【Qt初识】工程起步:项目创建、代码解读与对象树
数据库·c++·qt
ctrl_v助手3 小时前
C#表达题笔记
开发语言·c#
2zcode4 小时前
基于MATLAB卷积神经网络的口罩佩戴检测系统
开发语言·matlab·cnn
米饭不加菜4 小时前
使用万用表判断三极管(BJT)好坏
java·开发语言
绝世唐门三哥5 小时前
vue3中页面返回时刷新首页的处理方案
开发语言·前端·javascript