from __future__ import absolute_import import functools import logging logger = logging.getLogger("keyboard_handler") import wx from .main import KeyboardHandler, KeyboardHandlerError from . import key_constants __all__ = ['WXKeyboardHandler', 'WXControlKeyboardHandler'] def call_after(func): def wrapper(*args, **kwargs): wx.CallAfter(func, *args, **kwargs) functools.update_wrapper(wrapper, func) return wrapper class BaseWXKeyboardHandler(KeyboardHandler): def __init__(self, *args, **kwargs): super(BaseWXKeyboardHandler, self).__init__(*args, **kwargs) #Setup the replacement dictionaries. for i in dir(wx): if i.startswith('WXK_'): key = i[4:].lower() self.replacement_keys[key] = getattr(wx, i) elif i.startswith('MOD_'): key = i[4:].lower() self.replacement_mods[key] = getattr(wx, i) def parse_key (self, keystroke, separator="+"): keystroke = [self.keycode_from_key(i) for i in keystroke.split(separator)] mods = 0 for i in keystroke[:-1]: mods = mods | i #or everything together return (mods, keystroke[-1]) def keycode_from_key(self, key): result = None if key in self.replacement_mods: result = self.replacement_mods[key] elif key in self.replacement_keys: result = self.replacement_keys[key] if result >= 277: result -= 277 elif len(key) == 1: result = ord(key.upper()) if result is None: raise KeyboardHandlerError("Could not translate key %r into a valid keycode." % key) return result class WXKeyboardHandler(BaseWXKeyboardHandler): def __init__ (self, parent, *args, **kwargs): super(WXKeyboardHandler, self).__init__(*args, **kwargs) self.parent = parent self.key_ids = {} self.replacement_keys = key_constants.keys self.replacement_mods = key_constants.modifiers @call_after def register_key(self, key, function): super(WXKeyboardHandler, self).register_key(key, function) key_id = wx.NewId() parsed = self.parse_key(key) res = self.parent.RegisterHotKey(key_id, *parsed) if not res: logger.warn("Failed to register hotkey: %s for function %r", key, function) self.parent.Bind(wx.EVT_HOTKEY, lambda evt: self.process_key(evt, key_id), id=key_id) self.key_ids[key] = key_id return res def parse_key (self, keystroke, separator="+"): keystroke = str(keystroke) #We don't want unicode keystroke = [self.keycode_from_key(i) for i in keystroke.split(separator)] mods = 0 for i in keystroke[:-1]: mods = mods | i #or everything together return (mods, keystroke[-1]) @call_after def unregister_key (self, key, function): super(WXKeyboardHandler, self).unregister_key(key, function) if key not in self.key_ids: return #there's nothing we can do. key_id = self.key_ids[key] self.parent.UnregisterHotKey(key_id) self.parent.Unbind( wx.EVT_HOTKEY, id=key_id) self.key_ids.pop(key) def process_key (self, evt, id): evt.Skip() key_ids = self.key_ids.keys() for i in key_ids: if self.key_ids.get(i) == id: self.handle_key(i) class WXControlKeyboardHandler(wx.StaticText, KeyboardHandler): def __init__(self, parent=None, *a, **k): wx.StaticText.__init__(self, parent=parent) KeyboardHandler.__init__(self, *a, **k) self.wx_replacements = {} for i in [d for d in dir(wx) if d.startswith('WXK_')]: self.wx_replacements[getattr(wx, i)] = i[4:].lower() self.Bind(wx.EVT_KEY_DOWN, self.process_key, self) self.SetFocus() def process_key(self, evt): keycode = evt.GetKeyCode() keyname = self.wx_replacements.get(keycode, None) modifiers = "" replacements = ( (evt.ControlDown(), 'control+'), (evt.AltDown(), 'alt+'), (evt.ShiftDown(), 'shift+'), (evt.MetaDown(), 'win+') ) for mod, ch in (replacements): if mod: modifiers += ch if keyname is None: if 27 < keycode < 256: keyname = chr(keycode).lower() else: keyname = "(%s)unknown" % keycode key = modifiers + keyname self.handle_key(key)