В первой части статьи было кратко рассмотрено, как запустить QT в своем приложении, однако, не было сказано ни одного слово о том, как передавать в QT разнообразные события, чтобы GUI начал работать.
Также в первой части статьи вскользь упоминалась библиотека qglgui (https://github.com/Kvalme/qglgui). Эта библиотека предназначена для интеграции QT в свое приложение.
Передача сообщений от клавиатуры/мышки в QT
Первое желание, которое возникает после реализации рендеринга QT — потыкать в кнопочки. Первый метод которым хочется воспользоваться — запихнуть события непосредственно в QApplication::event, что разумеется не работает. Покопавшись чуть дальше, мы увидим, что события надо подсовывать непосредственно в eventloop QT и периодически дергать его.
Для работы с eventLoop у QApplication есть два метода:
- Можно вызвать QApplication::exec и больше ни о чем не думать. Из данного метода QApplication вернется только при закрытии. Так что этот метод не подходит, если мы хотим сохранить контроль над тредом QT, либо вообще делать что-либо кроме рисования UI в однопоточном случае.
- QApplication::processEvents — данный метод обрабатывает все имеющиеся на данный момент события и возвращает управление. Именно его я и предлагаю использовать.
После организации цикла с QApplication::processEvents в нашем приложении смогут работать сигналы, например, от таймера.
Для передачи событий в QT из QPA есть специальный класс: QWindowSystemInterface.
QWindowSystemInterface
Данный класс предназначен для получения событий от оконной системы. К сожалению, на него, как и почти на все классы QPA, нет никакой документации, поэтому все приходится узнавать либо методом тыка, либо смотря исходники QT.
Из того, что было найдено, наиболее важная вещь — методы данного класса могут вызываться не из основного потока QT приложения.
События от клавиатуры
От клавиатуры у нас есть всего два вида событий — нажатие и отпускание кнопки. Для передачи этих событий в QT используется один метод:
1 |
static void handleKeyEvent(QWindow *w, QEvent::Type t, int k, Qt::KeyboardModifiers mods, const QString & text = QString(), bool autorep = false, ushort count = 1); |
Кратко по списку параметров:
- w — окно которому передать события. Если передать NULL — уйдет текущему активному окну
- t — тип события. QEvent::KeyPress либо QEvent::KeyRelease
- k — кнопка с которой произошло событие (одна из Qt::Key)
- mods — состояние модификаторов (Qt::NoModifier, Qt::AltModifier, Qt::ShiftModifier, Qt::ControlModifier, Qt::MetaModier, есть еще экзотические типа: Qt::KeypadModifier, Qt::GroupSwitchModifier)
- text — символ, который соответствует нажатой кнопке. Если этот параметр пустой — отрабатывает нажатие кнопки, если не пустой — кнопка игнорируется и отрабатывается только печать символа
- autorep — по всей видимости что-то, связанное с автоповтором кнопки. Я реализую автоповтор с вызывающей стороны, поэтому не экспериментировал с этим параметром
- count — видимо, количество нажатий
События от мышки
Данная группа несколько объемней, и имеет некоторые тонкости в обработке. Рассмотрим методы по порядку.
1 |
static void handleMouseEvent(QWindow *w, const QPointF & local, const QPointF & global, Qt::MouseButtons b, Qt::KeyboardModifiers mods = Qt::NoModifier); |
Данный метод занимается работой с событиями от кнопок мыши и передвижения мыши.
- w — окно которому нужно передать событие.
- local — локальные (оконные) координаты точки в которой произошло событие. Можно получить используя wnd->mapFromGlobal(global)
- global — глобальные (экранные) координаты точки в которой произошло событие.
- b — нажатые кнопки. Если кнопка была отпущена — не передаем флаг, соответствующий отпущенной кнопке.
- mods — состояние модификаторов клавиатуры (Qt::NoModifier, Qt::AltModifier, Qt::ShiftModifier, Qt::ControlModifier, Qt::MetaModier, есть еще экзотические типа: Qt::KeypadModifier, Qt::GroupSwitchModifier)
- source — не трогаем. Назначение не известно.
Особенность обработки нажатий кнопок состоит в том, что необходимо после передачи события в QT, еще и вручную активировать окно (w->requestActivate).
1 |
static void handleWheelEvent(QWindow *w, const QPointF & local, const QPointF & global, QPoint pixelDelta, QPoint angleDelta, Qt::KeyboardModifiers mods = Qt::NoModifier, Qt::ScrollPhase phase = Qt::ScrollUpdate); |
Данный метод занимается работой с событиями от колесиков мыши.Параметры в целом совпадают с теми, что были рассмотрены выше. Два новых параметра(pixelDelta и angleDelta) отвечают за 2 координаты колесиков. Я заполняю их так:
1 2 |
QPoint angleDelta(deltax, deltay); QPoint pixelDelta(angleDelta * 10); |
Возможно, есть нормальный метод перевода угла поворота в пиксельное значение, но я его не нашел.
Обработка событий в QGLGUI
QGLGUI реализует как однопоточную схему работы с QT, так и многопоточную. Для передачи событий выделены несколько методов в классе GlGui:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
/** * Injects mouse move event into QT event queue * * @param screenId idientifyer of the screen on which event happened * @param positon current mouse position * @param buttons pressed mouse buttons * @param modifiers active keyboard modifiers */ virtual void InjectMouseMoveEvent(int screenId, QPoint position, Qt::MouseButtons buttons, Qt::KeyboardModifiers modifiers) = 0; /** * Injects mouse button event into QT event queue * * @param screenId idientifyer of the creen on which event happened * @param position current mouse position * @param button pressed button * @param modifiers active keyboard modifiers */ virtual void InjectMouseButtonEvent(int screenId, QPoint position, Qt::MouseButton button, Qt::KeyboardModifiers modifiers) = 0; /** * Injects mouse wheel event into QT event queue * * @param screenId identifyer of the screen on which event happened * @param position current mouse position * @param deltax offset of mouse wheel on X axis * @param deltay offset of mousewheel on Y axis * @param modifiers active keyboard modifiers */ virtual void InjectMouseWheelEvent(int screenId, QPoint position, double deltax, double deltay, Qt::KeyboardModifiers modifiers) = 0; /** * Injects keyboard event into QT event queue * Always passes key to current active window * * @param eventType Qt::KeyPress or Qt::KeyRelease * @param key key * @param modifiers modifiers state (shift, meta, ctrl) */ virtual void InjectKeyboardEvent(QEvent::Type eventType, Qt::Key key, Qt::KeyboardModifiers modifiers) = 0; /** * Injects character event into QT event queue * Always passes character to current active window * * @param character character to inject */ virtual void InjectCharacterEvent(QChar character) = 0; |
Данные методы могут быть вызваны из любого треда. Также, для упрощения интеграции с GLFW, имеется вспомогательная библиотека — glfwhelper.