События и фильтры событий
В 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 - "Another Look at Events" - сжато переформулирует её. Изложенного здесь достаточно примерно для 95% приложений.
Обработчики событий
Обычный способ доставки события - вызов виртуальной функции. Например, QPaintEvent доставляется вызовом QWidget::paintEvent(). Эта виртуальная функция отвечает за соответствующее взаимодействие, обычно перерисовывая виджет. Если в своей реализации виртуальной функции вы не выполнили всю необходимую работу, вам может понадобиться вызывать реализацию из базового класса.
Например, следующий код обрабатывает щелчки левой кнопкой мыши на пользовательском виджете флажка в то время, как щелчки остальными кнопками передаются в базовый класс QCheckBox:
void MyCheckBox::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton) {
} else {
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) {
return true;
}
} else if (event->type() == MyCustomEventType) {
MyCustomEvent *myEvent = static_cast<MyCustomEvent *>(event);
return true;
}
return QWidget::event(event);
}
Обратите внимание на то, что QWidget::event() по-прежнему вызывается для всех не обрабатываемых случаев, и что возвращаемое значение служит признаком обработки события; значение true предотвращает отправку события другим объектам.
Фильтры событий
Иногда объекту нужно ознакомиться - и, возможно, перехватить - события, которые доставлены другому объекту. Например, диалогам обычно необходимо фильтровать нажатия клавиш для некоторых виджетов; например, для изменения обработки клавиши Return.
Функция 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) {
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.
Авторские права © 2010 Nokia Corporation и/или её дочерние компании |
Торговые марки |
Qt 4.6.4 |
|