最大的问题是:你到底想做什么?接收这些事件的 Qt 类是什么? 就我而言,你正在努力做事,那何必呢?
QMetaCallEvent 是表示每当使用排队连接 调用槽时的槽调用的事件。这可能是由于连接到插槽的信号触发,或者由于使用了QMetaObject::invoke 或QMetaObject::invokeMethod。排队的连接位是重要的部分!默认情况下,队列连接不用于同一线程中的对象之间的调用,因为它们具有事件队列管理开销,除非以下两个条件之一成立:
您将Qt::QueuedConnection 参数提供给QObject::connect 或QMetaObject::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;
}
如果您想在全局范围内查看使用元调用系统调用的所有插槽,您也可以这样做。基类的模板参数化允许灵活地使用任何应用程序类 - 例如QCoreApplication、QGuiApplication、QApplication 或用户派生类型。
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);
...
}