Qt连接到插槽辅助接口

Qt connecting to slotted secondary interface

本文关键字:接口 插槽 连接 Qt      更新时间:2023-10-16

我的问题本质上是接口类中这个声明抽象信号的反面。 在该问题中,您持有指向槽 QObject 的指针,并尝试测试另一个对象的接口,该接口包含要连接到插槽对象的信号。

假设问题是相反的:你拿着一个指向发出信号的 QObject 的指针,你想测试一系列其他 QObject,看看它们是否实现了接收时隙。 如果是这样,您希望将它们全部连接到信号器。

我想使用的设计是,每个接收器都有一个主接口(QObject派生)和一个辅助插槽接口,我可以测试这些接口,即Q_DECLARE_INTERACE、qobject_cast等。

似乎辅助接口也必须是QObject派生的,否则您无法连接到它,但这打开了QObject的多重继承问题。

除了使用单个继承链之外,还有其他方法吗?

// receiver's primary interface
class IPrimary : public QObject
{
Q_OBJECT
public:
// etc.
};
// receiver's secondary interface with slot
class ISecondary : public QObject // QObject won't fly
{
Q_OBJECT  // nope
public slots:
void OnReceiptOfSomething(...);
public:
// etc.
};
// signaler
class Signaler : public QObject
{
Q_OBJECT
signals:
void SignalOfSomething(...);
public:
// etc.
};

我不完全确定我是否理解您的目标,但似乎给定QObject *emitter和信号名称signal_one您希望在第二个QObject *receiver中找到与signal_one兼容的所有插槽并自动创建所需的连接。 如果是这样,那么我认为仅使用Qt元数据基础设施是可能的 -QMetaObjectQMetaMethod等。

首先定义了一个函数are_compatible,该函数比较两个QMetaMethod实例的调用兼容性。

bool are_compatible (const QMetaMethod &signal_meta,
const QMetaMethod &slot_meta)
{
/*
* Work on the assumption that the arity of the signal must be at least that
* of the slot.
*/
if (signal_meta.parameterCount() < slot_meta.parameterCount())
return false;
/*
* Now check that all required parameters have the same type.
*/
for (int i = 0; i < slot_meta.parameterCount(); ++i) {
if (signal_meta.parameterType(i) != slot_meta.parameterType(i))
return false;
}
return true;
}

现在定义我们需要的主函数,该函数应自动将指定发射器中的命名信号连接到指定接收器中的所有兼容插槽。

void connect_where_possible (const char *signal_name,
QObject *emitter,
QObject *receiver)
{
const auto *emitter_meta_object = emitter->metaObject();
/*
* Look for signal_name in the emitter's metadata.
*/
auto index_of_signal = emitter_meta_object->indexOfSignal(signal_name);
if (index_of_signal == -1)
return;
/*
* Get the signal's associated QMetaMethod.
*/
const auto signal_meta_method = emitter_meta_object->method(index_of_signal);
/*
* Now go through the receiver's methods.  We could naively attempt to
* connect to each and every slot knowing that the Qt runtime will only
* succeed when the signal and slot are compatible.  A nicer/cleaner
* implementation is to use the metadata available to check for
* compatibility _before_ attempting to connect -- if only to avoid unwanted
* warning messages on the console.
*/
const auto *receiver_meta_object = receiver->metaObject();
for (int method_index = 0; method_index < receiver_meta_object->methodCount(); ++method_index) {
const auto receiver_slot_method = receiver_meta_object->method(method_index);
if (receiver_slot_method.methodType() == QMetaMethod::Slot) {
/*
* Found a slot so check it's compatibility and, if ok, try to connect.
*/
if (are_compatible(signal_meta_method, receiver_slot_method)) {
QObject::connect(emitter, signal_meta_method, receiver, receiver_slot_method);
}
}
}
}

通过一个例子,尝试以下代码...

class signaler: public QObject {
Q_OBJECT;
signals:
void sig1(int i, float f);
};
class receiver: public QObject {
Q_OBJECT;
public slots:
void slot1 (int i, float f) const
{
std::cerr << "nreceiver::slot1(i = " << i << ", f = " << f << ")";
}
void slot2 (int i) const
{
std::cerr << "nreceiver::slot2(i = " << i << ")";
}
void slot3 (float f) const
{
std::cerr << "nreceiver::slot3(f = " << f << ")";
}
};
...
signaler signaler;
receiver receiver;
connect_where_possible(QMetaObject::normalizedSignature("sig1(int, float)"), &signaler, &receiver);
signaler.sig1(10, 20.0f);

我看到输出...

receiver::slot1(i = 10, f = 20)
receiver::slot2(i = 10)

因此,正如预期的那样,receiver::slot1receiver::slot2是连接的,但receiver::slot3不是因为它被认为是不兼容的。