Шаг 131.
Библиотека Qt.
Искусственное создание событий

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

    Иногда возникает необходимость в событиях, созданных искусственно. Например, это полезно при отладке вашей программы, для того чтобы имитировать действия пользователя.

    Для генерации события можно воспользоваться одним из двух статических методов класса QCoreApplicationsendEvent() или 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);

    Файлы приложения можно взять здесь.

    На следующем шаге рассмотрим правила создания диалоговых окон.




Предыдущий шаг Содержание Следующий шаг