You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tkinter多线程键盘监听GUI崩溃问题求助

问题分析

导致GUI崩溃并触发exit code 133 (SIGTRAP)的核心原因有三点:

  1. 线程类设计冲突:你的Listener继承了threading.Thread,却重写了start()方法,违背了Thread类的原生设计逻辑,导致线程管理混乱。
  2. 不必要的线程阻塞:start()方法中调用self.listener.join()会阻塞当前线程,直到键盘监听线程完全结束,这种阻塞会引发线程资源异常,触发系统信号。
  3. 冗余线程嵌套:按钮命令中额外创建线程去调用listener.start(),而keyboard.Listener本身就是线程类,这种嵌套会加剧线程调度冲突。
解决方案

方案一:重构Listener类(推荐)

移除不必要的threading.Thread继承,直接基于keyboard.Listener实现逻辑:

def print_running_threads():
    threads = threading.enumerate()
    for thread in threads:
        print(thread.name)

class Listener:
    def __init__(self, key, buttons):
        self.query = AIQuery(key)
        self.buttons = buttons
        self.listener = None

    def on_press(self, key):
        try:
            for button in self.buttons:
                if key.char == button.key:
                    # 把HTTP请求放到独立线程,避免阻塞监听
                    threading.Thread(target=self.query.makeQuery, args=(button.prompt,), daemon=True).start()
                    break
        except AttributeError:
            pass

    def start(self):
        if self.listener:
            self.stop()
        # 直接启动键盘监听线程,无需手动join
        self.listener = keyboard.Listener(on_press=self.on_press)
        self.listener.start()

    def stop(self):
        if self.listener:
            self.listener.stop()
            self.listener.join()
            self.listener = None

    def update_buttons(self, new_buttons):
        self.buttons = new_buttons

同时简化按钮的command逻辑,无需额外创建线程:

self.run_btn = tk.Button(
    image=self.run_btn_img,
    borderwidth=0,
    highlightthickness=0,
    command=self.master.listener.start,
    relief="flat"
)

方案二:保留Thread继承(规范实现)

如果必须基于threading.Thread,需要正确实现run()方法:

import time

def print_running_threads():
    threads = threading.enumerate()
    for thread in threads:
        print(thread.name)

class Listener(threading.Thread):
    def __init__(self, key, buttons):
        super().__init__(daemon=True)  # 设置为守护线程,不阻塞程序退出
        self.query = AIQuery(key)
        self.buttons = buttons
        self.listener = None
        self.running = False

    def on_press(self, key):
        try:
            for button in self.buttons:
                if key.char == button.key:
                    threading.Thread(target=self.query.makeQuery, args=(button.prompt,), daemon=True).start()
                    break
        except AttributeError:
            pass

    def run(self):
        self.running = True
        self.listener = keyboard.Listener(on_press=self.on_press)
        self.listener.start()
        # 保持线程存活,直到收到停止信号
        while self.running:
            time.sleep(0.1)
        self.listener.stop()
        self.listener.join()

    def stop(self):
        self.running = False

    def update_buttons(self, new_buttons):
        self.buttons = new_buttons

按钮命令改为调用Thread原生的start()方法:

command=lambda: self.master.listener.start()
注意事项
  • 运行程序时需确保拥有系统级权限:Linux/macOS需用sudo启动,Windows需以管理员身份运行,因为全局键盘监听需要系统权限。
  • 所有耗时操作(比如HTTP请求makeQuery)必须放到独立线程中,绝对不能阻塞GUI线程或键盘监听线程。

内容的提问来源于stack exchange,提问作者Cham

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 02:12:17