Search code examples
pythontkinterfocusbind

Monitor keypress in tkinter without focus


I am writing a small timing module in Python using tkinter. In that regard, I would like to globally monitor when the escape key is pressed for stopping the timing.

Unfortunately, tkinters ".bind" and ".bind_all" functions only pick up keystrokes when the window is in focus.

I have looked at several other solutions for logging keystrokes including the packages "keyboard" and "pynput", however these packages requires running a while loop which makes the tkinter GUI freeze up and stop working.

I found this thread, but it is not very helpful for specifically showing how it can be done: Detect keypress without drawing canvas or frame on tkinter

Some different options i tried

Option 1: Using the tkinter loop function, but doesn't register when key is pressed

import keyboard
def _check_esc_pressed(self):
    if self.run_active and keyboard.press('esc'):
        self.Lap()
        self.Stop()
    self.after(50, self._check_esc_pressed())

Option 2: Freezes the tkinter client

import keyboard  
def _check_esc_pressed(self):
    while True:  
        if keyboard.is_pressed('esc'): 
            self.Lap()
            self.Stop()
            break  
        else:
            pass

Option 3: Freezes the tkinter client

from pynput.keyboard import Key, Listener
def on_release(self, key):
    if key == Key.esc:
        self.Lap()
        self.Stop()
        # Stop listener
        return False

def _check_esc_pressed(self):
    def on_press(key):
        pass
    with Listener(
            on_press=on_press,
            on_release=on_release) as listener:
        listener.join()

I expect that pressing escape will terminate the "_check_esc_pressed" function, register a lap and stop the timer. The check for escape should only be processed while a run is active


Solution

  • I found a solution to the issue by using the system_hotkey package for python. This package allows you to assign system-wide hotkeys that work without focus on the tkinter program.

    from system_hotkey import SystemHotkey
    hk = SystemHotkey()
    hk.register(['alt', 'q'], callback=lambda event: self.Start())
    hk.register(['alt', 'w'], callback=lambda event: self.Stop())
    

    Bear in mind that any hotkey added like this will make the registered hotkey combination inaccessible to other programs.