Spec-Zone.ru › Qt 5.6

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

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

Spec-Zone.ru

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