【问题标题】:How to run pynput.Listener simultaneously with tkinter.Tk().mainloop()如何与 tkinter.Tk().mainloop() 同时运行 pynput.Listener
【发布时间】:2019-11-14 06:04:40
【问题描述】:

我是一名教师。我教数学,但由于教育面临人力资源危机,我还有一些额外的职责。我教孩子们一些编程,他们做得很好。现在我想和他们一起做一个蛇游戏,但是在我的 GUI 应用程序中实现多线程时遇到了问题。

我发现了类似的案例,但没有解决方案。像这儿: Using the keyboard to move the character in a maze 和这里: 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()

我希望窗口与侦听器同时运行。相反,我的代码监听键盘,然后(在我击落监听器之后)弹出窗口。当我在调用主循环后调用监听器时它会反转,然后首先出现窗口,在我击落它之后,监听器开始工作。

【问题讨论】:

  • 我不知道为什么你需要 Listener 和线程来在 Tkinter 中创建游戏。你可以不使用线程(即使用tk.after())和不使用Listener 使用root.bind('<KeyPress>', on_press)root.bind('<KeyRelease>', on_release)
  • tkinter 示例使用root.bind() 在画布上移动桨(上/下):github.com/furas/python-examples/blob/master/tkinter/…
  • 你为什么不使用 tkinter 的监听事件的能力?为什么需要使用Listener 类?
  • 不了解 tkinter 解决方案。你太棒了,谢谢。现在一切正常。但是,可以肯定的是,既然你写了“你可以在没有线程的情况下做到这一点”,那么绑定不会创建一个线程吗?

标签: python tkinter window listener pynput


【解决方案1】:

Listener 是一个线程,所以如果你加入它,你的主线程将等到它结束继续处理。

你可以只创建一个不带with 语句的Listener 对象,它将沿着主线程运行(直到回调函数返回False

【讨论】:

    【解决方案2】:

    tkinter 中不需要 Listener。您可以使用root.bind 为事件按下和发布分配功能。

    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()
    

    您也可以为每个键单独分配功能

    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()
    

    Tcl/Tk 文档中的键符:https://www.tcl.tk/man/tcl8.4/TkCmd/keysyms.htm


    顺便说一句:

    如果你想同时运行tkinterpynput,那么你必须在join()之前完成

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

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

    【讨论】:

    • 谢谢弗拉斯。这比我一直在尝试的其他方法要简单得多。 pygame 有一个不错的键盘事件处理程序,但是 pygame 和 tkinter 之间存在很大的阻抗不匹配。 pynput 看起来很有希望,但又很难使用。您的方法的唯一小问题是,如果按住键,则会生成 KeyPress 事件流。
    • pynput Listener 也有同样的小问题 - 它还获取所有密钥,您必须对其进行过滤。据我所知,这个流是由系统生成的,而不是由tkinterpynput
    • 我怀疑我现在遇到了硬件扫描键盘方式的问题。我想检测何时按下多个键,但是我只能可靠地拾起两个同时按下的键:我可以在键盘的左端或右端拾起四个或五个键(例如 Q、W、E、R 和T),但中间只有两个(例如 G 和 H)
    • 据我所知,有些键盘无法发送更多键。您最终可以使用 dict {"Q": False, "W": False, ...}` 并在on_press 中获取按下的键并将其设置为True 在字典中并检查所有需要的键是否为True,并在on_release 中设置False在字典中。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-17
    • 1970-01-01
    • 2015-09-22
    • 2020-09-21
    • 1970-01-01
    • 1970-01-01
    • 2020-04-07
    相关资源
    最近更新 更多