QT —— 信号和槽(带参数的信号和槽函数)
QT —— 信号和槽(带参数的信号和槽函数)
- 带参的信号和槽函数
- 信号参数个数和槽函数参数个数
- 1. 参数匹配规则
- 2. 实际代码示例
- ✅ 合法连接(槽参数 ≤ 信号参数)
- ❌ 非法连接(槽参数 > 信号参数)
- 3. 特殊处理:使用 Lambda 适配参数
- 4. 注意事项
- 总结
- 信号和槽函数多对多思想
- disconnect
- QT中使用lambda函数
- Lambda 表达式基本结构
- 1. 捕获列表 `[ ]`
- 2. 参数列表 `( )`
- 3. 函数体 `{ }`
- 4. 返回值(可选)
- 常见用法示例
- (1) 按值捕获局部变量
- (2) 按引用捕获并修改局部变量
- (3) 捕获 `this` 访问成员变量
- (4) 带参数的 Lambda
- 注意事项
- 总结
我们之前已经了解了QT当中的信号,并且已经了解了自定义信号和自定义槽函数应该怎样书写,如果还不了解的小伙伴可以点击这里:
https://blog.csdn.net/qq_67693066/article/details/147278172
https://blog.csdn.net/qq_67693066/article/details/147275998
我们今天来看一下带参的信号和槽函数应该是怎么样的:
带参的信号和槽函数
其实还是比较简单,在头文件声明,在cpp文件中实现就行了:
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QString>
#include <QDebug>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();
//槽函数
public slots:void myaction_1(const QString&);//声明信号
signals:void mysignal_1(const QString&); //设置参数为QStringprivate:Ui::Widget *ui;
};
#endif // WIDGET_H
#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. 实际代码示例
✅ 合法连接(槽参数 ≤ 信号参数)
// 信号:带两个参数
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); // 无参数
❌ 非法连接(槽参数 > 信号参数)
// 槽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 表达式 转换参数:
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. 注意事项
-
参数类型必须兼容
- 如果信号是
int
,槽可以是int
或double
(隐式转换),但不能是QString
。 - 例如:
signals: void valueChanged(int); slots: void logValue(double); // ✅ OK(int → double) slots: void logValue(QString); // ❌ 错误(类型不匹配)
- 如果信号是
-
默认参数的影响
- 如果槽有默认参数,实际调用时未传递的参数会使用默认值:
slots: void showMessage(const QString &msg, bool bold = false); // 可以连接单参数信号: signals: void messageSent(const QString &);
- 如果槽有默认参数,实际调用时未传递的参数会使用默认值:
-
旧式 Qt4 语法的限制
- 使用
SIGNAL
和SLOT
宏时,参数类型必须严格匹配(不支持隐式转换):connect(btn, SIGNAL(clicked(bool)), this, SLOT(showMessage(QString))); // ❌ 错误
- 使用
总结
- 槽的参数个数 ≤ 信号的参数个数,且类型必须兼容。
- 多余参数可忽略,但缺少参数或类型不匹配会导致编译/运行时错误。
- 使用 Lambda 表达式可以灵活适配参数不匹配的情况。
如果有更复杂的需求(如动态修改参数),可能需要结合 QSignalMapper
或手动管理信号转发。
信号和槽函数多对多思想
QT中的槽函数其实在设计的时候就是一个比较独特的东西,它可以像MySQL那样,实现多对多(一个信号可以绑定多个槽函数,一个槽函数也可以被多个信号绑定)
我们可以再加一个信号:
#ifndef WIDGET_H
#define WIDGET_H#include <QWidget>
#include <QString>
#include <QDebug>QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{Q_OBJECTpublic:Widget(QWidget *parent = nullptr);~Widget();
//槽函数
public slots:void myaction_1(const QString&);//声明信号
signals:void mysignal_1(const QString&); //设置参数为QStringvoid mysignal_2(const QString&); //再设置一个信号private:Ui::Widget *ui;
};
#endif // WIDGET_H
#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顾名思义就是断开连接:
#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函数的参数要求:
#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 表达式基本结构
[ captures ] ( parameters ) -> return_type { body }
在你的代码中:
[=]() {newbutton->setFixedSize(400, 400);newbutton->setText("这是newbutton的文本");this->setWindowTitle("哈哈哈,我的标题已经改了");
}
对应部分:
[=]
:捕获列表(Captures)()
:参数列表(Parameters,此处为空){}
:函数体(Body)
1. 捕获列表 [ ]
定义 Lambda 如何访问外部变量:
捕获方式 | 说明 |
---|---|
[=] | 按值捕获(隐式捕获所有外部变量,副本) |
[&] | 按引用捕获(直接修改外部变量) |
[this] | 捕获当前类的 this 指针(可访问成员变量/函数) |
[a, &b] | 混合捕获:a 按值,b 按引用 |
[] | 不捕获任何外部变量 |
代码分析:
[=]
表示所有外部变量(如 newbutton
、this
)按值捕获(实际由于它们是指针,仍能修改指向的对象)。
2. 参数列表 ( )
定义 Lambda 的输入参数(类似于普通函数的参数):
- 如果连接的是无参数的信号(如
clicked()
),参数列表可以为空()
。 - 如果信号有参数(如
clicked(bool checked)
),则需要声明参数:connect(button, &QPushButton::clicked, this, [=](bool checked) {qDebug() << "按钮状态:" << checked; });
你的代码分析:
clicked()
信号无参数,所以 ()
为空。
3. 函数体 { }
Lambda 的具体实现逻辑(可以包含多行代码)。
代码分析:
在函数体中修改了按钮大小、文本和窗口标题:
{newbutton->setFixedSize(400, 400); // 设置按钮大小newbutton->setText("这是newbutton的文本"); // 修改按钮文本this->setWindowTitle("哈哈哈,我的标题已经改了"); // 修改窗口标题
}
4. 返回值(可选)
如果 Lambda 有返回值,需通过 -> return_type
指定(你的例子中无返回值,可省略)。
常见用法示例
(1) 按值捕获局部变量
int count = 0;
connect(button, &QPushButton::clicked, this, [=]() {qDebug() << "当前计数:" << count; // 捕获的是 count 的副本
});
(2) 按引用捕获并修改局部变量
int count = 0;
connect(button, &QPushButton::clicked, this, [&]() {count++; // 直接修改外部变量qDebug() << "计数:" << count;
});
(3) 捕获 this
访问成员变量
class Widget : public QWidget {QString m_message = "Hello";
public:void setupButton() {connect(button, &QPushButton::clicked, this, [this]() {qDebug() << m_message; // 直接访问成员变量});}
};
(4) 带参数的 Lambda
connect(button, &QPushButton::toggled, this, [=](bool checked) {button->setText(checked ? "ON" : "OFF");
});
注意事项
-
生命周期问题:
- 按引用捕获(
[&]
)时,需确保外部变量在 Lambda 执行时仍然有效(避免悬空引用)。 - 按值捕获(
[=]
)更安全,但可能增加拷贝开销。
- 按引用捕获(
-
默认捕获
[=]
和[&]
的风险:- 过度使用可能导致意外捕获不需要的变量(推荐显式指定捕获的变量,如
[this, button]
)。
- 过度使用可能导致意外捕获不需要的变量(推荐显式指定捕获的变量,如
-
Qt 信号槽与 Lambda:
- Lambda 中可以直接调用类的成员函数(通过
[this]
捕获)。 - 如果 Lambda 作为槽函数,确保其内部操作是线程安全的(跨线程时需小心)。
- Lambda 中可以直接调用类的成员函数(通过
总结
[=]
:安全捕获外部变量(副本),适合多数场景。()
:根据信号参数决定是否声明参数。{}
:实现自定义逻辑,支持多行代码。- 最佳实践:显式列出需要捕获的变量(如
[this, newbutton]
),避免隐式捕获带来的混淆。
通过 Lambda 表达式,你可以灵活地替代传统槽函数,尤其在需要快速响应信号并操作局部变量时非常方便。