Компания ALT Linux
Опубликован: 07.03.2015 | Доступ: свободный | Студентов: 2135 / 487 | Длительность: 24:14:00
Лекция 14:

Собственные классы в Qt. Создание элементов графического интерфейса

14.4 Фильтры событий (Event filters). Распространение (всплытие) событий (Event propagation)

Когда цикл обработки посылает событие объекту-адресату, он ожидает, что объект соответствующим образом обработает событие или сообщит о том, что он не будет выполнять обработку. Во втором случае некоторые события могут быть распространены (propagated), то есть переданы на обработку к родительскому объекту, если дочерний объект проигнорировал событие. Примером событий, для которых работает распространение — это события мышки и нажатий клавиш клавиатуры. Они будут "всплывать" по объектной иерархии от дочернего объекта к родительскому, пока один из этих объектов не обработает событие или все не проигнорируют его.

Для управления этим процессом используют методы QEvent::accept() (для обозначения события как обработанного) и QEvent::ignore() (для игнорирования события). Их используют только в виртуальных методах-обработчиках. Обычно QEvent::accept() явно не вызывают, поскольку большинство распространяемых событий обозначаются как обработанные по умолчанию.

Несмотря на гибкую систему контроля обработки событий, иногда возникает потребность выполнить обработку вместо объекта-адресата. Также иногда возникает необходимость заблокировать (отфильтровать) некоторые события, которые поступают на обработку к объекту. Этого можно достичь с помощью фильтров событий (event filters).

Фильтры событий являются обычными объектами, унаследованными от QObject. Но перед тем, как событие поступит к адресату, оно поступает к фильтру событий. Он может передать его дальше на обработку адресату, отсеять или выполнить необходимые действия в ответ. Для обработки событий, фильтру необходимо переопределить виртуальный метод QObject::eventFilter(). Этот метод возвращает логическое значение, которое означает, будет ли событие отфильтровано (значение true) или поступит на дальнейшую обработку (значение false). Фильтр событий для объекта можно задать с помощью метода QObject::installEventFilter(), который принимает указатель на QObject, выступающий в роли фильтра.

Наш предыдущий пример содержит поле ввода с пиктограммой. Для того чтобы реализовать реакцию на нажатие клавиши мыши на пиктограмму, но не создавать своего класса, унаследованного от класса метки с пиктограммой, мы добавим фильтр событий, который будет посылать сигнал, если для пиктограммы поступит событие типа QEvent::MouseButtonPress. Добавим объявление класса

QEvent: #include <QEvent>

Установим фильтр событий для метки: mIconLabel->installEventFilter(this);

И добавим фильтр событий для метки в файле IconizedLineEdit.h:

class IconizedLineEdit : public QLineEdit
{
	Q_OBJECT
public :
.....
	void setIconClickable ( bool pIsIconClickable );
.....
signals :
	void iconPressed ( );
protected :
.....
	bool eventFilter ( QObject *pobject, QEvent *pEvent );
.....
private :
.....
	bool mIsIconCklickable;
};

В файле IconizedLineEdit.cpp:

bool IconizedLineEdit::eventFilter ( QObject *pobject, QEvent *pEvent )
{
	if ( mIsIconCklickable )
	{
		if ( ( pobj e ct==mIconLabel ) && ( pEvent->type ( )== QEvent::MouseButtonPress ) )
		{
			emit iconPressed ( );
			return true;
		}
	}
return false;
}
//Установить режим реакции на нажатие мышкой на пиктограмму
void IconizedLineEdit::setIconClickable ( bool pIsIconClickable )
{
	mIsIconCklickable = pIsIconClickable;
	//Устанавливаем вид курсора при наведении на метку с пиктограммой
	if ( mIsIconCklickable )
	{
		mIconLabel->setCursor (Qt::PointingHandCursor );
	}
	else
	{
		mIconLabel->setCursor (Qt::ArrowCursor );
}
}

Наш класс для поля ввода с пиктограммой одновременно выступает фильтром событий для метки.

Добавим реакцию на нажатие к двум полям в mainwindow.cpp. Первое поле будет открывать диалог выбора файла и записывать путь к файлу. Последнее поле будет стирать своё содержимое в ответ на нажатие пиктограммы:

#include <QFileDialog>
.....
void MainWindow::createUi ( )
{
.....
IconizedLineEdit ->setIconClickable ( true );
connect ( IconizedLineEdit, SIGNAL( iconPressed ( ) ), this, SLOT( slotChooseFile ( ) ),
	Qt::UniqueConnection );
.....
iconizedLineEdit_5 ->setIconClickable ( true );
connect ( iconizedLineEdit_5, SIGNAL( iconPressed ( ) ),
iconizedLineEdit_5, SLOT( clear ( ) ), Qt::UniqueConnection );
.....
}
void MainWindow::slotChooseFile ( )
{
QString lFileName = QFileDialog::getOpenFileName ( this, " open File " );
IconizedLineEdit ->setText ( lFileName );
}

Объявления слота slotChooseFile() добавим к описанию класса главного окна:

class MainWindow : public QWidget
{
	Q_OBJECT
..... .
private slots :
	void slotChooseFile ( );
..... .
};

После этого наш пример окончательно готов (см. рис. 14.2).

Пример: поле ввода с пиктограммой.

Рис. 14.2. Пример: поле ввода с пиктограммой.

Просуммируем наши знания об обработке событий в Qt:

  • события дают возможность проводить обработку различных ситуаций, возникающих во время работы программы;
  • есть два источника поступления событий. Спонтанные события поступают от оконной системы в ответ на действия пользователя (движения мышкой, нажатия клавиш клавиатуры, действия с окном программы и т. д.). События также возникают внутри самой программы и поступают от объектов, которые участвуют в работе программы;
  • события направляются на обработку объектам, участвующим в работе программы. За сбор и перенаправление событий к обработчикам ответственным является цикл обработки событий в программе;
  • каждое событие существует в виде объекта типа QEvent. Для большинства событий существуют специализированные классы этих событий, унаследованные от QEvent, которые содержат информацию о событии и данные необходимые для её корректной обработки;
  • для участия в обработке событий объект должен быть унаследован от класса QObject. События, которые поступают к объекту на обработку, автоматически передаются к виртуальному методу QObject::event();
  • для удобства, для большинства событий существуют отдельные виртуальные методы обработки, которые вызываются виртуальным методом QObject::event();
  • объекты имеют возможность быть фильтрами событий для других объектов в приложении. Для того, чтобы установить объект в качестве фильтра событий, пользуются методом installEventFilter(). Объект-фильтр должен переопределить виртуальный метод QObject::eventFilter(), который возвращает логическое значение, было событие отфильтровано (то есть, оно не будет направляться на обработку) или нет.

А вот общий алгоритм для программной обработки событий:

  • в случае, когда нет возможности унаследовать класс объекта и переопределить методы обработки, а также в случае, когда необходимо фильтровать, блокировать или предварительно обработать событие перед тем, как она будет отправлена на обработку к объекту — воспользуйтесь фильтром событий
    • переопределите виртуальный метод QObject::eventFilter() для объекта, который будет выступать в роли фильтра событий;
    • в методе eventFilter() проверьте объект к которому на обработку будет направляться событие;
    • дальше, определите тип события (QEvent::Type) и класс события, проверьте является ли полученное событие событием нужного вам типа;
    • приведите объект события до нужного вам класса событий;
    • выполните предварительную обработку события, если необходимо. Для того, чтобы отфильтровать событие (после этого оно не будет передаваться объекту на обработку) верните из метода логическое значение true. Для того, чтобы отправить событие на обработку объекту- адресату верните логическое значение false.
  • для некоторых типов событий, которые не имеют отдельного метода- обработчика, обработку можно запрограммировать, переопределив метод QObject::event()
    • переопределите виртуальный метод для объекта QObject::event(), который должен выполнять обработку;
    • определите тип события (QEvent::Type) и класс события, проверьте является ли полученное событие событием нужного вам типа;
    • приведите объект события до нужного вам класса событий;
    • выполните обработку или вызовите метод, который её выполнит, верните значение true, которое означает, что событие распознано и обработано, или false в случае, если объект не будет выполнять обработку. В этом случае событие может быть переслано на обработку родительскому объекту.
    • вызовите метод event() для базового класса, чтобы провести обработку всех остальных видов событий. Верните из метода значение, которое вернёт метод event() базового класса после выполнения.
  • если объект имеет виртуальный метод-обработчик события, используйте его
    • переопределите виртуальный метод для объекта QObject::event(), который должен выполнять обработку;
    • выполните обработку события. Вызовите метод accept() для события, если во время обработки убедились, что событие должно быть проработано данным объектом. В противном случае, вызовите ignore() для объекта события (в этом случае событие может быть направлено к родительскому объекту на обработку)
    .
Сергей Радыгин
Сергей Радыгин

Символы кириллицы выводит некорректно. Как сделать чтобы выводился читабельный текст на русском языке?

Тип приложения - не Qt,

Qt Creator 4.5.0 основан на Qt 5.10.0. Win7.

 

Юрий Герко
Юрий Герко

Кому удалось собрать пример из раздела 13.2 Компоновка (Layouts)? Если создавать проект по изложенному алгоритму, автоматически не создается  файл mainwindow.cpp. Если создавать этот файл вручную и добавлять в проект, сборка не получается - компилятор сообщает об отсутствии класса MainWindow. Как правильно выполнить пример?