Spec-Zone.ru › Qt 6.0

Система событий

В 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.0/eventsandfilters.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API