Для чего нужен QMetaCallEvent и как получить доступ к его деталям?

У меня есть фильтр событий, и я заметил, что когда я нажимаю, чтобы развернуть/свернуть ветвь дерева, я получаю QEvent::MetaCall. Я думал об использовании этого для свертывания собственного кода развертывания/свертывания, но я не знаю, как получить какую-либо информацию, например индекс элемента.

Есть ли что-нибудь доступное для этого типа события MetaCall?

Я обнаружил, что кто-то задавал этот же вопрос на другом сайте, но без ответа, здесь: http://www.qtcentre.org/threads/37525-How-to-filter-QEvent-MetaCall

Для чего обычно используется это событие?


person Rafe    schedule 04.06.2012    source источник


Ответы (3)


Самый главный вопрос: Что именно вы пытаетесь сделать? Какой класс Qt получил эти события? Насколько я понимаю, вы пытаетесь сделать что-то сложным путем, так зачем беспокоиться?

QMetaCallEvent – это событие, представляющее вызов слота всякий раз, когда подключение в очереди используется для вызова слота. Это могло произойти из-за срабатывания сигнала, который был подключен к слоту, или из-за использования QMetaObject::invoke или QMetaObject::invokeMethod. Бит соединения в очереди является важной частью! Соединения в очереди не по умолчанию используются для вызовов между объектами в одном и том же потоке, поскольку они несут накладные расходы на управление очередью событий, за исключением случаев, когда выполняется одно из двух следующих условий:

  1. Вы предоставляете Qt::QueuedConnection аргумент для QObject::connect или QMetaObject::invoke[Method], или

  2. thread() принимающего объекта отличается от потока, из которого исходит вызов - во время вызова.

Класс событий QMetaCallEvent содержит информация, необходимая для вызова слота. Он содержит отправителя QObject и идентификатор его сигнала (если вызов исходит из соединения сигнал-слот), а также идентификатор целевого слота и аргументы, которые необходимо передать в слот.

Таким образом, вы можете проверить, является ли вызываемый слот тем, который вы хотите перехватить, а также какие аргументы ему были переданы. Например, если вы вызываете слот с одним параметром int, то *reinterpret_cast<int*>(metaCallEvent->args()[1]) даст вам значение этого целого числа. Нулевой аргумент используется для возвращаемого значения, если оно есть, поэтому параметры индексируются с основанием 1.

Отказ от ответственности Поскольку класс QMetaCallEvent является внутренним для реализации Qt, вы делаете двоичный файл вашего приложения полностью привязанным к конкретной версии Qt (вся основная.дополнительная версия) и вы теряете преимущества двоичной совместимости, предлагаемые 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);
  ...
}
person Kuba hasn't forgotten Monica    schedule 08.01.2014
comment
Не могу включить private/qobject_p.h, компилятор его не видит. - person Efog; 02.02.2015
comment
@Efog У вас должна быть полная установка Qt из исходников. В вашем случае источников нет, поэтому нет и приватных заголовков. - person Kuba hasn't forgotten Monica; 03.02.2015
comment
Это работает до тех пор, пока вы используете старые соединения сигнала/слота на основе строки. С указателями функций QMetaCallEvent.id() больше не содержит полезной информации. Это лучшее, что я смог выжать из новых соединений сигнал/слот: 2d4df2e46818d4e76fb7f7b8940c841a - person Simon Warta; 05.01.2017
comment
По какой-то причине я должен указать этот путь: #include <QtCore/5.6.0/QtCore/private/qobject_p.h>, чтобы получить частные заголовки. У меня есть подозрение, что это намеренно заставляет меня включить конкретную версию Qt из-за вышеупомянутых проблем совместимости. - person Tomáš Zato - Reinstate Monica; 20.04.2017
comment
Нет, вам не нужно этого делать. Добавьте QT += core_private в файл .pro и #include <private/qobject_p.h>, затем удалите папку сборки вашего проекта и пересоберите. Это сработает. Никогда не возникает вопроса о том, какую версию вы используете: текущую, с которой вы строите проект. Не стесняйтесь искать это в любом из моих ответов на stackoverflow на github, он отлично работает. - person Kuba hasn't forgotten Monica; 20.04.2017

Событие типа QEvent::MetaCall создается всякий раз, когда испускается сигнал, подключенный к слоту в принимающем QObject. Реакция на это событие в специальном обработчике фильтра/события, по-видимому, позволяет обойти самую мощную функцию Qt — архитектуру сигнальных слотов. Вероятно, лучше узнать, какой слот называется и является ли этот слот виртуальным, чтобы вы могли его перегрузить.

person arne    schedule 12.06.2012
comment
Спасибо за информацию. Чтобы принять ответ, я все же хотел бы знать, для чего он используется, возможно, в реальном примере, и есть ли что-нибудь доступное из типа QEvent. - person Rafe; 23.06.2012
comment
Неверно, что QMetaCallEvent создается всякий раз, когда испускается связанный сигнал. Это сделало бы механизм сигнального слота намного медленнее, чем необходимо. - person Kuba hasn't forgotten Monica; 08.01.2014

QEvent::MetaCall используется для доставки сигналов между потоками.

person Samoilenko Yuri    schedule 20.11.2013