QMetaCallEvent 的用途是啥以及如何访问其详细信息?

Posted

技术标签:

【中文标题】QMetaCallEvent 的用途是啥以及如何访问其详细信息?【英文标题】:What's the QMetaCallEvent for and how to access its details?QMetaCallEvent 的用途是什么以及如何访问其详细信息? 【发布时间】:2012-06-04 19:31:44 【问题描述】:

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

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

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

这个事件通常用于什么?

【问题讨论】:

【参考方案1】:

最大的问题是:你到底想做什么?接收这些事件的 Qt 类是什么? 就我而言,你正在努力做事,那何必呢?

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

    您将Qt::QueuedConnection 参数提供给QObject::connectQMetaObject::invoke[Method]

    接收对象的 thread() 与发起调用的线程不同 - 在调用时。

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;

如果您想在全局范围内查看使用元调用系统调用的所有插槽,您也可以这样做。基类的模板参数化允许灵活地使用任何应用程序类 - 例如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);
  ...

【讨论】:

我不能包含private/qobject_p.h,编译器看不到它。 @Efog 您必须从源代码完成 Qt 的完整安装。在您的情况下,源不存在,因此私有标头也不存在。 只要您使用旧的基于字符串的信号/插槽连接,它就可以工作。使用函数指针,QMetaCallEvent.id() 不再包含有用的信息。这是我可以从新的信号/插槽连接中挤出的最好的:gist.github.com/webmaster128/2d4df2e46818d4e76fb7f7b8940c841a 出于某种原因,我必须包含此路径:#include &lt;QtCore/5.6.0/QtCore/private/qobject_p.h&gt; 以获取“私有”标头。由于上述兼容性问题,我怀疑这是故意强迫我包含特定的 Qt 版本。 不,您不必这样做。将QT += core_private添加到.pro文件和#include &lt;private/qobject_p.h&gt;,然后删除你的项目的build文件夹并重建。它会起作用的。从来没有关于您使用什么版本的问题:您构建项目的当前版本。随意在我在 github 上的任何 *** 答案中寻找这个包含,它工作得很好。【参考方案2】:

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

【讨论】:

感谢您提供的信息。要接受答案,我仍然想知道它的用途,也许是在一个真实世界的例子中,以及 QEvent 类型是否有任何可用的东西。 每当发出连接的信号时,就会创建 QMetaCallEvent 是错误的。这会使信号槽机制比必要的慢得多。【参考方案3】:

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

【讨论】:

以上是关于QMetaCallEvent 的用途是啥以及如何访问其详细信息?的主要内容,如果未能解决你的问题,请参考以下文章

元表是如何工作的,它们的用途是啥?

Node.js module.exports 的用途是啥,你如何使用它?

Node.js module.exports 的用途是啥,你如何使用它?

何时使用 @JsonProperty 属性以及它的用途是啥?

PromQL:rate() 函数的用途是啥?

系统表 master..spt_values 的用途是啥,其值的含义是啥?