Pull to refresh

Симуляция нажатий Home, End, PgUp, PgDown

Python *

Введение

image
Примерно полтора года назад я стал счастливым обладателем HP Mini 110-3155sr. Машинка всем порадовала, но была одна проблема, которая со временем надоедала все больше — отсутствие кнопок Home, End, PgUp, PgDown. О том, как я решил эту проблему при помощи небольшой программки на Python — под катом.

Решение приходит в голову достаточно быстро. Если нет кнопок — значит, нужно создать для них какие-нибудь хоткеи. Я не нашел, как сделать это системными средствами, поэтому начал перебирать различные программы подобного рода. Несмотря на то, что таких программ действительно много, я не нашел ту, которая мне подошла бы. Одни предлагали выполнение команд на хоткеи, в других вообще не находилось удобного сочетания клавиш.

В конце концов я решил написать небольшую утилитку своими руками. Кроме того, это стало неплохой идеей в качестве «боевого крещения» на Python — начал учить совсем недавно, и стало интересно, насколько быстро удастся (если удастся вообще) сделать такую штучку.

Постановка задачи

image
Итак, нужно написать программу, которая будет реагировать на нажатия одних клавиш, и генерировать другие. В моем конкретном случае нужно генерировать нажатия всего четырех кнопок — Home, End, PgUp, PgDown. После перебора кучи возможных комбинаций, отброса существующих и важных для меня, я решил остановиться на комбинациях Alt + стрелки, а именно:
  • Alt + Left == Home
  • Alt + Right == End
  • Alt + Up == Page Up
  • Alt + Down == Page Down

Что используем?

Python 2.7 — эта версия достаточно хорошо поддерживается, в чем мне удалось убедиться;
Python for Windows Extentions (build 216) — для использования WinAPI;
pyHook 1.5.1 — удобная обертка для использования системных хуков (system hooks);
py2exe 0.6.9 (опционально) — для создания standalone-исполняемого файла.

Идея решения


Общая идея достаточно проста: вешаем системный хук, отслеживаем нажатия. Если нажата Alt + стрелка — генерируем соответствующее системное сообщение о нажатии кнопки.

Пряники и грабли
  1. Есть несколько моментов, которые требуют особого внимания:
  2. Клавиша Alt выделяется из других тем, что если она нажата, любое другое системное сообщение о нажатии кнопки будет «системным». Это значит, что непосредственно из сообщения о нажатии стрелки мы можем выяснить, нажата кнопка Alt в данный момент, или нет.
  3. Если мы попытаемся нажать кнопку PgUp при нажатом альте — ничего не произойдет. Точно так же и с генерацией системных сообщений. Выход прост: программно «отпустить» Alt перед генерацией, а потом «нажать» после.
  4. При нажатии (keystroke) каждой кнопки генерируется два сообщения — нажатие (key down) и освобождении (key up). Стоит их различать, иначе на один keystroke будет приходиться две симуляции нажатия нужной нам кнопки.
  5. Функция обработки сообщений возвращает True или False, и в зависимости от этого сообщение либо передается дальше по цепочке других системных хуков, либо не передается. Таким образом можно контролировать поток системных сообщений.

Код программы

# -*- encoding:cp1251 -*-
import pythoncom, pyHook, win32api, win32con

# функция обработки сообщений
def OnKeyboardEvent(event):
    # если словили сообщение о нажатии левого альта...
    if event.Key == 'Lmenu':
        # ... а именно о том, что клавиша была отпущена...
        if event.MessageName[-2:] == 'up':
            # ... то мы ее таки отпускаем...
            win32api.keybd_event(win32con.VK_MENU, 0, 
            win32con.KEYEVENTF_EXTENDEDKEY | win32con.KEYEVENTF_KEYUP, 0)
        # ... и в любом случае мы дальше не передаем это сообщение в цепочку обработчиков
        return False
    # если нажата не стрелка - значит, передаем сообщение дальше по цепочке
    if not(37 <= event.KeyID <= 40 and event.MessageName == 'key sys down'):
        return True
    
    # иначе - обрабатываем его
    # поскольку альт нажат, то прежде, чем генерировать сообщения, нужно альт "отпустить"
    win32api.keybd_event(win32con.VK_MENU, 0, 
    win32con.KEYEVENTF_EXTENDEDKEY | win32con.KEYEVENTF_KEYUP, 0)
    # после этого посылаем сообщение
    if   event.KeyID == 38: win32api.keybd_event(win32con.VK_PRIOR, 0, 0, 0)
    elif event.KeyID == 40: win32api.keybd_event(win32con.VK_NEXT,  0, 0, 0)
    elif event.KeyID == 37: win32api.keybd_event(win32con.VK_HOME,  0, 0, 0)
    elif event.KeyID == 39: win32api.keybd_event(win32con.VK_END,   0, 0, 0)
    # и снова таки "нажимаем" отпущенный альт
    win32api.keybd_event(win32con.VK_MENU, 0, win32con.KEYEVENTF_EXTENDEDKEY, 0)
    # в любом случае, это сообщение дальше по цепочке не передается
    return False

# создание экземпляра класса HookManager
hm = pyHook.HookManager()
# требуется отслеживать только нажатия клавиш
hm.KeyAll = OnKeyboardEvent
# вешаем хук
hm.HookKeyboard()
# ловим сообщения
pythoncom.PumpMessages()



Результат


Такая программа действительно упростила жизнь. Правда, работает она далеко не «идеально».
Во-первых, кнопка Alt потеряла полноту своей функциональности — теперь не получится просто так обратиться к меню приложения, нажав ее. Тем не менее, тот же Alt + F4 работает нормально. Впрочем, все основные системные хоткеи уже и так заняты, так что чем-то жертвовать в любом случае пришлось.
Во-вторых, обнаружен следующий баг: сочетание Alt + Tab работает, но при освобождении кнопки Alt окошко переключения не пропадает. Пока что я не знаю, как это исправить, надеюсь, кто-нибудь подскажет.

Выводы


Глобальный вывод — «Python — это круто» (напомню, это первое, что я написал на питоне). Действительно, необходимые библиотеки находятся без труда, устанавливаются в два клика. Приятно, что так легко можно использовать то, что уже писалось другими.

Вывод помельче касается непосредственно pyHook — пакет достаточно удобный. Уже появилось несколько идей, как можно будет его применить:
  • основываясь на перемещении курсора попытаться определить (приняв, например, 1 пиксель ~ 1 мм), какое расстояние проходит мышка (за час, за день, за все время наблюдений), ее средняя скорость, т.п.;
  • написать обычный клавиатурный keyhook в выводом в какой-нибудь лог-файл (давно хотел почувствовать себя шпионом :) );
  • составить частотный словарь своей речи. По сути тот же keyhook, только объединяющий отдельные буквы в слова, и подсчитывающий их частоту. Идея стырена навеяна давней перепиской с Skiminok — он делал подобный плагин для QIP.


UPD: lxyd подсказал, что для всего вышеописанного существует программа AutoHotkey. Статья получилась в стиле «как я изобрел свой никому не нужный велосипед».
Tags:
Hubs:
Total votes 34: ↑29 and ↓5 +24
Views 32K
Comments Comments 32