Archive for Июнь 9th, 2014

В первой части статьи было кратко рассмотрено, как запустить 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 используется один метод:

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 — видимо, количество нажатий

События от мышки

Данная группа несколько объемней, и имеет некоторые тонкости в обработке. Рассмотрим методы по порядку.

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).

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 координаты колесиков. Я заполняю их так:

QPoint angleDelta(deltax, deltay);
QPoint pixelDelta(angleDelta * 10);

Возможно, есть нормальный метод перевода угла поворота в пиксельное значение, но я его не нашел.

Обработка событий в QGLGUI

QGLGUI реализует как однопоточную схему работы с QT, так и многопоточную. Для передачи событий выделены несколько методов в классе GlGui:

/**
 * 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.

 

Tags: , ,