Ctrl-C/SIGTERM/SIGINT似乎被tkinter忽略。通常情况下,它可以通过回调再次捕获。这似乎不起作用,所以我想我应该在另一个线程中运行tkinter,因为它的main循环()是一个无限循环和块。实际上,我也想这样做,以便在一个单独的线程中阅读stdin。即使在此之后,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)
结果:
这是怎么回事,我怎么能让Ctrl-C从终端关闭应用程序?
更新:按照建议添加轮询在主线程中有效,但在另一个线程中启动时没有帮助。。。
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()
下面是一个在windows中或从命令行捕获控件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()
由于tkinter应用程序正在另一个线程中运行,因此不需要在主线程中设置信号处理程序,只需在应用程序后使用以下代码块即可。start()
语句:
import time
while app.is_alive():
try:
time.sleep(0.5)
except KeyboardInterrupt:
app.root.destroy()
break
然后,您可以使用Ctrl-C引发键盘中断
异常以关闭tkinter应用程序并中断while循环。如果关闭tkinter应用程序,while循环也将终止。
请注意,上述代码仅在Python2中工作(因为您在代码中使用了Tkinter
)。
正确的CTRL-C
问题是你正在退出主线程,所以信号处理器基本上没有用。你需要保持它运行,在一会儿循环,或我的个人偏好,事件从线程模块。您也可以只捕获由CTRL-C事件生成的KeyboardInter0009
异常,而不是处理信号处理程序。
特金特的信号
使用tkinter,您必须让tkinter应用程序在单独的线程中运行,以便它不会干扰信号处理程序或键盘中断
异常。在处理程序中,要退出,需要销毁然后更新tkinter root。更新允许tkinter进行更新,以使其关闭,而无需等待mainloop。否则,用户必须单击活动窗口以激活mainloop。
# Python 3
from tkinter import *
from threading import Thread
import signal
class MyTkApp(Thread):
def run(self):
self.root = Tk()
self.root.mainloop()
def sigint_handler(sig, frame):
app.root.quit()
app.root.update()
app = MyTkApp()
# Set signal before starting
signal.signal(signal.SIGINT, sigint_handler)
app.start()
注意:如果您将处理程序设置在与tkinter主循环相同的线程中,也可以捕获SIGINTs,但是您需要在信号之后使tkinter窗口处于活动状态,以便它的主循环可以运行。除非你在新的线程中运行,否则没有办法解决这个问题。
更多关于Tkinter的信息
有关tkinter和命令行之间通信的更多信息,请参阅在不使用Mainloop的情况下使用tkinter。基本上,您可以在循环中使用update方法,然后与其他线程和进程通信,等等。我个人不建议这样做,因为您基本上是在做python线程控制系统的工作,这可能与您想要做的相反。(python有一个在一个外部线程中运行所有内部线程的进程,因此您不能利用多线程,除非使用多处理模块)
# Python 2
from Tkinter import *
ROOT = Tk()
LABEL = Label(ROOT, text="Hello, world!")
LABEL.pack()
LOOP_ACTIVE = True
while LOOP_ACTIVE:
ROOT.update()
USER_INPUT = raw_input("Give me your command! Just type \"exit\" to close: ")
if USER_INPUT == "exit":
ROOT.quit()
LOOP_ACTIVE = False
else:
LABEL = Label(ROOT, text=USER_INPUT)
LABEL.pack()
问题内容: 如何在多进程python程序中捕获+ ,并正常退出所有进程,我需要在Unix和Windows上均可使用的解决方案。我尝试了以下方法: 这是可行的,但我认为这不是正确的解决方案。 问题答案: 正确的方法来处理/用是: 在创建流程之前,请忽略该流程。这样创建的子进程继承了处理程序。 创建a之后,在父进程中还原原始处理程序。 使用和代替阻塞和。 等待结果超时,因为默认阻塞将等待忽略所有信号。
问题内容: 使我的PyQt应用程序从控制台中退出时(Ctrl-C),退出的正确方法是什么? 当前(我在处理unix信号方面没有做任何特别的事情),我的PyQt应用程序忽略了SIGINT(Ctrl + C)。我希望它表现良好并在被杀死时退出。我该怎么办? 问题答案: 17.4。signal —设置异步事件的处理程序 尽管就Python用户而言,Python信号处理程序是异步调用的,但它们只能出现在P
我正在开发移动应用程序与离子有飞溅 截至目前,我正在使用以下代码进行配置 此外,我在第一次启动页面上使用了双击退出,下面是一段代码。 所以在启动页面,若我在5秒内点击两次后退按钮,它就会关闭。我可以在“打开的应用程序”列表中看到应用程序仍处于打开状态。(android手机中的第一个或最后一个按钮)。 问题是,如果我在双击退出后重新启动应用程序,那么它会显示一段时间的白屏和启动屏幕(但没有闪屏)。我
问题内容: 如何在CLI(命令行界面)Java应用程序中截获 (通常会杀死进程)? 是否存在多平台解决方案(Linux,Solaris,Windows)? 我使用的是,但是如有必要,我可以使用其他方法从标准输入中读取字符。 问题答案: 这应该能够截获信号,但是仅作为JVM完全关闭自身之前的中间步骤,因此它可能不是你要注意的。 你需要使用截获 触发的信号C(在Unix和Windows上)。
问题内容: 用代码退出Java应用程序的最佳方法是什么? 问题答案: 您可以用于此目的。 根据oracle的Java 8文档: 终止当前正在运行的Java虚拟机。参数用作状态码;按照惯例, 非零状态代码表示异常终止 。 此方法在类Runtime中调用exit方法。此方法永远不会正常返回。 该调用实际上等效于该调用:
问题内容: 我正在开发使用Facebook SDK登录的ios应用程序。我在情节提要中将a设置为初始View Controller,用户从中使用FB帐户登录。 我有另一个ViewController,一旦用户登录,它将正确加载。 我正在检查AppDelegate文件,如果不是nil,则直接加载第二个ViewController,因为用户已经登录。 但是,如果我退出该应用程序并重新启动它,则始终为零