什么是正确的方式来使我的PyQt应用程序退出从控制台死亡(Ctrl-C)?
什么是正确的方式来使我的PyQt应用程序退出从控制台死亡(Ctrl-C)?
目前(我没有做任何特殊的处理unix信号),我的PyQt应用程序忽略SIGINT(Ctrl + C)。 我希望它performance得很好,并在死亡时退出。 我该怎么做?
17.4。 信号 – 为asynchronous事件设置处理程序
尽pipe就Python用户而言,Python信号处理程序是asynchronous调用的,但它们只能在Python解释程序的“primefaces”指令之间进行。 这意味着在长时间计算过程中到达纯粹以C语言实现的信号(比如在大量文本上的正则expression式匹配)可能会延迟一段任意的时间。
这意味着在Qt事件循环运行时,Python无法处理信号。 只有当Python解释器运行时(当QApplication退出,或者从Qt调用Python函数时),信号处理程序才会被调用。
解决scheme是使用QTimer让解释器不时运行。
请注意,在下面的代码中,如果没有打开的窗口,应用程序将在消息框之后退出,而不pipe用户的select,因为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_())
LinearOrbit指出的另一种可能的解决scheme是signal.signal(signal.SIGINT, signal.SIG_DFL)
signal.SIGINT , signal.signal(signal.SIGINT, signal.SIG_DFL)
,但它不允许自定义处理程序。
如果你只是希望有ctrl-cclosures应用程序 – 而不是“漂亮”/优雅 – 然后从http://www.mail- archive.com/pyqt@riverbankcomputing.com/msg13758.html ,您可以使用这个:
import signal signal.signal(signal.SIGINT, signal.SIG_DFL) import sys from PyQt4.QtCore import QCoreApplication app = QCoreApplication(sys.argv) app.exec_()
显然这适用于Linux,Windows和OSX – 我到目前为止只在Linux上进行了testing(并且工作正常)。
我find了一个方法来做到这一点。 这个想法是迫使qt经常处理事件,并在python callabe中捕获SIGINT信号。
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_()
18.8.1.1。 Python信号处理程序的执行
Python信号处理程序不会在低级(C)信号处理程序中执行。 相反,低级信号处理程序设置一个标志,告诉虚拟机稍后(例如在下一个字节码指令)执行相应的Python信号处理程序。 这有后果:
[…]
无论接收到什么信号,纯粹以C语言实现的长时间运行(例如大量文本上的正则expression式匹配)可以在任意时间内连续运行。 计算完成后,Python信号处理程序将被调用。
Qt事件循环在C(++)中实现。 也就是说,它运行时并没有调用Python代码(例如,通过连接到Python插槽的Qt信号),信号被logging下来,但Python信号处理程序不被调用。
但是 ,由于Python 2.6和Python 3中,当使用signal.set_wakeup_fd()
接收到具有处理函数的信号时,您可以使Qt运行Python函数。
这是可能的,因为与文档相反,低级信号处理程序不仅为虚拟机设置标志,而且还可以在由set_wakeup_fd()
设置的set_wakeup_fd()
写入一个字节。 Python 2写入NUL字节,Python 3写入信号编号。
所以通过readReady()
一个readReady()
文件描述符并提供一个readReady()
信号的Qt类readReady()
如QAbstractSocket
,事件循环将在每次接收到一个信号(使用一个处理程序)时执行一个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_())
你可以使用标准的python unix信号处理机制:
import signal import sys def signal_handler(signal, frame): print 'You pressed Ctrl+C!' sys.exit(0) signal.signal(signal.SIGINT, signal_handler) print 'Press Ctrl+C' while 1: continue
在signal_handler
你可以释放所有的资源(closures所有数据库会话等),并轻轻closures你的应用程序。
从这里取的代码示例
我想我有一个更简单的解决scheme:
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)
这只是告诉应用程序尝试closures所有的窗口,如果Ctrl + C被按下。 如果有未保存的文档,应用程序应popup一个保存或取消对话框,就好像它已退出一样。
您可能还需要将QApplication信号lastWindowClosed()连接到插槽quit(),以在窗口closures时使应用程序实际退出。
来自Artur Gaspar的回答在terminal窗口焦点时为我工作,但在GUI焦点时不起作用。 为了让我的GUIclosures(从QWidgetinheritance),我不得不在类中定义下面的函数:
def keyPressEvent(self,event): if event.key() == 67 and (event.modifiers() & QtCore.Qt.ControlModifier): sigint_handler()
检查以确保事件键是67,确保按下“c”。 然后检查事件修饰符确定当'c'被释放时是否按下ctrl。