macOS启动pynput键盘监听器致Tkinter应用崩溃问题求助
macOS下Tkinter按钮启动pynput键盘监听器崩溃(exit code 133 SIGTRAP)解决方法
问题表现
在macOS系统下,通过Tkinter按钮的command回调函数启动pynput键盘监听器时,应用直接崩溃,控制台输出报错:
Process finished with exit code 133 (interrupted by signal 5: SIGTRAP)
直接通过终端运行脚本时,终端输出:
zsh: trace trap path/to/py/script
相同代码在Windows 11系统下可正常运行,尝试将监听器放到独立新线程中启动,崩溃问题依然存在。
运行环境
- 系统:macOS Monterey 12.4(M1 PRO芯片)
- Python版本:3.9
- pynput版本:1.7.6
复现代码
import tkinter from tkinter import Tk, Button from pynput import keyboard logo = b'iVBORw0KGgoAAAANSUhEUgAAACUAAAAlCAYAAADFniADAAAACXBIWXMAAAsSAAALEgHS3X78AAACOElEQVRYhc2Yy23CQBCG/1jcoYO4ATuc7GPcQdIB3H2hgzgdEMm+Ox2ECmKOvhFXAB1ABURjjdGyLHgX/PqlFcKP3Y+Z3WFmno7HI0zlJv4UQMBjAsAG8MzTrPkzA7ApwvzHdH5tKDfxafE5gIUAoKMDAAJbFmG+aQzKTfyIYcYGMCqtaJ4izLd3Q7GbUgAvD8KIOjBYagzlJn7AZn/UOtf0XYT5XBvKTfxq7+wNFnkFsAMgu6Y6BGvFO3QQFhdXCUocTuxNndjbO7EXyPduDSf2jk7sRYr5IrqneteJvcyJvVS+bkkWmvBRbstlKs3YMydZ0kNpx0CVlm7i2xdQvLHfegACGyKqvoyEG5H6eSPZ/ONE2ZoTkBsjimElFMej1wagZjzuFRlmXrlPGS960DuEPSWbvC+Nyf0Wh4Em/0YeVUCWmg4IiGRbnA8NSfaoK0tx+nNhFdWzI9XFlvShO+3IMBOo02cR5tpB2E38TBUfaU9ppahdymrYUk0osziZPwwIalNF9KxnEFFZBWVcm7WkVRHm+xKKK4shuLA0jph5LvtjKbWryi4ZatcfE05VzQmKfNljXrUSew5nhUMR5nQKvzoG2snGuFaMphzpTaL9L1W9XBHpKuIS/mydW2V7+mC+XSc67YGqEyPXfSdxnd+WK/8oZbrWGqptBXHJlBr2pG6pNpMwaZrNeQ/cC0f7LarrTRlBCXBUBtEgC9YBUpOMTnTKIUdLd/U8BUDK71Xp9FbHIkoB+AdGxhvJUKsuTwAAAABJRU5ErkJggg==' root = Tk() root.title("Test window") root.geometry("400x200") core_logo_icon = tkinter.PhotoImage(data=logo) def start_listener(): def on_press(key): print("Key ", key, " pressed") def on_release(key): print("Key ", key, " released") keyboard_listener = keyboard.Listener(on_press=on_press, on_release=on_release) keyboard_listener.start() print("Keyboard listener ON") button = Button(root, command=start_listener, image=core_logo_icon, borderwidth=0) button.pack() root.mainloop()
问题原因
- macOS下pynput的键盘监听器依赖系统辅助功能权限,权限缺失时底层Quartz事件框架会直接触发SIGTRAP信号终止进程,不会抛出Python层可捕获的异常。
- pynput的macOS后端基于CFRunLoop实现,监听器实例必须在Tkinter主事件循环(
mainloop())启动前、主线程上下文中完成初始化,在按钮回调(运行在Tk事件循环线程中)中创建监听器实例会触发底层断言崩溃,手动开新线程也无法规避——实例创建时的线程上下文不符合要求就会触发trap。
解决步骤
- 配置系统辅助功能权限
打开「系统设置」→「隐私与安全性」→「辅助功能」,将你运行脚本使用的终端(Terminal/iTerm2等)或代码IDE加入允许列表,勾选开启权限。如果之前已经添加过对应程序,先删除条目重新添加,权限配置完成后完全重启终端/IDE再运行脚本。 - 调整代码逻辑,提前初始化监听器实例
不要在按钮回调函数中创建keyboard.Listener实例,把实例初始化逻辑移到Tk主循环启动前的主线程中,按钮回调仅负责启动未运行的监听器即可。修正后的代码如下:
import tkinter from tkinter import Tk, Button from pynput import keyboard logo = b'iVBORw0KGgoAAAANSUhEUgAAACUAAAAlCAYAAADFniADAAAACXBIWXMAAAsSAAALEgHS3X78AAACOElEQVRYhc2Yy23CQBCG/1jcoYO4ATuc7GPcQdIB3H2hgzgdEMm+Ox2ECmKOvhFXAB1ABURjjdGyLHgX/PqlFcKP3Y+Z3WFmno7HI0zlJv4UQMBjAsAG8MzTrPkzA7ApwvzHdH5tKDfxafE5gIUAoKMDAAJbFmG+aQzKTfyIYcYGMCqtaJ4izLd3Q7GbUgAvD8KIOjBYagzlJn7AZn/UOtf0XYT5XBvKTfxq7+wNFnkFsAMgu6Y6BGvFO3QQFhdXCUocTuxNndjbO7EXyPduDSf2jk7sRYr5IrqneteJvcyJvVS+bkkWmvBRbstlKs3YMydZ0kNpx0CVlm7i2xdQvLHfegACGyKqvoyEG5H6eSPZ/ONE2ZoTkBsjimElFMej1wagZjzuFRlmXrlPGS960DuEPSWbvC+Nyf0Wh4Em/0YeVUCWmg4IiGRbnA8NSfaoK0tx+nNhFdWzI9XFlvShO+3IMBOo02cR5tpB2E38TBUfaU9ppahdymrYUk0osziZPwwIalNF9KxnEFFZBWVcm7WkVRHm+xKKK4shuLA0jph5LvtjKbWryi4ZatcfE05VzQmKfNljXrUSew5nhUMR5nQKvzoG2snGuFaMphzpTaL9L1W9XBHpKuIS/mydW2V7+mC+XSc67YGqEyPXfSdxnd+WK/8oZbrWGqptBXHJlBr2pG6pNpMwaZrNeQ/cC0f7LarrTRlBCXBUBtEgC9YBUpOMTnTKIUdLd/U8BUDK71Xp9FbHIkoB+AdGxhvJUKsuTwAAAABJRU5ErkJggg==' # 提前定义回调函数,不要嵌套在按钮回调里 def on_press(key): print("Key ", key, " pressed") def on_release(key): print("Key ", key, " released") root = Tk() root.title("Test window") root.geometry("400x200") core_logo_icon = tkinter.PhotoImage(data=logo) # 主循环启动前,主线程中初始化监听器实例 keyboard_listener = keyboard.Listener(on_press=on_press, on_release=on_release) def start_listener(): # 加判断避免重复启动报错 if not keyboard_listener.is_alive(): keyboard_listener.start() print("Keyboard listener ON") button = Button(root, command=start_listener, image=core_logo_icon, borderwidth=0) button.pack() root.mainloop()
- M1芯片设备额外检查
确认安装的Python是arm64原生版本,不要通过Rosetta 2转译运行x86版本的Python,转译环境下pynput和Tkinter的事件循环交互存在已知兼容问题,会偶发崩溃。
内容的提问来源于stack exchange,提问作者Tomáš Sýkora
相关产品推荐
相关产品推荐

