使我的PyQt应用程序从控制台中退出时(Ctrl-C),退出的正确方法是什么?

问题描述:

从控制台(Ctrl-C)终止时,使我的PyQt应用程序退出的正确方法是什么?

What is the correct way to make my PyQt application quit when killed from the console (Ctrl-C)?

当前(我在处理unix信号方面没有做任何特别的事情),我的PyQt应用程序忽略了SIGINT(Ctrl + C).我希望它表现良好并在被杀死时退出.我该怎么办?

Currently (I have done nothing special to handle unix signals), my PyQt application ignores SIGINT (Ctrl+C). I want it to behave nicely and quit when it is killed. How should I do that?

17.4. signal —设置异步事件的处理程序

尽管就Python用户而言,Python信号处理程序是异步调用的,但它们只能出现在Python解释器的原子"指令之间.这意味着纯粹在C语言中执行的长时间计算过程中到达的信号(例如,大文本正文中的正则表达式匹配)可能会延迟任意时间.

Although Python signal handlers are called asynchronously as far as the Python user is concerned, they can only occur between the "atomic" instructions of the Python interpreter. This means that signals arriving during long calculations implemented purely in C (such as regular expression matches on large bodies of text) may be delayed for an arbitrary amount of time.

这意味着在Qt事件循环运行时Python无法处理信号.只有当Python解释器运行时(当QApplication退出时,或者当从Qt调用Python函数时),信号处理程序才会被调用.

That means Python cannot handle signals while the Qt event loop is running. Only when the Python interpreter run (when the QApplication quits, or when a Python function is called from Qt) the signal handler will be called.

一种解决方案是使用QTimer让解释器不时运行.

A solution is to use a QTimer to let the interpreter run from time to time.

请注意,在下面的代码中,如果没有打开的窗口,则无论用户选择如何,应用程序都将在消息框后退出,因为QApplication.quitOnLastWindowClosed()== True.可以更改此行为.

Note that, in the code below, if there are no open windows, the application will quit after the message box regardless of the user's choice because QApplication.quitOnLastWindowClosed() == True. This behaviour can be changed.

import signal
import sys

from PyQt4.QtCore import QTimer
from PyQt4.QtGui import QApplication, QMessageBox

# Your code here

def sigint_handler(*args):
    """Handler for the SIGINT signal."""
    sys.stderr.write('\r')
    if QMessageBox.question(None, '', "Are you sure you want to quit?",
                            QMessageBox.Yes | QMessageBox.No,
                            QMessageBox.No) == QMessageBox.Yes:
        QApplication.quit()

if __name__ == "__main__":
    signal.signal(signal.SIGINT, sigint_handler)
    app = QApplication(sys.argv)
    timer = QTimer()
    timer.start(500)  # You may change this if you wish.
    timer.timeout.connect(lambda: None)  # Let the interpreter run each 500 ms.
    # Your code here.
    sys.exit(app.exec_())

LinearOrbit 指出的另一种可能的解决方案是,但它不允许自定义处理程序.

Another possible solution, as pointed by LinearOrbit, is signal.signal(signal.SIGINT, signal.SIG_DFL), but it doesn't allow custom handlers.