我想将信号或插槽的名称存储到容器中,以便我可以检索它并与之建立连接。
执行此操作的一种方法是hack the SIGNAL/SLOT macro并将信号/广告位的名称存储为字符串,然后使用old connect syntax进行此类连接
void connect(QObject *dst, QString slot)
{
connect(this, SIGNAL(foo(void)), dst, slot.toLocal8Bit().data());
// void foo(void) is a member signal of "this" and is known
// at compile time
// but "dst" and "slot" are not known until runtime.
}
然而,这对我来说似乎有些太过分了。
我知道有another syntax for connect并且还有an object type for signal/slots,这意味着不同的(可能)不那么狡猾的解决方案。 (请注意,这不是new signal/slot syntax in Qt5)
但问题是,QMetaMethod
没有文档化的构造函数,它似乎不是可复制构造的。
另一个问题是,当信号/插槽的名称是动态的时,如何建立连接?
对于我的编码环境是Qt4和gcc 4.3.3以及gnu++98
,所以欢迎需要Qt5和C ++ 11的解决方案,但对我来说不太有用。
答案 0 :(得分:2)
2个解决方案,我正在寻找第3个。
用于示例的简单类:
#include <QObject>
#include <QDebug>
class MyClass : public QObject
{
Q_OBJECT
public:
MyClass() : QObject() { }
void fireSignals()
{
qDebug() << "signal1 :" ;
emit signal1();
qDebug() << "signal2 :" ;
emit signal2();
qDebug() << "signal3 :" ;
emit signal3(3);
}
public slots:
void slot1() { qDebug() << "slot1"; }
void slot2() { qDebug() << "slot2"; }
void slot3() { qDebug() << "slot3"; }
void slot4(int i) { qDebug() << "slot4" << i; }
signals:
void signal1();
void signal2();
void signal3(int);
};
<强>的Qt4 / C ++ 98:强>
我没有Qt4,请告诉我这个解决方案是否有效。
使用Qt 5.5测试,mingw 4.9.2,-std = c ++ 98
它依赖于将信号或插槽名称转换为索引,然后转换为关联的QMetaMethod。
注意:可以存储QMetaMethod
个对象。此方法更安全,因为对插槽存在的检查更早发生,但在创建MyClass
之前需要QMap
实例,而存储插槽名称更灵活。
// Get the index of a method, using obj's metaobject
QMetaMethod fetchIndexOfMethod(QObject* obj, const char* name)
{
const QMetaObject* meta_object = obj->metaObject();
QByteArray normalized_name = QMetaObject::normalizedSignature(name);
int index = meta_object->indexOfMethod(normalized_name.constData());
Q_ASSERT(index != -1);
return meta_object->method(index);
}
// A QObject::connect wrapper
QMetaObject::Connection dynamicConnection(QObject* source,
const char* signal_name,
QObject* dest,
const char* slot_name)
{
return QObject::connect(source, fetchIndexOfMethod(source, signal_name),
dest, fetchIndexOfMethod(dest, slot_name));
}
void first_way()
{
qDebug() << "\nFirst way:";
QMap<QString, const char*> my_slots;
my_slots["id_slot1"] = "slot1()";
my_slots["id_slot2"] = "slot2()";
my_slots["id_slot3"] = "slot3()";
my_slots["id_slot4"] = "slot4(int)"; // slots with different signatures in the same container
MyClass object;
dynamicConnection(&object, "signal1()", &object, my_slots.value("id_slot1"));
dynamicConnection(&object, "signal1()", &object, my_slots.value("id_slot2"));
dynamicConnection(&object, "signal2()", &object, my_slots.value("id_slot3"));
dynamicConnection(&object, "signal3(int)", &object, my_slots.value("id_slot4"));
object.fireSignals();
}
<强> QT5 / C ++ 14:强>
使用Qt 5.5测试,mingw 4.9.2,-std = c ++ 14
这个是使用指向成员函数的指针。 它是类型安全的,因此您不能在同一容器中使用具有不同签名的插槽。
template <typename T, typename R, typename ...Args>
struct PointerToMemberHelper
{
using type = R (T::*)(Args...);
};
void second_way()
{
qDebug() << "\nSecond way:";
using MPTR_void = PointerToMemberHelper<MyClass, void>::type;
using MPTR_int = PointerToMemberHelper<MyClass, void, int>::type;
QMap<QString, MPTR_void> my_slots({{"id_slot1", MyClass::slot1},
{"id_slot2", MyClass::slot2},
{"id_slot3", MyClass::slot3}});
MyClass object;
QObject::connect(&object, MyClass::signal1, &object, my_slots.value("id_slot1"));
QObject::connect(&object, MyClass::signal1, &object, my_slots.value("id_slot2"));
QObject::connect(&object, MyClass::signal2, &object, my_slots.value("id_slot3"));
MPTR_int my_int_slot = MyClass::slot4; // or auto my_int_slot = ...
QObject::connect(&object, MyClass::signal3, &object, my_int_slot);
object.fireSignals();
}
目前正在尝试基于lambdas和QMetaObject::invokeMethod
获得第三个解决方案 - 但它不会比第二个解决方案做得更多。