【问题标题】:Qt connecting to slotted secondary interfaceQt 连接到开槽辅助接口
【发布时间】:2019-03-01 08:09:23
【问题描述】:

我的问题本质上与Declare abstract signal in interface class 正好相反。在那个问题中,你持有一个指向开槽 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.
};

【问题讨论】:

  • QMetaObjectQMetaMethod 中是否已经有足够的信息可以让您做您想做的事?虽然我真的不确定你为什么要这样做。
  • 元系统可以让我看到槽方法的存在,当然,但我认为如果没有编译时已知的接口,我不能编写 connect() 语句。插槽对象是插件小部件,每个小部件在收到信号后都会做不同的事情,我希望它们的主界面独立于插槽。我认为阻力最小的路径是将插槽接口放置在单行类层次结构中的某个位置,而不是使用第二个接口和多重继承。

标签: c++ qt


【解决方案1】:

我不完全确定我了解您的需求,但似乎给定 QObject *emitter 和信号名称 signal_one 您希望在第二个 QObject *receiver 中找到与 @987654324 兼容的所有插槽@ 并自动创建所需的连接。如果是这样,那么我认为仅使用 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 不是因为它被认为不兼容。

【讨论】:

  • 这是一个非常有用的响应,展示了 Qt 内省。我想我可以更简单地询问对象“您是否有一个名为 OnReceiptOfSomething 的插槽”并类似地​​连接它,而不是寻找您概述的签名兼容性。
猜你喜欢
  • 2016-03-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-17
  • 1970-01-01
  • 2012-12-15
  • 2012-10-15
相关资源
最近更新 更多