На этом шаге рассмотрим возможность искусственного создания событий из самой программы.
Иногда возникает необходимость в событиях, созданных искусственно. Например, это полезно при отладке вашей программы, для того чтобы имитировать действия пользователя.
Для генерации события можно воспользоваться одним из двух статических методов класса QCoreApplication — sendEvent() или postEvent(). Оба метода получают в качестве параметров указатель на объект, которому посылается событие, и адрес объекта события. Разница между ними состоит в том, что метод sendEvent() отправляет событие без задержек, т. е. его вызов приводит к немедленному вызову метода события, в то время как метод postEvent() помещает его в очередь для дальнейшей обработки.
Рассмотрим это на примере приложения, имитирующего нажатие пользователем клавиш от <0> до <9> (рис. 1).
Рис.1. Программа, демонстрирующая подмену события клавиатуры
Приведем содержимое файла приложения, имитирующего события клавиатуры.
QApplication app (argc, argv); app.setApplicationDisplayName("Имитация"); //Создается объект txt класса QLineEdit, //который будет выступать в качестве поля ввода QLineEdit txt("Пользователь ввел: "); txt.show(); txt.resize(280, 20); int i; for (i = 0; i < Qt::Key_9 - Qt::Key_0 + 1; ++i) { QChar ch = 48 + i; int nKey = Qt::Key_0 + i; //В цикле происходит создание событий типа QKeyEvent. //Первый параметр, передаваемый конструктору, задает тип события клавиатуры //(здесь он соответствует событию нажатия клавиши клавиатуры QEvent::KeyPress). //Второй параметр задает саму нажатую клавишу. //Третий — указывает на клавиши-модификаторы, которые могли быть совместно //нажаты, в нашем случае это значение равно Qt::NoModifier и означает, //что никаких клавиш-модификаторов нажато не было. //Четвертый параметр указывает на представление клавиши в ASCII-коде //(в примере это число начинается с 48, что соответствует цифре "0", //и циклически увеличивается на единицу) QKeyEvent* pePress = new QKeyEvent(QEvent::KeyPress, nKey, Qt::NoModifier, ch); QApplication::sendEvent(&txt, pePress); //Если хотим имитировать клавиатуру, то после каждого события KeyPress //должно следовать событие KeyRelease (событие отпускания клавиши клавиатуры) //в противном случае много виджетов, которым будет послано только одно событие //нажатия, будут вести себя неправильно, например в QLineEdit //перестает мигать курсор ввода QKeyEvent* peRelease = new QKeyEvent(QEvent::KeyRelease, nKey, Qt::NoModifier, ch); QApplication::sendEvent(&txt, peRelease); }
Файлы приложения можно взять здесь.
Если бы нам понадобилось симулировать нажатие мыши на какой-либо виджет, то реализация функций для этого могла бы выглядеть следующим образом:
//Первым параметром принимаем указатель на виджет, с которым мы хотим //провести симуляцию нажатия, вторым и третьим идут координаты позиции, //в которой было выполнено нажатие. //Четвертый и пятый параметры не обязательные и по умолчанию инициализируются //нажатием на левую кнопку, что является самым частым действием при нажатии void mousePress(QWidget* pwgt, int x, int y, Qt::MouseButton bt = Qt::LeftButton, Qt::MouseButtons bts = Qt::LeftButton ) { //Внутри функции проверяем действительность указателя на объект виджета, //после чего создаем объект события QmouseEvent, инициализируем переданными //в функцию параметрами if (pwgt) { QMouseEvent* pePress = new QMouseEvent(QEvent::MouseButtonPress, QPoint(x, y), bt, bts, Qt::NoModifier ); //пересылаем событие виджету (указатель pwgt) вызовом метода postEvent() QApplication::postEvent(pwgt, pePress); } }
Модифицировать объекты событий возможно не всегда. При совместном создании искусственных событий с фильтрами можно осуществить подмену самих объектов событий. В качестве показательного примера использования подобного перехвата события с целью его подмены можно назвать изменение назначения клавиш клавиатуры. Так как класс события клавиатуры не обладает методами, позволяющими его модифицировать, то каждое сообщение клавиатуры можно получить в объекте фильтра и перед передачей дальше подменить его другим.
В следующем примере происходит подмена клавиши <9> на клавишу <0> (рис. 2). Таким образом, нажатие пользователем клавиши <9> повлечет за собой отображение цифры 0 в поле ввода. Таким образом можно, например, имитировать измененную раскладку клавиатуры.
Рис.2. Программа, демонстрирующая подмену символа
Рассмотрим текст заголовочного файла:
class KeyFilter : public QObject { protected: bool eventFilter(QObject* pobj, QEvent* pe) { //в методе eventFilter() отслеживается идентификатор события QEvent::KeyPress, //который соответствует событию нажатия клавиши клавиатуры. if (pe->type() == QEvent::KeyPress) { //При обнаружении этого события его объект преобразовывается к типу //QKeyEvent, чтобы иметь возможность вызова метода key(), который //определен в этом классе и позволяет получить код нажатой клавиши. if (((QKeyEvent*)pe)->key() == Qt::Key_9) { //Затем создается и высылается новое событие нажатия клавиши <0>. QKeyEvent* pe = new QKeyEvent(QEvent::KeyPress, Qt::Key_0, Qt::NoModifier, "0" ); QApplication::sendEvent(pobj, pe); //После этого возвращается значение true, и это означает, //что событие не должно передаваться дальше. return true; } } //Если событие, переданное в параметрах //метода eventFilter(), не удовлетворяет двум поставленным условиям, //то этот метод вернет значение false, и, тем самым, событие будет передано дальше return false; } public: KeyFilter(QObject* pobj = 0) : QObject(pobj) { } };
Приведем часть текста программы с комментариями:
//создается объект класса QLineEdit и вызывается метода show() QLineEdit txt; txt.show(); //создается объект фильтра событий клавиатуры pFilter, в конструктор которого //в качестве объекта-предка передается адрес на однострочное //текстовое поле txt KeyFilter* pFilter = new KeyFilter(&txt); //созданный фильтр привязывается к текстовому полю методом installEventFilter() txt.installEventFilter(pFilter);
Файлы приложения можно взять здесь.
На следующем шаге рассмотрим правила создания диалоговых окон.