|
| 1 | +import threading, time |
| 2 | + |
| 3 | +IN_WHILE_SLEEP = 0.01 |
| 4 | +PRESS_VS_LONGPRESS = 370/1000 |
| 5 | +LONGPRESS_VS_TOOLONGPRESS = 1600/1000 |
| 6 | +DOUBLEPRESS_BETWEEN = 300/1000 |
| 7 | +CALL_THREADED = True |
| 8 | + |
| 9 | + |
| 10 | +## pynput ## |
| 11 | +import pynput.keyboard |
| 12 | + |
| 13 | +pynput_keys_DOWN = [] |
| 14 | + |
| 15 | +def pynput_on_press_lambda (key): |
| 16 | + global pynput_keys_DOWN |
| 17 | + if not key in pynput_keys_DOWN: |
| 18 | + pynput_keys_DOWN.append(key) |
| 19 | + |
| 20 | +def pynput_on_release_lambda (key): |
| 21 | + global pynput_keys_DOWN |
| 22 | + pynput_keys_DOWN=[x for x in pynput_keys_DOWN if x != key] |
| 23 | + |
| 24 | +pynput_listener = pynput.keyboard.Listener( |
| 25 | + on_press=pynput_on_press_lambda, |
| 26 | + on_release=pynput_on_release_lambda |
| 27 | + ).start() |
| 28 | + |
| 29 | +def pynput_pressed(keys=[]): # are these keys pressed? dont care about other keys |
| 30 | + for k in keys: |
| 31 | + if k not in pynput_keys_DOWN: |
| 32 | + return False |
| 33 | + return True |
| 34 | + |
| 35 | +def pynput_pressed_exclusively(keys=[]): # are exactly these keys pressed? other keys cant be pressed |
| 36 | + if len(pynput_keys_DOWN) == len(keys): |
| 37 | + return pynput_pressed(keys) |
| 38 | + else: |
| 39 | + #print(pynput_keys_DOWN) |
| 40 | + return False |
| 41 | +## pynput ## |
| 42 | + |
| 43 | +''' |
| 44 | +## keyboard ## - keyboard library can be also used (but was removed because contained bugs) |
| 45 | +import keyboard |
| 46 | +
|
| 47 | +def keyboard_pressed(keys=[]): ## IF PRESSED + DONT CARE ABOUT OTHER KEYS => ITS NOT EXCLUSIVELY PRESSED |
| 48 | + for k in keys: |
| 49 | + if not keyboard.is_pressed(k): |
| 50 | + return False |
| 51 | + return True |
| 52 | + |
| 53 | + ## keyboard ## |
| 54 | +''' |
| 55 | + |
| 56 | + |
| 57 | + |
| 58 | +def pressed (keys=[]): |
| 59 | + # TODO: convert keys=<string list> to pynput key list |
| 60 | + #return keyboard_pressed(keys) |
| 61 | + return pynput_pressed_exclusively(keys) |
| 62 | + |
| 63 | + |
| 64 | + |
| 65 | +def _keys_are_same(k1, k2): |
| 66 | + if len(k1) == len(k2): |
| 67 | + for k in k1: |
| 68 | + if not k in k2: |
| 69 | + return False |
| 70 | + return True |
| 71 | + else: |
| 72 | + return False |
| 73 | + |
| 74 | +def _handler_is_running(id): |
| 75 | + global registered_hotkeys |
| 76 | + for r in registered_hotkeys: |
| 77 | + if id in r: |
| 78 | + return True |
| 79 | + return False |
| 80 | + |
| 81 | +def _get_handler_id(k): |
| 82 | + global registered_hotkeys |
| 83 | + for i in range(len(registered_hotkeys)): |
| 84 | + if _keys_are_same(registered_hotkeys[i][0],k): |
| 85 | + return registered_hotkeys[i][-1] |
| 86 | + return 0 |
| 87 | + |
| 88 | + |
| 89 | +## HAMDLERS ## |
| 90 | + |
| 91 | +#TODO: def key_press_time(...): # return values to callback function (for custom press types) |
| 92 | +#TODO: call_function (keydown_or_keyup, number, time_before, duration, [<the_real_order_of_keys>]) |
| 93 | + |
| 94 | +def press (keys=[], on_press=lambda: None): |
| 95 | + id = _get_handler_id(keys) |
| 96 | + while True: |
| 97 | + while not pressed(keys): |
| 98 | + time.sleep(IN_WHILE_SLEEP) |
| 99 | + if not _handler_is_running(id): return |
| 100 | + |
| 101 | + on_press() |
| 102 | + |
| 103 | + while pressed(keys): |
| 104 | + time.sleep(IN_WHILE_SLEEP) |
| 105 | + |
| 106 | +def press_longpress_toolongpress (keys=[], on_press=lambda: None, on_longpress=lambda: None, on_toolongpress=lambda: None): |
| 107 | + id = _get_handler_id(keys) |
| 108 | + while True: |
| 109 | + while not pressed(keys): |
| 110 | + time.sleep(IN_WHILE_SLEEP) |
| 111 | + if not _handler_is_running(id): return |
| 112 | + |
| 113 | + t = time.time() |
| 114 | + |
| 115 | + while pressed(keys): |
| 116 | + time.sleep(IN_WHILE_SLEEP) |
| 117 | + |
| 118 | + t = time.time() - t |
| 119 | + |
| 120 | + if t < PRESS_VS_LONGPRESS: |
| 121 | + on_press() |
| 122 | + elif t < LONGPRESS_VS_TOOLONGPRESS: |
| 123 | + on_longpress() |
| 124 | + else: |
| 125 | + on_toolongpress() |
| 126 | + |
| 127 | +def press_doublepress_longpress_toolongpress (keys=[], on_press=lambda: None, on_doublepress=lambda: None, on_longpress=lambda: None, on_toolongpress=lambda: None): |
| 128 | + id = _get_handler_id(keys) |
| 129 | + while True: |
| 130 | + while not pressed(keys): |
| 131 | + time.sleep(IN_WHILE_SLEEP) |
| 132 | + if not _handler_is_running(id): return |
| 133 | + |
| 134 | + t = time.time() |
| 135 | + |
| 136 | + while pressed(keys): |
| 137 | + time.sleep(IN_WHILE_SLEEP) |
| 138 | + |
| 139 | + t = time.time() - t |
| 140 | + |
| 141 | + if t < PRESS_VS_LONGPRESS: |
| 142 | + tt = time.time() |
| 143 | + while not pressed(keys) and time.time()-tt <= DOUBLEPRESS_BETWEEN: |
| 144 | + time.sleep(IN_WHILE_SLEEP) |
| 145 | + |
| 146 | + tt = time.time()-tt |
| 147 | + if tt < DOUBLEPRESS_BETWEEN: |
| 148 | + on_doublepress() |
| 149 | + else: |
| 150 | + on_press() |
| 151 | + |
| 152 | + while pressed(keys): |
| 153 | + time.sleep(IN_WHILE_SLEEP) |
| 154 | + |
| 155 | + elif t < LONGPRESS_VS_TOOLONGPRESS: |
| 156 | + on_longpress() |
| 157 | + else: |
| 158 | + on_toolongpress() |
| 159 | + |
| 160 | + |
| 161 | + |
| 162 | +registered_hotkeys = [] # [keys,on_..., inique_id] |
| 163 | +last_thread_number = 0 |
| 164 | + |
| 165 | +def catch_hotkey (timeout=10): |
| 166 | + global pynput_keys_DOWN |
| 167 | + t = time.time() + timeout |
| 168 | + keys = [] |
| 169 | + while t > time.time(): |
| 170 | + if len(keys) < len(pynput_keys_DOWN): |
| 171 | + keys = pynput_keys_DOWN |
| 172 | + elif len(keys) > len(pynput_keys_DOWN): |
| 173 | + return keys |
| 174 | + time.sleep(IN_WHILE_SLEEP) |
| 175 | + |
| 176 | + |
| 177 | +def add_hotkey (keys=[], _callback_press=None, _callback_longpress=None, _callback_toolongpress=None, _callback_doublepress=None): |
| 178 | + global registered_hotkeys, last_thread_number |
| 179 | + |
| 180 | + for i in range(len(registered_hotkeys)): |
| 181 | + if _keys_are_same(registered_hotkeys[i][0], keys): |
| 182 | + registered_hotkeys.pop(i) |
| 183 | + break |
| 184 | + |
| 185 | + if CALL_THREADED: |
| 186 | + callback_press = lambda: threading.Thread(target=_callback_press).start() # if _callback_press is not None else None |
| 187 | + callback_longpress = lambda: threading.Thread(target=_callback_longpress).start() |
| 188 | + callback_toolongpress = lambda: threading.Thread(target=_callback_toolongpress).start() |
| 189 | + callback_doublepress = lambda: threading.Thread(target=_callback_doublepress).start() |
| 190 | + else: |
| 191 | + callback_press = _callback_press |
| 192 | + callback_longpress = _callback_longpress |
| 193 | + callback_toolongpress = _callback_toolongpress |
| 194 | + callback_doublepress = _callback_doublepress |
| 195 | + |
| 196 | + |
| 197 | + last_thread_number = last_thread_number+1 |
| 198 | + registered_hotkeys.append([keys, callback_press, callback_longpress, callback_toolongpress, callback_doublepress, last_thread_number]) |
| 199 | + |
| 200 | + |
| 201 | + if _callback_press is None and _callback_doublepress is None and _callback_longpress is None and _callback_toolongpress is None: # remove |
| 202 | + return |
| 203 | + elif _callback_press is not None and (_callback_doublepress is None and _callback_longpress is None and _callback_toolongpress is None): # press |
| 204 | + th = threading.Thread(target=press, args=(keys, callback_press)) |
| 205 | + elif _callback_doublepress is None: # press_longpress_toolongpress |
| 206 | + th = threading.Thread(target=press_longpress_toolongpress, args=(keys, callback_press, callback_longpress, callback_toolongpress)) |
| 207 | + else: |
| 208 | + th = threading.Thread(target=press_doublepress_longpress_toolongpress, args=(keys, callback_press, callback_doublepress, callback_longpress, callback_toolongpress)) # press_doublepress_longpress_toolongpress |
| 209 | + |
| 210 | + th.daemon = True |
| 211 | + th.start() |
| 212 | + |
| 213 | + |
0 commit comments