【问题标题】:How can a Tkinter work with a Listener together?Tkinter 如何与 Listener 一起工作?
【发布时间】:2019-12-09 18:55:35
【问题描述】:

我写了一个 Tkinter,我希望有一个 Listener 来监控用户的键盘输入。但是当我使用mainloop()启动Tkinter时,Listener不能和它一起工作,直到我退出Tkinter才会启动。

我曾尝试在 Tkinter 子单元中添加此 Listener,但效果不一样。

def initialization():
    print("Starting...")
    print("Start listener...")
    with mouse.Listener(on_click=onMouseClick) as listener:
        listener.join()

if __name__ == "__main__" :
    root = tk.Tk()
    root.geometry('800x80')
    root.resizable(height=True, width=True)
    root.overrideredirect(False)
    root.title('vENC Console')

    OneBtn = Button(root, command=initialization, text="One Button", width='30')
    root.mainloop()

我怎样才能让他们一起工作?我需要使用多线程吗?

【问题讨论】:

  • 当用户关注或不关注 tkinter 时,您是否尝试监控鼠标点击?
  • @HenryYik 其实我想监听键盘输入,比如当用户发送 F5 键时,我会在 Tkinter 中做一些动作。
  • 他们的网站上有一个非阻塞的解决方案。 listener = mouse.Listener(...) 然后listener.start()
  • 我尝试使用 Thread.start 和 Join,但效果不一样。
  • 鼠标监听器是一个threading.Thread,所以如果你调用listen.join(),它会等待线程终止并阻塞tkinter的主线程。我不确定什么不一样 - 你能澄清一下吗?

标签: python python-3.x multithreading tkinter pynput


【解决方案1】:

与几乎所有的 GUI 工具包一样,Tkinter 是事件驱动的。这意味着 Tkinter 应该处理鼠标和键盘事件。 小部件可以通过为某些事件添加绑定来注册它们对事件感兴趣。 Tkinter 依赖工作的事件流。

基本上,mainloop 等待事件发生(键盘、鼠标、超时),然后调用任何已注册的回调。

添加另一个独立于 Tkinter 的事件处理程序会发生冲突。最重要的是,Tkinter 是 not 线程安全的。如果你必须使用线程,你应该确保只有主线程使用 Tkinter 函数和方法。

基本上,Tkinter 和 Listener 不能一起工作。

所以我建议你改用bind_all。 通过使用bind_all 方法(阅读它here),您可以在应用程序级别注册绑定,而不是针对特定小部件。

【讨论】:

    【解决方案2】:

    你可以这样写

    listener = mouse.Listener(on_click=onMouseClick)
    listener.start() # start thread
    
    root.mainloop()
    
    listener.stop()  # stop thread
    listener.join()  # wait till thread really ends its job
    

    并且不要在onMouseClick 中使用return False,因为它会结束监听器。

    tkinter 有自己的方法来获取键和鼠标事件,所以也许你应该使用它们。 Listener 如果您必须在 tkinter 的窗口最小化并且它没有从系统获取事件时捕获事件,那么它会很有用。


    编辑:

    import tkinter as tk
    from pynput import mouse
    
    def onMouseClick(*args):
        print(args)
    
    root = tk.Tk()
    
    listener = mouse.Listener(on_click=onMouseClick)
    listener.start() # start thread
    
    root.mainloop()
    
    listener.stop()  # stop thread
    listener.join()  # wait till thread really ends its job
    

    编辑:

    import tkinter as tk
    from pynput import mouse
    
    def onMouseClick(*args):
        print(args)
    
    def initialization():
        global listener
    
        print("Starting...")
        print("Start listener...")
        listener = mouse.Listener(on_click=onMouseClick)
        listener.start() # start thread
    
    if __name__ == "__main__" :
    
        listener = None   
    
        root = tk.Tk()
    
        btn = tk.Button(root, command=initialization, text="One Button")
        btn.pack()
    
        root.mainloop()
    
        # stop listener if it was created
        if listener: # if listener is not None:
            print("Stop listener...")
            listener.stop()  # stop thread
            listener.join()  # wait till thread really ends its job
    

    编辑:带有停止监听器按钮的示例

    import tkinter as tk
    from pynput import mouse
    
    def onMouseClick(*args):
        print(args)
    
    def on_start():
        global listener
    
        if not listener:
            print("Start listener...")
            listener = mouse.Listener(on_click=onMouseClick)
            listener.start() # start thread
        else:
            print("listener already running")
    
    def on_stop():
        global listener
    
        if listener:
            print("Stop listener...")
            listener.stop()  # stop thread
            listener.join()  # wait till thread really ends its job
            listener = None  # to inform that listener doesn't exist
        else:
            print("listener not running")
    
    if __name__ == "__main__" :
    
        print("Starting...")
    
        listener = None  # to keep listener
    
        root = tk.Tk()
    
        btn = tk.Button(root, command=on_start, text="Star Mouse Listener")
        btn.pack()
    
        btn = tk.Button(root, command=on_stop, text="Stop Mouse Listener")
        btn.pack()
    
        root.mainloop()
    
        # stop listener if it was created
        if listener: # if listener is not None:
            print("Stop listener...")
            listener.stop()  # stop thread
            listener.join()  # wait till thread really ends its job
    

    【讨论】:

    • 有效!但是为什么mainloop() 必须在listener.start()listener.join() 之间呢?我可以先使用mainloop,然后启动Listener,然后Join()吗?
    • mailoop() 一直有效,直到您关闭窗口 - 所以mainloop() 之后的侦听器将在您关闭窗口后创建。
    • 那么,当我使用 '''listener.start()'''' 启动监听器然后使用 '''mainloop()''' 时,mainloop 不会杀死监听器吗?只屏蔽监听器?那么join()会让它们一起工作吗?
    • 监听器创建单独的线程来运行它 - 所以mainloop() 不会阻止它,也不会杀死它。它们同时工作,但在不同的线程中。而join() 仅用于等待侦听器的关闭(线程的关闭)。您甚至可以跳过join(),但随后您可能会收到线程未正确关闭(或类似情况)的错误。
    • 是的,我删除了 join() 并再次尝试。当我杀死'''mainloop()''''时,监听器将同时被杀死。非常感谢!它帮助我更深入地理解线程!
    猜你喜欢
    • 2018-05-17
    • 2021-12-07
    • 1970-01-01
    • 2019-04-17
    • 1970-01-01
    • 2013-02-17
    • 2023-02-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多