Winner Code
Veni, vidi, programmare!
Veni, vidi, programmare!
29 апреля 2010
Эта тема очень интересная, поэтому именно ей я посвящу этот топик после длительного молчания. Думаю, за свою практику программисту не может не потребоваться использование глобальных клавиш (hotkeys, shortcuts) в приложении. Благодаря им пользователь может сэкономить своё бесценное время, да и функционал программы повышается. Моей задачей было найти способ регистрирования в системе комбинации клавиш и их дальнейшее транслирование. Задача не очень тяжелая, если выполнять под конкретную ОС, а вот сделать кроссплатформенность уже не так легко, нужно учесть некоторые факторы.
Давайте возьмемся за X11. Существует функция XGrabKey, с помощью которой и можно зарегистрировать сочетания клавиш в X11. Для её использования нужно подключить X11-headers. Вот пример кода, который должен работать:
#include <QtGui> #include <QX11Info> #include <X11/X.h> #include <X11/Xlib.h> #include <X11/keysym.h> #ifdef KeyPress const int XKeyPress = KeyPress; const int XKeyRelease = KeyRelease; #undef KeyPress #undef KeyRelease #endif class X11ShortCut :QMainWindow { Q_OBJECT public: X11ShortCut(QWidget *parent) : QMainWindow(parent) { qApp->installEventFilter(this); m_keyCode = XKeysymToKeycode(QX11Info::display(), XK_F11); XGrabKey(QX11Info::display(), m_keyCode, ControlMask|ShiftMask, QX11Info::appRootWindow(), False, GrabModeAsync, GrabModeAsync); XFlush(QX11Info::display()); } ~X11ShortCut() { XUngrabKey(QX11Info::display(), m_keyCode, ControlMask|ShiftMask, QX11Info::appRootWindow()); } bool X11ShortCut::eventFilter(QObject *, QEvent *event) { if(event->type() == QEvent::KeyPress) { QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event); int qtKey = keyEvent->key(); if(keyEvent->modifiers() == (Qt::ControlModifier|Qt::ShiftModifier) && qtKey == Qt::Key_F11) { insertPlainText("Ctrl+Shift+F11 pressed!\n"); return false; } } return true; } private: int m_keyCode; };
Здесь и нечего комментировать.
m_keyCode = XkeysymToKeycode(QX11Info::display(), XK_F11);
Получаем код клавиши F11. Далее регистрируем клавишу в системе с помощью главной функции — XGrabKey. В деструкторе сниманием хоткей. А далее у нас идет фильтр событий, где мы ожидаем известия о нажатии нужного нам сочетания. Более подробнее можно почитать здесь: http://www.xfree.org/current/XGrabKey.3.html.
Теперь о регистрации “горячих” клавиш в OS Windows. Будем использовать WinApi. Суть та же, мы регистрируем комбинацию с помощью ф-ции WinApi RegisterHotKey() и ожидаем прибытия сообщения WM_HOTKEY (http://msdn.microsoft.com/en-us/library/ms646279(VS.85).aspx).
BOOL RegisterHotKey ( HWND hWnd, int id, UINT fsModifiers, UINT vk );
#DEFINE HOTKEYHIDE 0×000001 RegisterHotKey(hwndMain,HOTKEYHIDE,MOD_CONTROL,VK_F12);
Как отлавливать сообщения я уже и не помню, это будет Вашим домашним заданием :D.
Ещё есть популярная ОС — MacOS, но под неё я ни разу ничего не программировал, поэтому и не раскажу. Читайте далее о кроссплатформенном решении.
Так вот, понадобилось мне в одном проекте использовать hotkeys +программа кроссплатформенная, поэтому желательно было найти соответственный фреймворк. Гугл, как всегда, помог мне (хоть и потратил я немало времени). Есть отличное дополнение для Qt, которое называется libQxt (http://libqxt.org). Качаем от туда архив, ставим. Устанавливается это “сокровище” довольно легко:
$ ./configure && make && make install
Единственное, я использую QtCreator. В его поставке идет “своя” утилита qmake, поэтому вам нужно её явно указать. Установка немного изменяется:
$ ./configure -qmake-bin /home/%user%/%QtSDk%/qt/bin/qmake $ make && make install
Для интеграции в проект нужно в .pro-файл дописать:
CONFIG += qxt QXT += core gui
Документация здесь: http://doc.libqxt.org.
Переходим к делу. В версии 0.5, для использования глобальных клавиш, вам надо создавать объект QxtApplication, а не Qapplication. По словам разработчиков, в версии 0.6 они уберут эту зависимость. Поэтому файл main.cpp изменяется:
#include <QtGui/QApplication> #include "mainwindow.h" #include <QxtApplication> int main(int argc, char *argv[]) { QxtApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }
Далее переходим в класс MainWindow. В .h-файле дописываем:
#include <QxtGlobalShortcut> class MainWindow : public QMainWindow { … private: QString m_shortcutDef; QxtGlobalShortcut m_hotkeyHandle; … private slots: void hotkeyPressed(); }
в .cpp-файле (в конструкторе инициализируем все):
m_shortcutDef = QString("Ctrl+Shift+X"); m_hotkeyHandle.setShortcut( QKeySequence(m_shortcutDef) ); m_hotkeyHandle.setEnabled(true); connect( &m_hotkeyHandle, SIGNAL(activated()), this, SLOT(hotkeyPressed()) );
И, собственно, сам обработчик сообщения:
void MainWindow::hotkeyPressed() { // Achtung }
Вот и всё. Этот код вы можете использовать для X11, Windows, MacOS.
Удачи!