Система событий
В 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/qt-6.2/eventsandfilters.html