Hook and simulate keyboard events on Windows and Linux
—
Registration and management of complex keyboard shortcuts, including multi-step hotkey sequences, global hotkey detection, and advanced hotkey remapping capabilities. The keyboard package provides comprehensive hotkey functionality that works system-wide regardless of application focus.
Register callbacks to be executed when specific key combinations are pressed.
def add_hotkey(hotkey, callback, args=(), suppress=False, timeout=1, trigger_on_release=False):
"""
Invokes a callback every time a hotkey is pressed. The hotkey must be in
the format 'ctrl+shift+a, s'. This would trigger when the user holds ctrl,
shift and "a" at once, releases, and then presses "s".
Parameters:
- hotkey: Hotkey string (e.g., 'ctrl+c', 'ctrl+shift+a, s')
- callback: Function to call when hotkey is triggered
- args: Optional arguments to pass to callback
- suppress: If True, prevent hotkey from reaching other applications
- timeout: Seconds allowed between steps in multi-step hotkeys
- trigger_on_release: If True, trigger on key release instead of press
Returns:
Function to remove the hotkey
Examples:
- add_hotkey('ctrl+c', copy_function)
- add_hotkey('f1', show_help)
- add_hotkey('ctrl+shift+s, ctrl+v', special_paste)
"""
def register_hotkey(hotkey, callback, args=(), suppress=False, timeout=1, trigger_on_release=False):
"""Alias for add_hotkey()."""Remove previously registered hotkeys by hotkey string or callback reference.
def remove_hotkey(hotkey_or_callback):
"""
Removes a previously hooked hotkey. Must be called with the hotkey string
used in add_hotkey() or the callback function.
Parameters:
- hotkey_or_callback: Hotkey string or callback function to remove
"""
def clear_hotkey(hotkey_or_callback):
"""Alias for remove_hotkey()."""
def unregister_hotkey(hotkey_or_callback):
"""Alias for remove_hotkey()."""
def unhook_all_hotkeys():
"""
Removes all keyboard hotkeys in use, including abbreviations, word listeners,
recorders and waits.
"""
def clear_all_hotkeys():
"""Alias for unhook_all_hotkeys()."""
def remove_all_hotkeys():
"""Alias for unhook_all_hotkeys()."""
def unregister_all_hotkeys():
"""Alias for unhook_all_hotkeys()."""Remap hotkeys to other hotkey combinations with optional suppression.
def remap_hotkey(src, dst, suppress=True, trigger_on_release=False):
"""
Whenever the hotkey src is pressed, suppress it and send dst instead.
Parameters:
- src: Source hotkey to remap
- dst: Destination hotkey to send instead
- suppress: If True, suppress the original hotkey
- trigger_on_release: If True, trigger on key release
Returns:
Function to remove the remapping
Example:
remap_hotkey('alt+w', 'ctrl+up') # Remap Alt+W to Ctrl+Up
"""
def unremap_hotkey(hotkey):
"""Alias for remove_hotkey()."""Utility functions for working with hotkey strings and current key states.
def get_hotkey_name(names=None):
"""
Returns a string representation of hotkey from the given key names, or
the currently pressed keys if not given.
Parameters:
- names: List of key names (optional, uses currently pressed keys if None)
Returns:
str: Standardized hotkey string
Example:
get_hotkey_name(['ctrl', 'shift', 'a']) # Returns 'ctrl+shift+a'
"""
def parse_hotkey_combinations(hotkey):
"""
Parses a user-provided hotkey. Instead of each step being a list of the
different scan codes for each key, each step is a list of all possible
combinations of those scan codes.
Parameters:
- hotkey: Hotkey string to parse
Returns:
tuple: All possible scan code combinations for the hotkey
"""import keyboard
def on_ctrl_c():
print('Ctrl+C was pressed!')
def on_f1():
print('F1 Help key pressed!')
def on_custom_hotkey():
print('Custom hotkey Ctrl+Shift+H pressed!')
# Register hotkeys
keyboard.add_hotkey('ctrl+c', on_ctrl_c)
keyboard.add_hotkey('f1', on_f1)
keyboard.add_hotkey('ctrl+shift+h', on_custom_hotkey)
print('Hotkeys registered. Press ESC to exit.')
keyboard.wait('esc')
# Clean up
keyboard.unhook_all_hotkeys()import keyboard
def show_message(msg, count=1):
for i in range(count):
print(f'{msg} ({i+1})')
# Hotkey with arguments
keyboard.add_hotkey('f2', show_message, args=('Hello from F2!', 3))
keyboard.add_hotkey('f3', show_message, args=('Quick message',))
keyboard.wait('esc')
keyboard.unhook_all_hotkeys()import keyboard
def konami_code():
print('Konami code activated!')
def quick_save():
print('Quick save sequence activated!')
# Multi-step hotkey sequences
keyboard.add_hotkey('up, up, down, down, left, right, left, right, b, a', konami_code)
keyboard.add_hotkey('ctrl+s, ctrl+s', quick_save, timeout=2) # Double Ctrl+S within 2 seconds
keyboard.wait('esc')
keyboard.unhook_all_hotkeys()import keyboard
def custom_alt_f4():
print('Alt+F4 intercepted! Custom close behavior.')
# Could implement custom confirmation dialog here
return False # Suppress the original Alt+F4
# Suppress Alt+F4 and replace with custom behavior
keyboard.add_hotkey('alt+f4', custom_alt_f4, suppress=True)
print('Alt+F4 is now intercepted. Press ESC to exit.')
keyboard.wait('esc')
keyboard.unhook_all_hotkeys()import keyboard
hotkey_callbacks = {}
def register_dynamic_hotkey(hotkey_str, message):
def callback():
print(f'Dynamic hotkey: {message}')
remove_func = keyboard.add_hotkey(hotkey_str, callback)
hotkey_callbacks[hotkey_str] = remove_func
print(f'Registered: {hotkey_str}')
def unregister_dynamic_hotkey(hotkey_str):
if hotkey_str in hotkey_callbacks:
hotkey_callbacks[hotkey_str]() # Call remove function
del hotkey_callbacks[hotkey_str]
print(f'Unregistered: {hotkey_str}')
# Register some dynamic hotkeys
register_dynamic_hotkey('f5', 'Refresh action')
register_dynamic_hotkey('f6', 'Toggle action')
register_dynamic_hotkey('ctrl+1', 'Workspace 1')
print('Dynamic hotkeys registered. Press F9 to remove F5, F10 to exit.')
def remove_f5():
unregister_dynamic_hotkey('f5')
keyboard.add_hotkey('f9', remove_f5)
keyboard.wait('f10')
# Clean up all remaining hotkeys
for remove_func in hotkey_callbacks.values():
remove_func()import keyboard
# Remap Windows key combinations for different workflow
remove_remap1 = keyboard.remap_hotkey('win+l', 'ctrl+alt+l') # Custom lock
remove_remap2 = keyboard.remap_hotkey('win+d', 'alt+tab') # Show desktop -> Alt+Tab
# Remap for left-handed users
remove_remap3 = keyboard.remap_hotkey('ctrl+c', 'ctrl+insert') # Alternative copy
remove_remap4 = keyboard.remap_hotkey('ctrl+v', 'shift+insert') # Alternative paste
print('Hotkey remapping active. Press ESC to exit.')
keyboard.wait('esc')
# Remove all remappings
remove_remap1()
remove_remap2()
remove_remap3()
remove_remap4()import keyboard
class HotkeyManager:
def __init__(self):
self.mode = 'normal'
self.hotkeys = {}
self.setup_mode_hotkeys()
def setup_mode_hotkeys(self):
# Mode switching
self.hotkeys['f12'] = keyboard.add_hotkey('f12', self.toggle_mode)
# Context-sensitive hotkeys
self.hotkeys['1'] = keyboard.add_hotkey('1', self.handle_1_key)
self.hotkeys['2'] = keyboard.add_hotkey('2', self.handle_2_key)
def toggle_mode(self):
self.mode = 'editing' if self.mode == 'normal' else 'normal'
print(f'Switched to {self.mode} mode')
def handle_1_key(self):
if self.mode == 'normal':
print('Normal mode: Action 1')
else:
print('Editing mode: Edit action 1')
def handle_2_key(self):
if self.mode == 'normal':
print('Normal mode: Action 2')
else:
print('Editing mode: Edit action 2')
def cleanup(self):
for remove_func in self.hotkeys.values():
remove_func()
# Usage
manager = HotkeyManager()
print('Context-sensitive hotkeys active. F12 to toggle mode, ESC to exit.')
keyboard.wait('esc')
manager.cleanup()import keyboard
def slow_sequence():
print('Slow sequence completed!')
def fast_sequence():
print('Fast sequence completed!')
# Different timeout values for different sequences
keyboard.add_hotkey('ctrl+a, ctrl+b', slow_sequence, timeout=5) # 5 seconds allowed
keyboard.add_hotkey('ctrl+x, ctrl+y', fast_sequence, timeout=0.5) # 0.5 seconds allowed
keyboard.wait('esc')
keyboard.unhook_all_hotkeys()import keyboard
def on_key_release():
print('Hotkey triggered on release!')
def on_key_press():
print('Hotkey triggered on press!')
# Compare press vs release triggering
keyboard.add_hotkey('f7', on_key_press, trigger_on_release=False)
keyboard.add_hotkey('f8', on_key_release, trigger_on_release=True)
keyboard.wait('esc')
keyboard.unhook_all_hotkeys()'f1' - Function keys'a' - Letter keys'space' - Named keys'enter' - Special keys'ctrl+c' - Single modifier'ctrl+shift+a' - Multiple modifiers'alt+f4' - Case insensitive'ctrl+k, ctrl+c' - Visual Studio style'ctrl+x, ctrl+c' - Emacs style'g, g' - Vim style (double key)'plus' for '+' symbol'comma' for ',' symbol'space' for spacebar'left ctrl' / 'right ctrl' for sided modifiersHotkey registration may fail due to:
The package will raise ValueError for invalid hotkey strings and may silently fail to register hotkeys in restricted environments.
For optimal performance:
Install with Tessl CLI
npx tessl i tessl/pypi-keyboard