使用 Ctrl-C 退出 tkinter 应用程序并捕获 SIGINT

joz*_*yqk 5 python multithreading signals tkinter

Ctrl-C/SIGTERM/SIGINT 似乎被 tkinter 忽略。通常可以通过回调再次捕获它。这似乎不起作用,所以我想我应该在另一个线程中运行 tkinter ,因为它的mainloop() 是一个无限循环和 block。实际上我也想这样做以在单独的线程中从标准输入读取。即使在此之后,Ctrl-C 仍然不会被处理,直到我关闭窗口。这是我的 MWE:

#! /usr/bin/env python
import Tkinter as tk
import threading
import signal
import sys

class MyTkApp(threading.Thread):
    def run(self):
        self.root = tk.Tk()
        self.root.mainloop()

app = MyTkApp()
app.start()

def signal_handler(signal, frame):
    sys.stderr.write("Exiting...\n")

    # think only one of these is needed, not sure
    app.root.destroy()
    app.root.quit()

signal.signal(signal.SIGINT, signal_handler)
Run Code Online (Sandbox Code Playgroud)

结果:

  • 运行应用程序
  • 在终端中按 Ctrl-C(没有任何反应)
  • 关上窗户
  • 打印“Exiting...”,并且收到有关循环已退出的错误。

这是怎么回事?如何让终端中的 Ctrl-C 关闭应用程序?


更新:按照建议添加 poll,在主线程中工作,但在另一个线程中启动时没有帮助......

class MyTkApp(threading.Thread):
    def poll(self):
        sys.stderr.write("poll\n")
        self.root.after(50, self.poll)

    def run(self):
        self.root = tk.Tk()
        self.root.after(50, self.poll)
        self.root.mainloop()
Run Code Online (Sandbox Code Playgroud)

kdu*_*ubs 6

这是一个在窗口或命令行中捕获 control c 的工作示例。这是用 3.7.2 进行测试的,这似乎比其他解决方案更简单。我几乎感觉自己失去了一些东西。

import tkinter as TK

import signal

def hello():
    print("Hello")

root = TK.Tk()

TK.Button(root, text="Hi", command=(hello)).pack(  )

def handler(event):
    root.destroy()
    print('caught ^C')

def check():
    root.after(500, check)  #  time in ms.

# the or is a hack just because I've shoving all this in a lambda. setup before calling main loop
signal.signal(signal.SIGINT, lambda x,y : print('terminal ^C') or handler(None))

# this let's the terminal ^C get sampled every so often
root.after(500, check)  #  time in ms.

root.bind_all('<Control-c>', handler)
 
root.mainloop()
Run Code Online (Sandbox Code Playgroud)


acw*_*668 4

由于您的 tkinter 应用程序在另一个线程中运行,因此您不需要在主线程中设置信号处理程序,只需在语句后使用以下代码块app.start()

import time

while app.is_alive():
    try:
        time.sleep(0.5)
    except KeyboardInterrupt:
        app.root.destroy()
        break
Run Code Online (Sandbox Code Playgroud)

然后,您可以使用Ctrl-C引发KeyboardInterrupt异常来关闭 tkinter 应用程序并中断 while 循环。如果您关闭 tkinter 应用程序,while 循环也将终止。

请注意,上述代码仅适用于 Python 2(如您Tkinter在代码中使用的那样)。