class KeyboardWatcher: def __init__(self, key_handlers: Sequence, verbose: bool = False): self._pool = KeyPool() self._verbose = verbose # sort them in order to give combinations precedence key_handlers = sorted( key_handlers, key=lambda x: len(x.trigger), reverse=True, ) self._handlers = {handler.trigger: handler for handler in key_handlers} self._listener = None target = partial(infinite_handle_cycle, active_keys=self.pressed, handlers=self._handlers, handle_method_name='press') hold_thread = Thread(target=target, daemon=True) hold_thread.start() self._listener = Listener( on_press=self.handle_press, on_release=self.handle_release, # suppress=True, ) @property def pressed(self): return self._pool.pressed @property def released(self): return self._pool.released @staticmethod def _get_pressed_key(key: KeyCode): if isinstance(key, KeyCode): key_name = key.char elif isinstance(key, Key): key_name = key.name else: raise ValueError try: key_name = key_name.lower() except AttributeError: return None return key_name def handle_press(self, key: KeyCode): key_name = self._get_pressed_key(key) if (key_name and key_name not in self.pressed): self.pressed.add(key_name) if self._verbose: print(f'pressed: {self.pressed}') def handle_release(self, key: KeyCode): key_name = self._get_pressed_key(key) if key_name in self.pressed: self.pressed.remove(key_name) self.released.add(key_name) _handle_cycle(self.released, self._handlers, 'release') self.released.remove(key_name) if self._verbose: print(f'released: {self.pressed}') def join(self): while self._listener.is_alive(): self._listener.join(0.1) def __enter__(self): self._listener.start() self._listener.wait() return self def __exit__(self, exc_type, exc_val, exc_tb): self._listener.stop() def start(self): self._listener.start() while True: time.sleep(0.1)
class KeyboardDriver(): class Commands(Enum): Quit = 100 Command1 = 1 Command2 = 2 Command3 = 3 Command4 = 4 Command5 = 5 Command6 = 6 Command7 = 7 Command8 = 8 Command9 = 9 Command10 = 0 Test = 101 @classmethod def contains(cls, key): return any(key == item.value for item in cls) @classmethod def byvalue(cls, val): for item in cls: if item.value == val: return item return None def __init__(self): # print('KeyboardDriver::__init__()') self._command = None # Setup the hook functions self.listener = Listener(on_press=self.__on_press__, on_release=self.__on_release__) self.listener.start() self.listener.wait() def get_command(self): rVal = self._command self._command = None return rVal def __on_press__(self, key): # For this case we can simply ignore the key down event try: ch = ord(key.char) - ord('0') if self.Commands.contains(ch): self._command = self.Commands.byvalue(ch) except AttributeError: pass def __on_release__(self, key): try: ch = key.char # print('U = {0}'.format(ch)) except AttributeError: if key == Key.esc: self._command = self.Commands.Quit
if listen: #if hasattr(key, 'char') and key.char == '1': # there is no `Key.1` and `1` if str(key) == "'1'": # it need `' '` in string label_var.set(label_var.get() + '1') print('1!') # --- main --- listen = False root = tk.Tk() label_var = tk.StringVar() label = tk.Label(root, textvariable=label_var, width=10) label.pack() listener = Listener(on_press=onpress) print('start') listener.start() try: listener.wait() print('mainloop') root.mainloop() finally: print('stop') listener.stop() print('end') # without `listener.join()` because it runs as `daemon`
class Keyboard_Driver(): class Keys(Enum): KEY1 = 'q' KEY2 = 'w' KEY3 = 'e' @classmethod def contains(cls, key): return any(key == item.value for item in cls) @classmethod def byvalue(cls, val): for item in cls: if item.value == val: return item return None def __init__(self, on_press, on_release): # Check if we're on a Mac here, and see if we have GUID; if not throw an exception if sys.platform == 'darwin': # TODO : This really should exit entirely but for now make it print a message if os.getuid() != 0: # raise ZeroOneException("Must be run as root on a Mac, use 'sudo -s '") print( "*** Should be run as root on a Mac, this may not work properly ***" ) # Store the last_key_press self.keyEvents = { self.Keys.KEY1.value: None, self.Keys.KEY2.value: None, self.Keys.KEY3.value: None, } self.keyHandler = { self.Keys.KEY1.value: None, self.Keys.KEY2.value: None, self.Keys.KEY3.value: None, } # Setup the hook functions self.listener = Listener(on_press=self.__on_press__, on_release=self.__on_release__) self.listener.start() self.listener.wait() self.__client_on_press = on_press self.__client_on_release = on_release def __on_press__(self, key): try: ch = key.char if not self.Keys.contains(ch): self.__client_on_press(key) else: if self.keyEvents[ch] != ButtonEvent.BUTTON_DOWN: self.keyEvents[ch] = ButtonEvent.BUTTON_DOWN if self.keyHandler[ch] != None: self.keyHandler[ch](self.Keys.byvalue(ch), ButtonEvent.BUTTON_DOWN) # print('Key = {0}'.format(ch)) except AttributeError: self.__client_on_press(key) def __on_release__(self, key): try: ch = key.char if not self.Keys.contains(ch): self.__client_on_release(key) else: if self.keyEvents[ch] != ButtonEvent.BUTTON_UP: self.keyEvents[ch] = ButtonEvent.BUTTON_UP if self.keyHandler[ch] != None: self.keyHandler[ch](self.Keys.byvalue(ch), ButtonEvent.BUTTON_UP) except AttributeError: self.__client_on_release(key) # Register a callback method to register for the events def register_key_event_handler(self, callback, key=Keys.KEY1): self.keyHandler[key.value] = callback