tkinter似乎忽略了Ctrl-C / SIGTERM / SIGINT。通常它可以是captured again with a callback。这似乎没有用,所以我认为自in another thread以来我就会运行tkinter mainloop() is an infinite loop and blocks。我实际上也想在另一个线程中从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?
更新:Adding a poll,as suggested,在主线程中工作但在另一个线程中启动时没有帮助......
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()
答案 0 :(得分:3)
Python中正确的CTRL-C和SIGINT用法
问题是您要退出主线程,因此信号处理程序基本上没有用。您需要使其循环运行,或者循环运行,或者根据我的个人喜好Events模块中的threading。您也可以只捕获CTRL-C事件生成的KeyboardInterrupt
异常,而不用处理信号处理程序。
Tkinter中的SIGINT
使用tkinter,必须使tkinter应用程序在单独的线程中运行,以免干扰信号处理程序或KeyboardInterrupt
异常。在处理程序中,要退出,您需要销毁然后更新tkinter根目录。 Update允许tkinter更新,使其关闭,而无需等待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 mainloop相同的线程中设置处理程序,也会捕获SIGINT,但是您需要在信号之后使tkinter窗口处于活动状态,以便它的mainloop可以运行。除非您在新线程中运行,否则无法解决此问题。
有关Tkinter和命令行通信的更多信息
有关tkinter与命令行之间通信的更多信息,请参见Using Tkinter Without Mainloop。基本上,您可以在循环中使用update方法,然后与其他线程和进程等进行通信。我个人不建议这样做,因为您实际上是在执行python线程控制系统的工作,这可能与您的工作相反想做。 (python具有在一个外部线程中运行所有内部线程的进程,因此,除非使用multiprocessing模块,否则您不会利用多头处理)
# 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()
答案 1 :(得分:2)
由于你的tkinter应用程序在另一个线程中运行,你不需要在主线程中设置信号处理程序,只需在int visits[] = null;
// Increments visit count of crewMemberName on planetNameVariable
visits = tm.get(planetNameVariable);
if (visits == null) {
tm.put(planetNameVariable, new int[14]);
visits = tm.get(planetNameVariable);
}
visits[indexDesired]++;
// Get an iterator
Iterator<String> iterator = tm.keySet().iterator();
while (iterator.hasNext()) {
String key = iterator.next();
int[] temp = tm.get(key);
if (temp != null) {
for (int i = 0; i < temp.length; i++) {
System.out.println(key + " " + temp[i]);
}
}
}
语句之后使用以下代码块:
app.start()
然后,您可以使用 Ctrl-C 引发import time
while app.is_alive():
try:
time.sleep(0.5)
except KeyboardInterrupt:
app.root.destroy()
break
异常以关闭tkinter应用并中断while循环。如果您关闭tkinter应用程序,也会终止while循环。
请注意,上面的代码仅适用于Python 2(在代码中使用KeyboardInterrupt
)。
答案 2 :(得分:1)
这是一个在窗口中或从命令行捕获控件 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()