当前位置: 首页 > 知识库问答 >
问题:

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

乔望
2023-03-14

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(什么都没发生)
  • 关闭窗口
  • "退出..."被打印,我得到一个关于循环已经退出的错误。

这是怎么回事,我怎么能让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()

共有3个答案

桂志新
2023-03-14

下面是一个在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()
郤浩慨
2023-03-14

由于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)。

轩辕弘雅
2023-03-14

正确的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,因为用户已经登录。 但是,如果我退出该应用程序并重新启动它,则始终为零