Search code examples
pythontkinterwindowlistenerpynput

How to run pynput.Listener simultaneously with tkinter.Tk().mainloop()


I am a teacher. I teach math, but since education is facing human resources crisis, I have some additional duties. I teach kids a bit of programming, they do quite well. Now I'd like to make with them a snake game, but I have a problem achieving multithreading in my GUI app.

I found similar cases but no solutions. Like here: Using the keyboard to move the character in a maze and here: Tkinter.Tk() and threading

def on_press(key):
    print('{0} pressed'.format(key))

def on_release(key):
    if key == Key.esc:
        return False

with Listener(on_press=on_press, on_release=on_release) as listener:
    listener.join()

root = Tk()
root.mainloop()

I expected the window to run simultaneously with the listener. instead, my code listens to the keyboard and then (after I shoot the listener down) pops the window up. it reverses when I call the listener after calling the mainloop, then first the window appears and after I shoot it down the listener is starting to work.


Solution

  • You don't need Listener in tkinter. You can use root.bind to assign function to events press and release.

    from tkinter import *
    
    def on_press(event):
        #print('on_press: event:', event)
        #print('on_press: keysym:', event.keysym)
        print('{0} pressed'.format(event.keysym))
    
    def on_release(event):
        #print('on_release: event:', event)
        #print('on_release: keysym:', event.keysym)
        print('{0} release'.format(event.keysym))
    
        if event.keysym == 'Escape':
             print("exist program")
             root.destroy()
    
    root = Tk()
    
    root.bind('<KeyPress>', on_press)
    root.bind('<KeyRelease>', on_release)
    
    root.mainloop()
    

    You can also assign function to every key separatelly

    from tkinter import *
    
    def on_escape(event):
        print("exist program")
        root.destroy()
    
    root = Tk()
    
    root.bind('<Escape>', on_escape)
    #root.bind('<KeyPress-Escape>', on_press_escape)
    #root.bind('<KeyRelease-Escape>', on_release_escape)
    
    root.mainloop()
    

    Keysyms in Tcl/Tk documentation: https://www.tcl.tk/man/tcl8.4/TkCmd/keysyms.htm


    BTW:

    If you want to run tkinter and pynput at the same time then you have to do it before join()

    with Listener(on_press=on_press, on_release=on_release) as listener:
    
        root = Tk()
        root.mainloop()
    
        #listener.stop()
        listener.join()
    

    or

    listener = Listener(on_press=on_press, on_release=on_release)
    listener.start()
    
    root = Tk()
    root.mainloop()
    
    #listener.stop()
    listener.join()