Wiki

Ранние "сюрпризы"

перевод Racheengel

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

Объект может отслеживать события другого объекта, используя QObject::installEventFilter():

targetObj->installEventFilter( monitorObj );

После такого вызова, все события, посланные целевому объекту, вначале будут проходить через виртуальную функцию объекта-"наблюдателя" eventFilter(). Если целевым объектом является qApp, функцией eventFilter() "наблюдателя" будут перехвачены события для всех объектов приложения.

Данная техника может быть использована для создания класса EasterEgg, который поможет Вам скрывать "сюрпризы" в вашем приложении. Например, после создания объекта EasterEgg в примере ниже, слот doSomething() объекта myWin будет вызван, как только пользователь наберет "волшебное слово" 'TEKEHTOPA':

EasterEgg egg1( "TEKEHTOPA", myWin, SLOT(doSomething()) );

Приведем шаг за шагом код для класса EasterEgg, начиная с его объявления..

class EasterEgg : public QObject
{
	Q_OBJECT
public:
	EasterEgg( const char *magicWord, QObject *target, const char *slot );
 
	virtual bool eventFilter( QObject *obj, QEvent *event );
 
signals:
	void found();
 
private:
	QCString magic;
	int j;
};

Класс EasterEgg наследует QObject и переопределяет Object::eventFilter(). Частный член данных magic хранит "волшебное слово", а j хранит количество набранных пользователем символов. Например, если в magic хранится 'TEKEHTOPA' и пользователь уже набрал 'TEKE', то j имеет значение 4. Сигнал found() активируется, когда пользователь набрал "волшебное слово" целиком.

EasterEgg::EasterEgg( const char *magicWord, 
                      QObject *target, const char *slot )
: magic( magicWord ), j( 0 )
{
	qApp->installEventFilter( this );
	connect( this, SIGNAL(found()), target, slot );
}

Конструктор инициализирует частные члены данных, инсталлирует объект EasterEgg в качестве "наблюдателя" за событиями приложения, и присоединяет found() к слоту, переданному как параметр.

bool EasterEgg::eventFilter( QObject *obj, QEvent *event )
{
	if ( event->type() == QEvent::KeyPress ) {
		QKeyEvent *keyEvent = (QKeyEvent *) event;
		int key = keyEvent->key();
		if ( magic[j] == key ) {
			j++;
			if ( j == magic.length() ) {
				emit found();
				j = 0;
			}
		} else {
			int right = j;
			while ( j > 0 ) {
				if ( magic[j - 1] == key &&
					 magic.left(j - 1) == magic.mid(right - j + 1, j - 1) )
						break;
				j--;
			}
		}
	}
	return FALSE;
}

Реализация QObject::eventFilter() обновляет j в соответствии с нажатой клавишей. Если нажата ожидаемая клавиша, j увеличивается на единицу, иначе, j уменьшается. Сигнал found() активируется, когда "волшебное слово" введено полностью, и j при этом сбрасывается в 0. В конце функции, мы возвращаем FALSE, сообщая, что событие должно быть передано его целевому объекту.

Код, уменьшающий j (ветвь 'else' вышеприведенного 'if'), "хитрый". На первый взгляд, естественно бы было сбросить j в 0, но это не всегда будет правильным. Например, если набрано 'TEKT', j должен быть равен 1, а не 0, так как последняя T может быть первой T в слове 'TEKEHTOPA'. Если "волшебным словом" является 'OOP', j равен 2, когда пользователь набрал 'OO' и должно оставаться равным 2, когда нажата третья 'O'.

Преимущество данного подхода в том, что он не требует наследования от QApplication и позволяет иметь несколько "сюрпризов" с различными "волшебными словами" одновременно. Его главное ограничение в том, что распознаются только факты нажания клавишей, но при этом не существует никаких фундаментальных отличий от других типов событий.

"Сюрпризы" - одна из наиболее известных традиций в компьютерной науке наряду с "взломом" видеоигр. Класс EasterEgg может быть использован в обеих случаях. Если Вы хотите узнать больше о сюрпризах, включая скрытую игру 'Spy Hunter' в Microsoft Excel 2000, смотрите Архив Сюрпризов: www.eeggs.com.

Если Вы хотите узнать больше про фильтры событий, прочтите документацию по QApplication::notify() и События и Фильтры.

Copyright © 2002 Trolltech. Trademarks