如何将 Qt 信号/插槽名称存储到数据结构中?
Posted
技术标签:
【中文标题】如何将 Qt 信号/插槽名称存储到数据结构中?【英文标题】:How to store Qt signal/slot names to a data structure? 【发布时间】:2015-08-05 15:04:34 【问题描述】:我想将信号或槽的名称存储到容器中,以便我可以检索它并与之建立连接。
一种方法是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 的解决方案虽然是受欢迎的,但对我来说用处不大。
【问题讨论】:
您只需要使用旧语法的第一种方式,只需在每个插槽的名称中添加“1”即可。 @Chernobyl 其实更容易做到connect(aQObject, SLOT(aSlotOfThatQObject));
我有 Qt5,但我想我找到了解决方案。但是,它假定您的插槽具有签名 void mySlot();
;有问题吗?
@Armaghast 您可以假设签名相同或兼容。
【参考方案1】:
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
对象。这种方法更安全,因为检查槽是否存在更早,但在创建QMap
之前需要有一个MyClass
instance,而存储槽名称更灵活。
// 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
获得第三种解决方案,但它不会比第二种解决方案做得更多。
【讨论】:
您的第一个解决方案比我基于字符串的方法更复杂,但它演示了如何获取插槽的QMetaMethod
表示,这比QMetaMethod
强类型的字符串更好。此外,基于字符串的方法在连接发生之前无法检测到错误,QMetaMethod
方法可以在查找插槽时检测到错误。
我对模板不是很熟悉。你能在你的PointerToMemberHelper
上解释更多吗?
我想我在这里找到了答案 ***.com/questions/10747810/… 。 using
在这种情况下类似于 typedef
。
确实,使QMetaMethod
更早更安全,尽管基于字符串的方法允许您在没有MyClass
实例的情况下存储槽,并允许一些更强大(也更危险)的绑定,例如构造插槽名称dynamicConnection( [...], QString("slot%1()").arg(an_int))
。我将编辑我的答案以反映这一点。以上是关于如何将 Qt 信号/插槽名称存储到数据结构中?的主要内容,如果未能解决你的问题,请参考以下文章