我有很多信号都具有相同的参数但执行不同的功能。
所有信号的连接和断开代码都是相同的,信号连接的插槽处理程序也是如此。
而不是一遍又一遍地编写这段代码。我想使用函数指针或类似的东西来分配信号,然后有一个公共代码块来执行连接或断开连接。
以下代码只是为了说明我所描述的内容,它不是有效的,也不会编译。
void (*pfnSignal)(quint8, QString);
switch( eSigID ) {
case SIGNAL_A:
pfnSignal = signalA;
break;
case SIGNAL_B:
pfnSignal = signalB;
break;
default:
pfnSignal = NULL;
}
if ( pfnSignal != NULL ) {
QObject::connect(pobjRef, pfnSignal, this, SLOT(handler(quint8, QString)));
}
答案 0 :(得分:6)
在Qt5中,这可以轻松完成,因为它允许使用new pointer to member function syntax进行连接。
// Using decltype to avoid figuring out the ugly pointer-to-member-function syntax.
// Assumes all signals have the same arguments.
decltype<&ThatClass::someSignal> pfnSignal = nullptr;
switch( eSigID ) {
case SIGNAL_A:
pfnSignal = &ThatClass::signalA;
break;
case SIGNAL_B:
pfnSignal = &ThatClass::signalB;
break;
}
if (pfnSignal) {
connect(pobjRef, pfnSignal, this, &ThisClass::handler);
}
但实际上,Qt4甚至可以实现这一点,因为SIGNAL
宏的类型为const char*
。
const char *pfnSignal = nullptr;
switch( eSigID ) {
case SIGNAL_A:
pfnSignal = SIGNAL(signalA(quint8, QString));
break;
case SIGNAL_B:
pfnSignal = SIGNAL(signalB(quint8, QString));
break;
}
if (pfnSignal) {
QObject::connect(pobjRef, pfnSignal, this, SLOT(handler(quint8, QString)));
}
答案 1 :(得分:3)
C ++ 11允许您编写非常简洁的Qt代码。
利用range-based for loops迭代指针。这些可以是指向小部件的指针,指向方法的指针等等:
for (auto signal : {&Class::signal1, &Class:signal2})
QObject::connect(sender, signal, receiver, slot);
利用lambda expressions捕获常量参数值:
auto const cMySlot = [&](void (Sender::*signal)(int)){
QObject::connect(sender, signal, receiver, slot);
然后:
for (auto signal : {&Class::signal1, &Class:signal2}) cMySlot(signal);
完整示例:
// https://github.com/KubaO/stackoverflown/tree/master/questions/signals-simpler-43631464
#include <QtWidgets>
#include <initializer_list>
class Receiver : public QLabel {
Q_OBJECT
public:
Receiver(QWidget * parent = {}) : QLabel{parent} {}
Q_SLOT void intSlot(int val) {
setText(QStringLiteral("int = %1").arg(val));
}
};
class Sender : public QWidget {
Q_OBJECT
QFormLayout m_layout{this};
QPushButton btn1{"Send 1"}, btn2{"Send 5"}, btn3{"Send 10"};
public:
Sender(QWidget * parent = {}) : QWidget{parent} {
m_layout.setMargin(1);
for (auto w : {&btn1, &btn2, &btn3}) m_layout.addWidget(w);
auto const clicked = &QPushButton::clicked;
connect(&btn1, clicked, this, [this]{ emit signal1(1); });
connect(&btn2, clicked, this, [this]{ emit signal2(5); });
connect(&btn3, clicked, this, [this]{ emit signal3(10); });
}
Q_SIGNAL void signal1(int);
Q_SIGNAL void signal2(int);
Q_SIGNAL void signal3(int);
};
using Widgets = std::initializer_list<QWidget*>;
int main(int argc, char **argv)
{
QApplication app{argc, argv};
QWidget win;
QVBoxLayout layout{&win};
Sender sender;
Receiver receiver;
for (auto w : Widgets{&sender, &receiver}) layout.addWidget(w);
// Factor out connection
auto const cIntSlot = [&](void (Sender::*signal)(int)){
QObject::connect(&sender, signal, &receiver, &Receiver::intSlot);
};
// Factor out connection on a list
for (auto signal : {&Sender::signal1, &Sender::signal2, &Sender::signal3})
cIntSlot(signal);
win.show();
return app.exec();
}
#include "main.moc"
答案 2 :(得分:2)
实际上,Thomas McGuire比我快。 (该死的。)虽然,我想补充这个答案,因为:
它提供了完整的样本。
它使用函子而不是信号处理程序的对象/成员函数指针。
因此,它可能是Thomas McGuire答案的补充。
在Qt 5之前,信号由char*
描述,应该非常简单。因此,我认为您的问题与Qt 5以来的新API有关。
如果使用正确的方法指针类型,这也应该有效。我为QPushButton
和QCheckBox
执行此操作以进行演示,因为它们都来自QAbstractButton
,而又有两个具有相同签名的信号。对于您的解决方案,IMHO必须具有相同的信号签名。
#include <QtWidgets>
enum SigType { None, Click, Toggle };
template <typename FUNCTOR>
void installSignalHandler(
QAbstractButton *pQBtn,
SigType sigType,
FUNCTOR sigSlot)
{
void (QAbstractButton::*pSignal)(bool) = nullptr;
switch (sigType) {
case Click: pSignal = &QAbstractButton::clicked; break;
case Toggle: pSignal = &QAbstractButton::toggled; break;
}
if (pSignal) QObject::connect(pQBtn, pSignal, sigSlot);
}
int main(int argc, char **argv)
{
qDebug() << "Qt Version: " << QT_VERSION_STR;
// main application
QApplication app(argc, argv);
// setup GUI
QWidget qWin;
QVBoxLayout qVBox(&qWin);
QPushButton qBtn1("Button 1 -> Click");
qVBox.addWidget(&qBtn1);
QPushButton qBtn2("Button 2 -> Toggle");
qVBox.addWidget(&qBtn2);
QPushButton qBtn3("Button 3 -> None");
qVBox.addWidget(&qBtn3);
QCheckBox qTgl1("Toggle 1 -> Click");
qVBox.addWidget(&qTgl1);
QCheckBox qTgl2("Toggle 2 -> Toggle");
qVBox.addWidget(&qTgl2);
QCheckBox qTgl3("Toggle 3 -> None");
qVBox.addWidget(&qTgl3);
qWin.show();
// install signal handlers
installSignalHandler(&qBtn1, Click,
[](bool) { qDebug() << "Button 1 received clicked."; });
installSignalHandler(&qBtn2, Toggle,
[](bool) { qDebug() << "Button 2 received toggled."; });
installSignalHandler(&qBtn3, None, // will be actually never called
[](bool) { qDebug() << "Button 3 received none."; });
installSignalHandler(&qTgl1, Click,
[](bool) { qDebug() << "CheckBox 1 received clicked."; });
installSignalHandler(&qTgl2, Toggle,
[](bool) { qDebug() << "CheckBox 2 received toggled."; });
installSignalHandler(&qTgl2, None, // will be actually never called
[](bool) { qDebug() << "CheckBox 3 received none."; });
// run-time loop
return app.exec();
}
使用VisualStudio进行编译和测试Windows 10(64位)上的Qt 5.6: