QT —— 信号和槽(带参数的信号和槽函数)

QT ------ 信号和槽(带参数的信号和槽函数)

  • 带参的信号和槽函数
  • 信号参数个数和槽函数参数个数
      • [1. 参数匹配规则](#1. 参数匹配规则)
      • [2. 实际代码示例](#2. 实际代码示例)
        • [✅ 合法连接(槽参数 ≤ 信号参数)](#✅ 合法连接(槽参数 ≤ 信号参数))
        • [❌ 非法连接(槽参数 > 信号参数)](#❌ 非法连接(槽参数 > 信号参数))
      • [3. 特殊处理:使用 Lambda 适配参数](#3. 特殊处理:使用 Lambda 适配参数)
      • [4. 注意事项](#4. 注意事项)
      • 总结
  • 信号和槽函数多对多思想
  • disconnect
  • QT中使用lambda函数
      • [Lambda 表达式基本结构](#Lambda 表达式基本结构)
      • [1. 捕获列表 `[ ]`](#1. 捕获列表 [ ])
      • [2. 参数列表 `( )`](#2. 参数列表 ( ))
      • [3. 函数体 `{ }`](#3. 函数体 { })
      • [4. 返回值(可选)](#4. 返回值(可选))
      • 常见用法示例
        • [(1) 按值捕获局部变量](#(1) 按值捕获局部变量)
        • [(2) 按引用捕获并修改局部变量](#(2) 按引用捕获并修改局部变量)
        • [(3) 捕获 `this` 访问成员变量](#(3) 捕获 this 访问成员变量)
        • [(4) 带参数的 Lambda](#(4) 带参数的 Lambda)
      • 注意事项
      • 总结

我们之前已经了解了QT当中的信号,并且已经了解了自定义信号和自定义槽函数应该怎样书写,如果还不了解的小伙伴可以点击这里:

https://blog.csdn.net/qq_67693066/article/details/147278172
https://blog.csdn.net/qq_67693066/article/details/147275998

我们今天来看一下带参的信号和槽函数应该是怎么样的:

带参的信号和槽函数

其实还是比较简单,在头文件声明,在cpp文件中实现就行了:

cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QString>
#include <QDebug>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
//槽函数
public slots:
    void myaction_1(const QString&);

//声明信号
signals:
    void mysignal_1(const QString&); //设置参数为QString

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{

    ui->setupUi(this);

    // 连接 Widget 的自定义信号到槽函数
    connect(this, &Widget::mysignal_1, this, &Widget::myaction_1);

    //发射信号
    emit Widget::mysignal_1("this this my text"); //传递了参数
}

//实现槽函数
void Widget::myaction_1(const QString& text)
{
    qDebug() << text;
}

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

信号参数个数和槽函数参数个数

在 Qt 的信号和槽机制中,槽函数的参数个数可以少于或等于信号的参数个数,但不能多于信号的参数个数。这是 Qt 信号和槽连接的重要规则之一。


1. 参数匹配规则

情况 信号参数 槽函数参数 是否合法 说明
1 void signal() void slot() ✅ 合法 无参数,完全匹配
2 void signal(int) void slot(int) ✅ 合法 参数类型和数量完全匹配
3 void signal(int, QString) void slot(int) ✅ 合法 槽可以忽略后面的参数
4 void signal(int) void slot(int, QString) ❌ 不合法 槽的参数不能多于信号
5 void signal(int) void slot(QString) ❌ 不合法 参数类型必须兼容
6 void signal(int) void slot() ✅ 合法 槽可以完全不接收参数

2. 实际代码示例

✅ 合法连接(槽参数 ≤ 信号参数)
cpp 复制代码
// 信号:带两个参数
signals:
    void dataReady(int value, const QString &message);

// 槽1:完全匹配(两个参数)
public slots:
    void handleData(int value, const QString &msg) {
        qDebug() << "Value:" << value << "Message:" << msg;
    }

// 槽2:只接收第一个参数(忽略第二个)
    void handleValueOnly(int value) {
        qDebug() << "Value:" << value;
    }

// 槽3:无参数
    void handleNoArgs() {
        qDebug() << "Data received!";
    }

// 连接方式(Qt5 风格)
connect(this, &MyClass::dataReady, this, &MyClass::handleData);      // 完全匹配
connect(this, &MyClass::dataReady, this, &MyClass::handleValueOnly); // 忽略第二个参数
connect(this, &MyClass::dataReady, this, &MyClass::handleNoArgs);    // 无参数
❌ 非法连接(槽参数 > 信号参数)
cpp 复制代码
// 槽4:尝试接收三个参数(但信号只有两个)
public slots:
    void handleInvalid(int a, const QString &b, bool c) {  // 编译报错!
        qDebug() << a << b << c;
    }

// 错误连接
connect(this, &MyClass::dataReady, this, &MyClass::handleInvalid);  // 编译失败

3. 特殊处理:使用 Lambda 适配参数

如果信号和槽的参数不匹配,可以通过 Lambda 表达式 转换参数:

cpp 复制代码
connect(this, &MyClass::dataReady, this, [this](int v, const QString &m) {
    mySlot(v, m, true);  // 手动添加额外参数
});

// 槽函数(三个参数)
void mySlot(int v, const QString &m, bool extra) {
    qDebug() << v << m << extra;
}

4. 注意事项

  1. 参数类型必须兼容

    • 如果信号是 int,槽可以是 intdouble(隐式转换),但不能是 QString

    • 例如:

      cpp 复制代码
      signals: void valueChanged(int);
      slots: void logValue(double);  // ✅ OK(int → double)
      slots: void logValue(QString); // ❌ 错误(类型不匹配)
  2. 默认参数的影响

    • 如果槽有默认参数,实际调用时未传递的参数会使用默认值:

      cpp 复制代码
      slots: void showMessage(const QString &msg, bool bold = false);
      // 可以连接单参数信号:
      signals: void messageSent(const QString &);
  3. 旧式 Qt4 语法的限制

    • 使用 SIGNALSLOT 宏时,参数类型必须严格匹配(不支持隐式转换):

      cpp 复制代码
      connect(btn, SIGNAL(clicked(bool)), this, SLOT(showMessage(QString)));  // ❌ 错误

总结

  • 槽的参数个数 ≤ 信号的参数个数,且类型必须兼容。
  • 多余参数可忽略,但缺少参数或类型不匹配会导致编译/运行时错误。
  • 使用 Lambda 表达式可以灵活适配参数不匹配的情况。

如果有更复杂的需求(如动态修改参数),可能需要结合 QSignalMapper 或手动管理信号转发。

信号和槽函数多对多思想

QT中的槽函数其实在设计的时候就是一个比较独特的东西,它可以像MySQL那样,实现多对多(一个信号可以绑定多个槽函数,一个槽函数也可以被多个信号绑定)

我们可以再加一个信号:

cpp 复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QString>
#include <QDebug>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
//槽函数
public slots:
    void myaction_1(const QString&);

//声明信号
signals:
    void mysignal_1(const QString&); //设置参数为QString
    void mysignal_2(const QString&); //再设置一个信号

private:
    Ui::Widget *ui;
};
#endif // WIDGET_H
cpp 复制代码
#include "widget.h"
#include "ui_widget.h"
#include <QPushButton>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{

    ui->setupUi(this);

    // 连接 Widget 的自定义信号到槽函数
    connect(this, &Widget::mysignal_1, this, &Widget::myaction_1);
    connect(this, &Widget::mysignal_2, this, &Widget::myaction_1);

    //发射信号
    emit Widget::mysignal_1("this this my text");
    emit Widget::mysignal_2("this this my text2");
}

//实现槽函数
void Widget::myaction_1(const QString& text)
{
    qDebug() << text;
}

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

其实这种设计放到现在会有点冗余,因为现在一对一的关系可以完全满足要求了。但不过QT诞生的时代还没有足够的开发经验以供参考,所以做出这样的设计情有可原。

disconnect

disconnect顾名思义就是断开连接:

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

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
{

    ui->setupUi(this);

    // 连接 Widget 的自定义信号到槽函数
    connect(this, &Widget::mysignal_1, this, &Widget::myaction_1);
    disconnect(this, &Widget::mysignal_1, this, &Widget::myaction_1); //断开连接
    connect(this, &Widget::mysignal_2, this, &Widget::myaction_1);

    //发射信号
    emit Widget::mysignal_1("this this my text");
    emit Widget::mysignal_2("this this my text2");
}

//实现槽函数
void Widget::myaction_1(const QString& text)
{
    qDebug() << text;
}

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

QT中使用lambda函数

我们使用connect的时候,最后一个参数其实我们是连接的一个函数,lambda是匿名函数,可以符合我们connect函数的参数要求:

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

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

    connect(newbutton,&QPushButton::clicked,this,[=]()
    {
        newbutton->setFixedSize(400,400);
        newbutton->setText("这是newbutton的文本");
        this->setWindowTitle("哈哈哈,我的标题已经改了");
    });
}

//实现槽函数
void Widget::myaction_1(const QString& text)
{
    qDebug() << text;
}

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

在 Qt 的 connect 中使用 Lambda 表达式 可以方便地编写自定义槽函数,特别是在需要捕获局部变量或执行多行代码时。以下是代码片段及其 Lambda 表达式的详细解析:


Lambda 表达式基本结构

cpp 复制代码
[ captures ] ( parameters ) -> return_type { body }

在你的代码中:

cpp 复制代码
[=]() {
    newbutton->setFixedSize(400, 400);
    newbutton->setText("这是newbutton的文本");
    this->setWindowTitle("哈哈哈,我的标题已经改了");
}

对应部分:

  • [=]:捕获列表(Captures)
  • ():参数列表(Parameters,此处为空)
  • {}:函数体(Body)

1. 捕获列表 [ ]

定义 Lambda 如何访问外部变量:

捕获方式 说明
[=] 按值捕获(隐式捕获所有外部变量,副本)
[&] 按引用捕获(直接修改外部变量)
[this] 捕获当前类的 this 指针(可访问成员变量/函数)
[a, &b] 混合捕获:a 按值,b 按引用
[] 不捕获任何外部变量

代码分析
[=] 表示所有外部变量(如 newbuttonthis)按值捕获(实际由于它们是指针,仍能修改指向的对象)。


2. 参数列表 ( )

定义 Lambda 的输入参数(类似于普通函数的参数):

  • 如果连接的是无参数的信号(如 clicked()),参数列表可以为空 ()

  • 如果信号有参数(如 clicked(bool checked)),则需要声明参数:

    cpp 复制代码
    connect(button, &QPushButton::clicked, this, [=](bool checked) {
        qDebug() << "按钮状态:" << checked;
    });

你的代码分析
clicked() 信号无参数,所以 () 为空。


3. 函数体 { }

Lambda 的具体实现逻辑(可以包含多行代码)。
代码分析

在函数体中修改了按钮大小、文本和窗口标题:

cpp 复制代码
{
    newbutton->setFixedSize(400, 400);      // 设置按钮大小
    newbutton->setText("这是newbutton的文本"); // 修改按钮文本
    this->setWindowTitle("哈哈哈,我的标题已经改了"); // 修改窗口标题
}

4. 返回值(可选)

如果 Lambda 有返回值,需通过 -> return_type 指定(你的例子中无返回值,可省略)。


常见用法示例

(1) 按值捕获局部变量
cpp 复制代码
int count = 0;
connect(button, &QPushButton::clicked, this, [=]() {
    qDebug() << "当前计数:" << count; // 捕获的是 count 的副本
});
(2) 按引用捕获并修改局部变量
cpp 复制代码
int count = 0;
connect(button, &QPushButton::clicked, this, [&]() {
    count++; // 直接修改外部变量
    qDebug() << "计数:" << count;
});
(3) 捕获 this 访问成员变量
cpp 复制代码
class Widget : public QWidget {
    QString m_message = "Hello";
public:
    void setupButton() {
        connect(button, &QPushButton::clicked, this, [this]() {
            qDebug() << m_message; // 直接访问成员变量
        });
    }
};
(4) 带参数的 Lambda
cpp 复制代码
connect(button, &QPushButton::toggled, this, [=](bool checked) {
    button->setText(checked ? "ON" : "OFF");
});

注意事项

  1. 生命周期问题

    • 按引用捕获([&])时,需确保外部变量在 Lambda 执行时仍然有效(避免悬空引用)。
    • 按值捕获([=])更安全,但可能增加拷贝开销。
  2. 默认捕获 [=][&] 的风险

    • 过度使用可能导致意外捕获不需要的变量(推荐显式指定捕获的变量,如 [this, button])。
  3. Qt 信号槽与 Lambda

    • Lambda 中可以直接调用类的成员函数(通过 [this] 捕获)。
    • 如果 Lambda 作为槽函数,确保其内部操作是线程安全的(跨线程时需小心)。

总结

  • [=]:安全捕获外部变量(副本),适合多数场景。
  • ():根据信号参数决定是否声明参数。
  • {}:实现自定义逻辑,支持多行代码。
  • 最佳实践 :显式列出需要捕获的变量(如 [this, newbutton]),避免隐式捕获带来的混淆。

通过 Lambda 表达式,你可以灵活地替代传统槽函数,尤其在需要快速响应信号并操作局部变量时非常方便。

相关推荐
BanyeBirth2 分钟前
C++滑动门问题(附两种方法)
开发语言·c++
一伦明悦დ1 小时前
嵌入式系统C语言编程常用设计模式---参数表驱动设计
c语言·开发语言·单片机·设计模式
Want5951 小时前
Python炫酷烟花
开发语言·python·pygame
androidwork1 小时前
Android 内存溢出(OOM)的 Kotlin 排查与优化指南
android·开发语言·kotlin
androidwork1 小时前
Kotlin与Flutter:跨平台开发的互补之道与实战指南
开发语言·flutter·kotlin
明月看潮生2 小时前
青少年编程与数学 02-020 C#程序设计基础 02课题、开发环境
开发语言·青少年编程·c#·开发环境·编程与数学
明月看潮生2 小时前
青少年编程与数学 02-020 C#程序设计基础 03课题、开始编程
开发语言·青少年编程·c#·编程与数学
Cherl.2 小时前
C语言 贪吃蛇小游戏的实现
c语言·开发语言·链表·贪吃蛇
Susea&2 小时前
初识C++:模版
c语言·开发语言·c++
V文宝2 小时前
R语言速查表
开发语言·r语言