【Qt】信号和槽

发布于:2025-04-22 ⋅ 阅读:(23) ⋅ 点赞:(0)

一、概念

Qt中的信号有以下三个要素:

  • 信号源:由哪个控件发出的信号
  • 信号的类型:用户进行不同的操作,就可能触发不同的信号
  • 信号的处理方式:槽(slot)->函数

Qt中可以使用connect这样的函数,把一个信号和一个槽关联起来,后续只要信号触发,Qt就会自动的执行槽函数(槽函数其实是一种回调函数)

注意:Qt中一定是先关联信号和槽,然后再触发这个信号,顺序

二、connect

connect函数是QObject提供的静态的成员函数
在这里插入图片描述

  • sender:信号是哪个控件发出的
  • signal:信号的类型
  • receiver:哪个控件进行处理
  • method:处理的函数
  • 最后一个参数暂时不考虑,很少使用

示例:点击按钮就会关闭窗口
代码:

#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;
}

当点击窗口的关闭按钮,窗口就会关闭

三、自定义的槽函数

我们自己实现一个槽函数,然后调用它。与自定义一个成员函数是一样的。别忘记声明该函数

代码:

#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::handleClick);

}

Widget::~Widget()
{
    delete ui;
}
void Widget::handleClick()
{
    // 按下按钮,修改窗口标题
    this->setWindowTitle("按钮已经按下");
}

在这里插入图片描述

第二种调用信号槽的方式:
进入界面设计,把QPushButton按钮拉到窗口,右击选择转到槽,然后选择clicked,点击确定,就会在代码中显示这个函数:

void Widget::on_pushButton_clicked()
{
    
}

然后接下来我们实现这个函数:

void Widget::on_pushButton_clicked()
{
    this->setWindowTitle("按钮已经按下");
}

运行,最终的效果与前面一样

这种方式不需要手动connect,因为Qt内部已经帮我们处理好了

四、自定义的信号

Qt中也是允许自定义信号的,在开发中,大部分情况是要自定义槽函数,来进行业务逻辑。相比之下,自定义信号更少见,因为实际开发中用的比较少。

信号是对应用户的某个操作,在GUI中,用户能够进行哪些操作,是可以穷举的,Qt内置的信号,基本上已经覆盖到了上述所有可能的用户操作。因此,使用Qt内置的信号,就足够了。

Widget是没有定义任何信号的,但是它继承了QWidget和QObject,这两个类已经提供了一些信号,可以直接使用。

信号,其实是一种特殊的函数。程序员只要写出函数声明,并且告诉Qt,这是一个信号就行了。这个函数的定义,是Qt在编译过程中自动生成的。

信号是Qt中特殊的机制,Qt生成的信号函数的实现,要配合Qt框架做很多既定的操作

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

代码:

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

public:
    void handleMySignal();

signals:
    void mySignal();
private:
    Ui::Widget *ui;
};
#endif // WIDGET_H
#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("处理自定义信号");
}

注意:建立连接,不代表信号发出。Qt内置的信号都不需要我们手动来触发,自定义的信号需要关键字emit

运行:标题已改变
在这里插入图片描述

还可以通过图形界面化的方式

在这里插入图片描述
然后创建槽函数:

#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("处理自定义信号");
}

void Widget::on_pushButton_clicked()
{
    // 发送自定义信号可以在任何合适的代码中
    //  发送自定义信号
    emit mySignal();
}

在这里插入图片描述
这段代码是编译器自动生成的

运行:点击按钮后标题修改
在这里插入图片描述

注意:在用图形化界面的按钮时,即使没有emit关键字,信号也能发送出去。但还是建议写上

五、带参数的信号和槽

信号和槽也可以带上参数,但是信号带参数时,槽函数的参数必须与信号的参数保持一致。此时发送信号,就可以给信号函数传递实参,然后这个参数就会被传递到槽函数中,最后达到让信号给槽传参的效果。

在这里插入图片描述
信号参数一致主要是类型一致,个数可以不一致,如果不一致,信号的参数个数必须比槽的参数个数多

在这里插入图片描述
信号处理函数的参数的具体内容是发送信号时传参给的

运行:点击按钮,标题修改
在这里插入图片描述

可以再搭配其他的参数,比如多设置一个按钮,然后在按钮点击事件函数里面提供不同的参数,达到同样的效果

六、信号和槽存在的意义

信号和槽解决的问题:响应用户的操作
优点:

  • 解耦合:把触发用户操作的控件和处理对应用户的操作逻辑解耦合
  • 多对多:一个信号可以connect多个槽函数,一个槽函数可以被多个信号connect

connect的作用:与关联表相同
在这里插入图片描述
所以:Qt引入信号槽机制,目的是为了能够让信号和槽之间按照多对多的方式来进行关联。但是实际开发中很少使用,大部分情况一对一就够了。

七、信号和槽的断开

使用disconnect来断开信号槽的连接,用法与connect相似
大部分情况信号和槽连接上就不用管了,主动断开是把信号重新绑定到另一个槽函数上。

先断开原来的连接,再连接新的槽函数:

#include "widget.h"
#include "ui_widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{
    ui->setupUi(this);
    connect(ui->pushButton, &QPushButton::clicked, this, &Widget::handleClick1);
}

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

void Widget::handleClick1()
{
    this->setWindowTitle("修改标题1");
}

void Widget::handleClick2()
{
    this->setWindowTitle("修改标题2");
}

void Widget::on_pushButton_2_clicked()
{
    disconnect(ui->pushButton, &QPushButton::clicked, this, &Widget::handleClick1);
    connect(ui->pushButton, &QPushButton::clicked, this, &Widget::handleClick2);
}

点击按钮修改标题,窗口的标题变成:
在这里插入图片描述
点击切换槽函数,再点击修改标题:
在这里插入图片描述

如果没有断开,两个槽函数都会执行

八、使用lambda表达式定义槽函数

声明和定义一步到位,不需要分开写。要注意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->move(200, 200);
    button->setText("按钮");
    connect(button, &QPushButton::clicked, this, [](){
        qDebug() << "lambda执行";
    });
}

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

在这里插入图片描述


网站公告

今日签到

点亮在社区的每一天
去签到