如何让pynput.keyboard.listener线程在主应用终止后仍后台运行
问题解决方案
问题分析
- 关闭Tkinter窗口后,
mainloop()执行完毕,主进程直接退出,导致所有后台线程(包括pynput的Listener)被强制终止,来不及执行on_endsession()写入计数。 - 当前
KeypressCounter的run()方法启动Listener后立即结束,线程本身提前退出,Listener线程成为独立线程,但主进程退出时仍会被终止。
修正代码
1. 修改counter.py
import pynput.keyboard import threading from threading import Event class KeypressCounter(threading.Thread): def __init__(self): super().__init__(daemon=False) # 设为非守护线程,保证主进程等待它完成 self.key_count = 0 self.stop_event = Event() self.listener = None def run(self): # 创建Listener并保存引用 self.listener = pynput.keyboard.Listener(on_press=self.on_press, daemon=False) self.listener.start() # 循环等待停止信号,避免线程提前退出 while not self.stop_event.is_set(): self.listener.join(0.1) # 非阻塞等待,防止线程卡死 # 确保Listener正确停止 if self.listener.is_alive(): self.listener.stop() def on_press(self, key): self.key_count += 1 print(f"counter: {self.key_count}") if key == pynput.keyboard.Key.esc: print("esc pressed") self.on_endsession() def on_endsession(self): # 写入计数到文件 with open("key_count.txt", "w") as f: print("writing to file") f.write(str(self.key_count)) # 触发停止信号 self.stop_event.set() def main(): keypress_counter = KeypressCounter() keypress_counter.start() keypress_counter.join() # 等待线程自然结束 if __name__ == "__main__": main()
2. 修改app.py
from tkinter import * from counter import KeypressCounter # 全局保存计数器实例,方便窗口关闭时引用 key_counter = None def start(): global key_counter # 避免重复启动线程 if key_counter is None or not key_counter.is_alive(): key_counter = KeypressCounter() key_counter.start() print("监听线程已启动") def on_window_close(): # 销毁GUI窗口,但不直接退出主进程 window.destroy() # 等待后台线程自然结束(比如用户按esc触发退出) if key_counter is not None and key_counter.is_alive(): key_counter.join() window_size = '1200x600' window = Tk() window.geometry(window_size) # 重写窗口关闭事件,替换默认的直接退出行为 window.protocol("WM_DELETE_WINDOW", on_window_close) keyBoardFrame = Frame(window, width=100, height=100, bg='green') keyBoardFrame.pack() # 修复Frame未显示的问题 button = Button(keyBoardFrame, text='start', bg='red', fg='white', command=start) button.grid(row=0, column=0, sticky='nsew') window.mainloop()
关键改动说明
- counter.py:
- 让
KeypressCounter线程循环等待停止信号,避免提前退出,绑定Listener的生命周期。 - 确保Listener能被正确停止,防止资源泄漏。
- 让
- app.py:
- 用全局变量保存计数器实例,方便窗口关闭时控制线程。
- 重写窗口关闭事件,销毁窗口后让主进程等待后台线程自然结束,而非直接退出。
- 修复了Frame未
pack导致按钮不显示的问题。
修改后,点击GUI关闭按钮,窗口会消失,但后台监听线程会持续运行,直到用户按下esc键,此时计数会自动写入key_count.txt,线程终止后主进程才会退出。
内容的提问来源于stack exchange,提问作者akashKP
相关产品推荐
相关产品推荐

