Skip to content

Instantly share code, notes, and snippets.

@mdavey
Created June 5, 2014 09:51
Show Gist options
  • Save mdavey/6d40a89dbc15aefcc8cd to your computer and use it in GitHub Desktop.
Save mdavey/6d40a89dbc15aefcc8cd to your computer and use it in GitHub Desktop.
Global hot keys in Python for Win32
import ctypes
import ctypes.wintypes
import win32con
class GlobalHotKeys(object):
"""
Register a key using the register() method, or using the @register decorator
Use listen() to start the message pump
Example:
from globalhotkeys import GlobalHotKeys
@GlobalHotKeys.register(GlobalHotKeys.VK_F1)
def hello_world():
print 'Hello World'
GlobalHotKeys.listen()
"""
key_mapping = []
user32 = ctypes.windll.user32
MOD_ALT = win32con.MOD_ALT
MOD_CTRL = win32con.MOD_CONTROL
MOD_CONTROL = win32con.MOD_CONTROL
MOD_SHIFT = win32con.MOD_SHIFT
MOD_WIN = win32con.MOD_WIN
@classmethod
def register(cls, vk, modifier=0, func=None):
"""
vk is a windows virtual key code
- can use ord('X') for A-Z, and 0-1 (note uppercase letter only)
- or win32con.VK_* constants
- for full list of VKs see: http://msdn.microsoft.com/en-us/library/dd375731.aspx
modifier is a win32con.MOD_* constant
func is the function to run. If False then break out of the message loop
"""
# Called as a decorator?
if func is None:
def register_decorator(f):
cls.register(vk, modifier, f)
return f
return register_decorator
else:
cls.key_mapping.append((vk, modifier, func))
@classmethod
def listen(cls):
"""
Start the message pump
"""
for index, (vk, modifiers, func) in enumerate(cls.key_mapping):
if not cls.user32.RegisterHotKey(None, index, modifiers, vk):
raise Exception('Unable to register hot key: ' + str(vk))
try:
msg = ctypes.wintypes.MSG()
while cls.user32.GetMessageA(ctypes.byref(msg), None, 0, 0) != 0:
if msg.message == win32con.WM_HOTKEY:
(vk, modifiers, func) = cls.key_mapping[msg.wParam]
if not func:
break
func()
cls.user32.TranslateMessage(ctypes.byref(msg))
cls.user32.DispatchMessageA(ctypes.byref(msg))
finally:
for index, (vk, modifiers, func) in enumerate(cls.key_mapping):
cls.user32.UnregisterHotKey(None, index)
@classmethod
def _include_defined_vks(cls):
for item in win32con.__dict__:
item = str(item)
if item[:3] == 'VK_':
setattr(cls, item, win32con.__dict__[item])
@classmethod
def _include_alpha_numeric_vks(cls):
for key_code in (range(ord('A'), ord('Z')) + range(ord('0'), ord('9'))):
setattr(cls, 'VK_' + chr(key_code), key_code)
# Not sure if this is really a good idea or not?
#
# It makes decorators look a little nicer, and the user doesn't have to explicitly use win32con (and we add missing VKs
# for A-Z, 0-9
#
# But there no auto-complete (as it's done at run time), and lint'ers hate it
GlobalHotKeys._include_defined_vks()
GlobalHotKeys._include_alpha_numeric_vks()
from globalhotkeys import GlobalHotKeys
@GlobalHotKeys.register(GlobalHotKeys.VK_F1, GlobalHotKeys.MOD_SHIFT)
def hello_world():
print "Hello World!"
@GlobalHotKeys.register(GlobalHotKeys.VK_F2)
def hello_world_2():
print "Hello World again?"
# Q and ctrl will stop message loop
GlobalHotKeys.register(GlobalHotKeys.VK_Q, 0, False)
GlobalHotKeys.register(GlobalHotKeys.VK_C, GlobalHotKeys.MOD_CTRL, False)
# start main loop
GlobalHotKeys.listen()
@thinker3
Copy link

Many thanks to you!
I also find a bug:
for key_code in (range(ord('A'), ord('Z') + 1) + range(ord('0'), ord('9') + 1)):

@randylewis7
Copy link

How do I manually kill the hotkey listener? in the example, lines 15 and 16 do what I want if you press the appropriate keys, but I'm not sure how to duplicate this functionality without actively pressing keys. I looked at the register function to try to understand how it does this, but couldn't figure it out.
p.s. thanks for writing the only global python hotkey program that actually works!

@Aliense
Copy link

Aliense commented Nov 7, 2020

python 3.6 TypeError: unsupported operand type(s) for +: 'range' and 'range'

used it

        a = ord('A');b = ord('Z')+1;c = ord('0');d = ord('9')+1;
        #range(a,b)+range(c,d) is list, that have items between a and b and between c and d
        #that list we also can get used list bettwen min(a,c) and max(b,d), but not between max(a,c) and min(b,d)
        #that list is [k for k in range(min(a,c), max(b,d)) if k not in range(max(a,c), min(b,d))]
        
        for key_code in [k for k in range(min(a,c), max(b,d)) if k not in range(max(a,c), min(b,d))]:
        #for key_code in (range(ord('A'), ord('Z')) + range(ord('0'), ord('9'))):

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment