一、基本概念
要想了解connect() 函数,要先知道在qt中什么是什么是信号(signal)和槽(slot)。
信号(signal):指在特定情况下发射的通知。比如当我们点击了用户界面的一个按钮之后就发射了一个信号。
槽(slot):是指对信号进行响应的函数。也就是我们点击了一个按钮后我们要实现的功能。
那么connect() 函数就是qt 框架中用于将信号(signal)和槽(slot)关联起来的核心函数。
二、用法
1.旧版用法(qt4和早期qt5)
connect(sender, signal(signalname()), receiver, slot(slotname()));
- sender:发射信号的对象名称
- signalname():信号名称
- receiver:接收信号的对象名称,一般情况下为槽函数所属对象,写this即可
- slotname():槽函数名称
示例:
connect(button, signal(clicked()), this, slot(handlebuttonclicked()));
注意: 如果信号与槽函数带参数还需要在函数括号内标出。
2.新版用法(推荐)
connect(sender, &senderclass::signalname, receiver, &receiverclass::slotname);
这里引入了基于函数指针的语法,使得编译器可以进行更好的类型检查。但是这里有一个问题:与旧版的写法不同的是,信号与槽函数的参数怎么办?
首先我们知道,在 qt 的信号与槽机制中,槽函数的参数不能多于信号的参数。也就是说,信号的参数可以多于槽的参数,且槽函数的参数必须是信号参数的一个子集。那么就可以分为如下三种情况:
1.信号和槽函数具有相同数量和类型的参数。这时可以直接连接且无需出现函数参数:
connect(lineedit, &qlineedit::textchanged, this, &widget::do_textchanged);
在上面示例中,信号textchanged(qstring)和槽函数do_textchanged(qstring)参数相同。
2.信号的参数多于槽函数的参数。槽函数将忽略信号的多余参数,这个特性允许你在信号和槽参数不完全匹配的情况下,仍然能够正常工作。正常情况下,不推荐把槽函数设计为overload型。
3.某些信号的参数有默认值。也就是说同一信号的参数分为有参数和无参数,如qcheckbox的clicked()信号和clicked(bool)信号。这时connect()函数有两种写法:
a.设置不同名字的槽函数:
//槽函数 void do_checked(bool checked); void do_checked_noparam(); //connect()函数 connect(checkbox, &qcheckbox::clicked, this, &widget::do_checked); connect(checkbox, &qcheckbox::clicked, this, &widget::do_checked_noparam);
b.使用模板函数qoverload()来明确参数类型:
connect(checkbox, &qcheckbox::clicked, this, qoverload(&widget::do_checked)); connect(checkbox, &qcheckbox::clicked, this, qoverload<>(&widget::do_checked));
3.自动连接(无需使用connect()函数)
在构建项目时,若勾选了“generate form”字段,则会生成一个后缀为“.ui”的文件。
那么在ui设计界面种可以选中一个对象,右键选择“go to slot”或者“转到槽”,在弹出的对话框中再选择信号,那么就会自动生成一个槽函数。如果槽函数的名称符合相应的格式,qt 会在运行时通过setupui(this)自动生成connect()函数完成连接。
dialog::dialog(qwidget *parent) : qdialog(parent) , ui(new ui::dialog) { //由go to slot生成的槽函数可以自动关联 ui->setupui(this); //自定义槽函数需要手动关联 connect(ui->radiobuttonblack,signal(clicked()),this,slot(do_setfontcolor())); } //自动生成的槽函数格式:void on__ (){} void widget::on_btncal_clicked() { ... } //手动写的槽函数 void widget::do_setfontcolor() { ... }
当然在设计一些复杂功能时,自动连接方法就不太适用了。
4.lambda表达式(qt5.4)
lambda 表达式提供了一种非常灵活的方式来处理信号。有如下几个优点:
- 可以直接在连接语句中定义信号处理逻辑,无需在类中定义额外的槽函数。这在处理简单逻辑时尤其方便。
- 避免为每个信号处理逻辑定义新的槽函数,减少命名空间污染。
- 允许你在处理信号参数时,捕获外部变量或定义额外的处理逻辑。
基本格式如下:
connect(sender, &senderclass::signalname, [lambda expression]);
举个栗子对比理解一下:
//方法一:使用槽函数指针连接 void processdata(int value, qstring message) { if (value > 10) { qdebug() << "value is greater than 10:" << value << ", message:" << message; } else { qdebug() << "value is 10 or less:" << value; } } qobject::connect(&sender, &senderclass::signalname, this, &receiverclass::processdata);
//方法二:使用lambda表达式连接 qobject::connect(&sender, &senderclass::signalname, [this](int value, qstring message) { if (value > 10) { qdebug() << "value is greater than 10:" << value << ", message:" << message; } else { qdebug() << "value is 10 or less:" << value; } });
三、断开连接disconnect()
1.为什么使用disconnect()
使用connect()函数可以关联信号与槽,那么删除connect()函数不就可以断开连接了吗?
其实不然!我们最好使用官方的 disconnect() 函数来解除信号与槽的连接。原因有如下几点:
a.动态控制信号和槽的连接
有时你需要在运行时动态地控制信号和槽的连接。例如,你可能需要在某些情况下连接信号和槽,而在其他情况下断开它们。在这种情况下,使用 disconnect() 函数可以让你在需要时灵活地断开连接,而不需要修改代码来删除 connect() 调用。
b.保持代码的结构和可读性
connect() 调用通常在类的构造函数或初始化方法中设置,表示对象之间的关系和交互。将连接和断开连接的逻辑分开可以使代码更清晰、更具结构性。如果直接删除 connect() 调用,代码逻辑可能会变得混乱且难以维护。
c. 方便的调试和维护
使用 disconnect() 可以更方便地调试和维护代码。你可以在调试时临时断开某些连接,而无需删除和重新添加 connect() 调用。这样可以避免错误,并更容易地还原代码。
d. 多次连接和断开
在某些情况下,你可能会多次连接和断开相同的信号和槽。例如,当某个条件发生变化时,你希望断开以前的连接并建立新的连接。使用 disconnect() 可以确保你正确地断开之前的连接,而不会遗留下不需要的连接。
2.使用方法
a.解除与一个发射者所有信号的连接
disconnect(myobject, nullptr, nullptr, nullptr); myobject->disconnect();
b.解除与一个特定信号的所有连接
disconnect(myobject, signal(mysignal), nullptr, nullptr); myobject->disconnect(signal(mysignal));
c.解除与一个特定接收者的所有连接
disconnect(myobject, nullptr, myreceiver, nullptr); myobject->disconnect( myreceiver);
d.解除特定的一个信号与槽的连接
disconnect(lineedit, &qlineedit::textchanged, label, &qlabel::settext);
四、细节问题
1. 一个信号可以连接到多个槽,所有连接的槽都会在信号发出时依次被调用。
connect(sender, &senderclass::signalname, receiver1, &receiverclass1::slotname); connect(sender, &senderclass::signalname, receiver2, &receiverclass2::slotname);
2. 信号和槽机制是单向的,即信号发出时会调用槽函数,但槽函数中发出的信号不会自动调用其他槽函数,除非你明确连接了这些信号和槽。