Система событий
В Qt события — это объекты, производные от абстрактного класса QEvent, представляющие собой произошедшие события внутри приложения или в результате внешней активности, о которой приложение должно знать. События могут быть получены и обработаны любым экземпляром подкласса QObject, но они особенно важны для виджетов. В этом документе описывается, как события доставляются и обрабатываются в типичном приложении.
Доставка событий
Когда происходит событие, Qt создаёт объект события, представляющий его, создавая экземпляр соответствующего подкласса QEvent, и доставляет его конкретному экземпляру QObject (или одному из его подклассов), вызывая его функцию event().
Эта функция сама не обрабатывает событие; в зависимости от типа доставленного события она вызывает обработчик событий для этого конкретного типа события и отправляет ответ, основанный на том, было ли событие принято или проигнорировано.
Некоторые события, такие как QMouseEvent и QKeyEvent, поступают от системного окна; некоторые, такие как QTimerEvent, поступают из других источников; некоторые — от самого приложения.
Типы событий
Большинство типов событий имеют специальные классы, в частности, QResizeEvent, QPaintEvent, QMouseEvent, QKeyEvent и QCloseEvent. Каждый класс является подклассом QEvent и добавляет функции, специфичные для события. Например, QResizeEvent добавляет size() и oldSize(), чтобы виджеты могли узнать, как изменились их размеры.
Некоторые классы поддерживают более одного фактического типа события. QMouseEvent поддерживает нажатия кнопок мыши, двойные щелчки, перемещения и другие связанные операции.
Каждое событие имеет связанный тип, определённый в QEvent::Type, и это можно использовать в качестве удобного источника информации о типе во время выполнения, чтобы быстро определить, из какого подкласса был создан данный объект события.
Поскольку программы должны реагировать разнообразными и сложными способами, механизмы доставки событий Qt гибкие. Документация по QCoreApplication::notify() кратко рассказывает всю историю; статья из Qt Quarterly Ещё раз о событиях пересказывает её менее лаконично. Здесь мы объясним достаточно для 95% приложений.
Обработчики событий
Обычный способ доставки события — вызов виртуальной функции. Например, QPaintEvent доставляется путём вызова QWidget::paintEvent(). Эта виртуальная функция отвечает за соответствующую реакцию, обычно перерисовывая виджет. Если вы не выполнили всю необходимую работу в своём реализации виртуальной функции, вам может потребоваться вызвать реализацию базового класса.
Например, следующий код обрабатывает нажатия левой кнопки мыши на пользовательском виджете флажка, передавая все остальные нажатия кнопок базовому классу QCheckBox:
void MyCheckBox::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
// handle left mouse button here
} else {
// pass on other buttons to base class
QCheckBox::mousePressEvent(event);
}
} Если вы хотите заменить функцию базового класса, вы должны реализовать всё сами. Однако, если вы хотите только расширить функциональность базового класса, тогда вы реализуете то, что вам нужно, и вызовете базовый класс, чтобы получить стандартное поведение для любых случаев, которые вы не хотите обрабатывать.
Иногда такой функции, специфичной для события, не существует или её недостаточно. Наиболее распространённый пример — нажатия клавиши Tab. Обычно QWidget перехватывает их, чтобы переместить фокус клавиатуры, но некоторые виджеты нуждаются в клавише Tab для себя.
Эти объекты могут переопределить QObject::event(), общий обработчик событий, и либо обработать своё событие до или после обычной обработки, либо полностью заменить функцию. Очень необычный виджет, который интерпретирует Tab и имеет прикладное пользовательское событие, может содержать следующую функцию event():
bool MyWidget::event(QEvent *event)
{
if (event->type() == QEvent::KeyPress) {
QKeyEvent *ke = static_cast<QKeyEvent *>(event);
if (ke->key() == Qt::Key_Tab) {
// special tab handling here
return true;
}
} else if (event->type() == MyCustomEventType) {
MyCustomEvent *myEvent = static_cast<MyCustomEvent *>(event);
// custom event handling here
return true;
}
return QWidget::event(event);
} Обратите внимание, что QWidget::event() всё ещё вызывается для всех необработанных случаев, и что возвращаемое значение указывает, было ли событие обработано; значение true предотвращает отправку события другим объектам.
Фильтры событий
Иногда объекту нужно посмотреть на события, которые доставляются другому объекту, и, возможно, перехватить их. Например, диалоги обычно хотят фильтровать нажатия клавиш для некоторых виджетов; например, чтобы изменить обработку клавиши Возврат.
Функция QObject::installEventFilter() позволяет это сделать, создавая фильтр событий, заставляя назначенный объект-фильтр получать события для целевого объекта в своей функции QObject::eventFilter(). Фильтр событий может обработать события до целевого объекта, позволяя ему проверять и отбрасывать события по мере необходимости. Существующий фильтр событий может быть удалён с помощью функции QObject::removeEventFilter().
Когда вызывается реализация функции eventFilter() объекта-фильтра, он может принять или отклонить событие и разрешить или запретить дальнейшую обработку события. Если все фильтры событий разрешают дальнейшую обработку события (возвращая false), событие отправляется самому целевому объекту. Если один из фильтров прекращает обработку (возвращая true), целевой объект и любые последующие фильтры событий вообще не видят событие.
bool FilterObject::eventFilter(QObject *object, QEvent *event)
{
if (object == target && event->type() == QEvent::KeyPress) {
QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
if (keyEvent->key() == Qt::Key_Tab) {
// Special tab handling
return true;
} else
return false;
}
return false;
} Приведённый выше код демонстрирует ещё один способ перехвата событий нажатия клавиши Tab, отправленных конкретному целевому виджету. В этом случае фильтр обрабатывает соответствующие события и возвращает true для предотвращения их дальнейшей обработки. Все остальные события игнорируются, и фильтр возвращает false для разрешения отправки их целевому виджету через любые другие установленные на нём фильтры событий.
Также можно фильтровать все события для всего приложения, установив фильтр событий на объект QApplication или QCoreApplication. Такие глобальные фильтры событий вызываются до объектно-специфических фильтров. Это очень мощно, но также замедляет доставку каждого события во всём приложении; другие обсуждаемые методы обычно следует использовать вместо этого.
Отправка событий
Многие приложения хотят создавать и отправлять свои собственные события. Вы можете отправлять события точно так же, как и собственный цикл событий Qt, создавая соответствующие объекты событий и отправляя их с помощью QCoreApplication::sendEvent() и QCoreApplication::postEvent().
sendEvent() обрабатывает событие немедленно. По возвращении фильтры событий и/или сам объект уже обработают событие. Для многих классов событий есть функция isAccepted(), которая сообщает вам, было ли событие принято или отклонено последним вызванным обработчиком.
postEvent() помещает событие в очередь для последующей отправки. В следующий раз, когда цикл основных событий Qt запускается, он обрабатывает все опубликованные события с некоторыми оптимизациями. Например, если есть несколько событий изменения размеров, они сжимаются в одно. То же самое относится к событиям рисования: QWidget::update() вызывает postEvent(), что устраняет мерцание и повышает скорость, избегая многократного перерисовки.
postEvent() также используется во время инициализации объектов, поскольку опубликованное событие, как правило, отправляется сразу после завершения инициализации объекта. При реализации виджета важно понимать, что события могут быть доставлены очень рано в его жизни, поэтому в его конструкторе обязательно инициализируйте члены переменных на раннем этапе, прежде чем он может получить какое-либо событие.
Чтобы создать события пользовательского типа, вам нужно определить номер события, который должен быть больше QEvent::User, и вам может потребоваться создать подкласс QEvent для передачи определённой информации о вашем пользовательском событии. Дополнительные сведения см. в документации к QEvent.
© The Qt Company Ltd
Licensed under the GNU Free Documentation License, Version 1.3.
https://doc.qt.io/archives/qt-5.11/eventsandfilters.html