17.4. signal — Set handlers for asynchronous events 尽管Python信号处理程序对Python用户来说是异步调用的,但它们只能在Python解释器的“原子”指令之间发生。这意味着在纯C语言实现的长时间计算(例如在大文本上的正则表达式匹配)中到达的信号可能会延迟任意长的时间。 这意味着当Qt事件循环运行时,Python不能处理信号,只有当Python解释器运行时(当QApplication退出时,或者当从Qt调用Python函数时),信号处理程序才会被调用。 一个解决方案是使用QTimer让解释器不时地运行。 注意,在下面的代码中,如果没有打开的窗口,应用程序将在消息框后退出,而不管用户的选择,因为QApplication.quitOnLastWindowClosed()== True。此行为可以更改。
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_())
18.8.1.1. Execution of Python signal handlers Python信号处理程序不会在低级(C)信号处理程序中执行。相反,低级信号处理程序设置一个标志,告诉虚拟机在稍后的时间点(例如在下一条字节码指令)执行相应的Python信号处理程序。这会产生以下后果: [...] 一个完全用C语言实现的长时间运行的计算(比如正则表达式匹配大量文本)可以不间断地运行任意长的时间,而不管接收到什么信号。当计算完成时,Python信号处理程序将被调用。 Qt事件循环是用C(++)实现的。这意味着,当它运行并且没有Python代码被调用时(例如,通过连接到Python插槽的Qt信号),信号被记录下来,但是Python信号处理程序没有被调用。
import sys, signal, socket
from PyQt4 import QtCore, QtNetwork
class SignalWakeupHandler(QtNetwork.QAbstractSocket):
def __init__(self, parent=None):
super().__init__(QtNetwork.QAbstractSocket.UdpSocket, parent)
self.old_fd = None
# Create a socket pair
self.wsock, self.rsock = socket.socketpair(type=socket.SOCK_DGRAM)
# Let Qt listen on the one end
self.setSocketDescriptor(self.rsock.fileno())
# And let Python write on the other end
self.wsock.setblocking(False)
self.old_fd = signal.set_wakeup_fd(self.wsock.fileno())
# First Python code executed gets any exception from
# the signal handler, so add a dummy handler first
self.readyRead.connect(lambda : None)
# Second handler does the real handling
self.readyRead.connect(self._readSignal)
def __del__(self):
# Restore any old handler on deletion
if self.old_fd is not None and signal and signal.set_wakeup_fd:
signal.set_wakeup_fd(self.old_fd)
def _readSignal(self):
# Read the written byte.
# Note: readyRead is blocked from occuring again until readData()
# was called, so call it, even if you don't need the value.
data = self.readData(1)
# Emit a Qt signal for convenience
self.signalReceived.emit(data[0])
signalReceived = QtCore.pyqtSignal(int)
app = QApplication(sys.argv)
SignalWakeupHandler(app)
signal.signal(signal.SIGINT, lambda sig,_: app.quit())
sys.exit(app.exec_())
import signal, sys
from PyQt4.QtGui import QApplication, QWidget # also works with PySide
# You HAVE TO reimplement QApplication.event, otherwise it does not work.
# I believe that you need some python callable to catch the signal
# or KeyboardInterrupt exception.
class Application(QApplication):
def event(self, e):
return QApplication.event(self, e)
app = Application(sys.argv)
# Connect your cleanup function to signal.SIGINT
signal.signal(signal.SIGINT, lambda *a: app.quit())
# And start a timer to call Application.event repeatedly.
# You can change the timer parameter as you like.
app.startTimer(200)
w = QWidget()
w.show()
app.exec_()
import signal
import PyQt4.QtGui
def handleIntSignal(signum, frame):
'''Ask app to close if Ctrl+C is pressed.'''
PyQt4.QtGui.qApp.closeAllWindows()
signal.signal(signal.SIGINT, handleIntSignal)
9条答案
按热度按时间hfyxw5xn1#
17.4. signal — Set handlers for asynchronous events
尽管Python信号处理程序对Python用户来说是异步调用的,但它们只能在Python解释器的“原子”指令之间发生。这意味着在纯C语言实现的长时间计算(例如在大文本上的正则表达式匹配)中到达的信号可能会延迟任意长的时间。
这意味着当Qt事件循环运行时,Python不能处理信号,只有当Python解释器运行时(当QApplication退出时,或者当从Qt调用Python函数时),信号处理程序才会被调用。
一个解决方案是使用QTimer让解释器不时地运行。
注意,在下面的代码中,如果没有打开的窗口,应用程序将在消息框后退出,而不管用户的选择,因为QApplication.quitOnLastWindowClosed()== True。此行为可以更改。
另一个可能的解决方案as pointed by LinearOrbit是
signal.signal(signal.SIGINT, signal.SIG_DFL)
,但它不允许自定义处理程序。dddzy1tm2#
如果您只是希望使用ctrl-c关闭应用程序-而不是“友好地”/优雅地关闭它-那么在http://www.mail-archive.com/pyqt@riverbankcomputing.com/msg13758.html中,您可以使用以下命令:
显然,这在Linux、Windows和OSX上都有效--到目前为止,我只在Linux上测试过(而且有效)。
kuarbcqp3#
18.8.1.1. Execution of Python signal handlers
Python信号处理程序不会在低级(C)信号处理程序中执行。相反,低级信号处理程序设置一个标志,告诉虚拟机在稍后的时间点(例如在下一条字节码指令)执行相应的Python信号处理程序。这会产生以下后果:
[...]
一个完全用C语言实现的长时间运行的计算(比如正则表达式匹配大量文本)可以不间断地运行任意长的时间,而不管接收到什么信号。当计算完成时,Python信号处理程序将被调用。
Qt事件循环是用C(++)实现的。这意味着,当它运行并且没有Python代码被调用时(例如,通过连接到Python插槽的Qt信号),信号被记录下来,但是Python信号处理程序没有被调用。
但是,从Python 2.6开始,在Python 3中,当使用
signal.set_wakeup_fd()
接收到带有处理程序的信号时,可以让Qt运行Python函数。这是可能的,因为与文档相反,低级信号处理程序不仅为虚拟机设置一个标志,而且它还可以将一个字节写入
set_wakeup_fd()
设置的文件描述符中。Python 2写入一个NUL字节,Python 3写入信号编号。因此,通过对一个Qt类进行子类化,该Qt类接受一个文件描述符并提供一个
readReady()
信号,例如QAbstractSocket
,事件循环将在每次接收到信号(带有一个处理程序)时执行一个Python函数,从而使信号处理程序几乎立即执行,而不需要计时器:ovfsdjhp4#
我找到了一种方法,其思想是迫使qt足够频繁地处理事件,并在python callabe中捕捉SIGINT信号。
kqlmhetl5#
cg 909/ Michael Herrmann的异步方法在替换定时器方面非常有趣。因此,这里有一个简化的版本,它也使用socket.socketpair(SOCK_STREAM)的默认类型。
myzjeezk6#
Artur Gaspar的答案在终端窗口处于焦点时对我有效,但在GUI处于焦点时无效。为了关闭GUI(继承自QWidget),我必须在类中定义以下函数:
通过检查事件键是否为67,可以确定按下了“c”。然后检查事件修饰符,确定在释放“c”时是否按下了ctrl。
agyaoht77#
您可以使用标准的python unix信号处理机制:
在
signal_handler
中,您可以释放所有资源(关闭所有数据库会话等)并轻轻关闭应用程序。取自here的代码示例
v64noz0r8#
我想我有一个更简单的解决方案:
这只是告诉应用程序尝试关闭所有窗口,如果有一个未保存的文档,你的应用程序应该弹出一个保存或取消对话框,就像它被退出一样。
您可能还需要将QApplication信号lastWindowClosed()连接到slot quit(),以使应用程序在窗口关闭时实际退出。
shyt4zoc9#
您可以借鉴matplotlib的解决方案。
matplotlib有一个名为
_maybe_allow_interrupt
的函数隐藏在matplotlib.backends.backend_qt
中当然,由于这不是一个公共函数,它可能会在matplotlib的未来版本中改变或消失,所以这更像是一个“快速而肮脏”的解决方案。