Ранние "сюрпризы"
Материал из Wiki.crossplatform.ru
Qt Quarterly | Выпуск 2 | Документация |
перевод 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() и События и Фильтры.