什么是QMetaCallEvent以及如何访问其详细信息?

时间:2012-06-04 19:31:45

标签: qt events filter meta qevent

我有一个事件过滤器,当我点击展开/折叠树枝时,我注意到了QEvent::MetaCall。我正在考虑使用它来滚动我自己的展开/折叠代码,但我不知道如何获取任何信息,例如项目的索引。

这个MetaCall事件类型有什么可用的吗?

我发现有人在另一个网站上问了同样的问题,但没有回答,这里: http://www.qtcentre.org/threads/37525-How-to-filter-QEvent-MetaCall

此事件通常用于什么?

3 个答案:

答案 0 :(得分:8)

最大的问题是:你究竟想做什么?接受这些事件的Qt课程是什么?就我而言,你正在努力做事,为什么要这么做呢?

只要使用排队连接来调用插槽,QMetaCallEvent就是表示插槽调用的事件。这可能是由于连接到插槽的信号触发,或者是由于使用QMetaObject::invokeQMetaObject::invokeMethod。排队的连接位是重要的部分!对于同一线程中的对象之间的调用,缺省情况下,排队连接,因为它们具有事件队列管理开销,除非以下两个条件中的任何一个成立:

  1. 您向Qt::QueuedConnectionQObject::connect

  2. 提供QMetaObject::invoke[Method]参数
  3. 接听对象的thread()与发起呼叫的线程不同 - 在通话时。

  4. QMetaCallEvent事件类包含调用插槽所需的信息。它包含发送方QObject及其信号id(如果调用来自信号槽连接),以及目标槽标识符,以及需要传递到插槽的参数。

    因此,您可以检查被调用的插槽是否是您要拦截的插槽,以及传递给它的参数。例如,如果您使用单个int参数调用插槽,则*reinterpret_cast<int*>(metaCallEvent->args()[1])将为您提供该整数的值。第零个参数用于返回值(如果有),因此参数使用基数1进行索引。

    免责声明由于QMetaCallEvent类是Qt实现的内部类,因此您将应用程序的二进制文件与特定的Qt版本完全绑定(整个major.minor版本),并且您将丢失Qt在主要版本中提供的二进制兼容性的好处。当您切换到另一个次要版本的Qt时,您的代码仍然可以编译但停止正常工作!

    以下内容适用于Qt 5.2.0,我没有看过任何其他版本!

    因此,假设您要拦截对QLabel::setNum的调用。你会发现如下事件:

    #include <private/qobject_p.h> // Declaration of QMetaCallEvent
    
    bool Object::eventFilter(QObject * watched, QEvent * event) {
      QLabel * label = qobject_cast<QLabel*>(watched);
      if (! label || event->type() != QEvent::MetaCall) return false;
      QMetaCallEvent * mev = static_cast<QMetaCallEvent*>(event);
      static int setNumIdx = QLabel::staticMetaObject.indexOfSlot("setNum(int)");
      if (mev->id() != setNumIdx) return false;
      int num = *reinterpret_cast<int*>(mev->args()[1]);
      // At this point, we can invoke setNum ourselves and discard the event
      label->setNum(num);
      return true;
    }
    

    如果要全局查看使用metacall系统调用的所有插槽,您也可以这样做。基类的模板参数化允许灵活地使用任何应用程序类 - 比如QCoreApplicationQGuiApplicationQApplication或用户派生类型。

    template <class Base> class MetaCallWatcher : public Base {
      MetaCallWatcher(int& argc, char** argv) : Base(argc, argv) {}
      bool notify(QObject * receiver, QEvent * event) { 
        if (event->type() == QEvent::MetaCall) {
          QMetaCallEvent * mev = static_cast<QMetaCallEvent*>(event);
          QMetaMethod slot = receiver->metaObject()->method(mev->id());
          qDebug() << "Metacall:" << receiver << slot.methodSignature();
        }
        return Base::notify(receiver, event);
      }
    }
    
    int main(int argc, char ** argv) {
      MetaCallWatcher<QApplication> app(argc, argv);
      ...
    }
    

答案 1 :(得分:0)

只要发出连接到接收QEvent::MetaCall中的插槽的信号,就会创建QObject - 类型事件。在自定义过滤器/事件处理程序中对此事件做出反应似乎绕过了Qt最强大的功能 - 信号槽架构。最好找出哪个插槽被调用,如果该插槽是虚拟的,那么你可以重载它。

答案 2 :(得分:-2)

QEvent::MetaCall用于传递跨线程信号。