What is the correct way to get PyQt to leave the console (Ctrl-C)?

What is the correct way to get the PyQt application to exit the console (Ctrl-C)?

Currently (I have not done anything special to handle unix signals), my PyQt application ignores SIGINT (Ctrl + C). I want him to behave well and leave when they kill him. How should I do it?

+60
python linux qt signals pyqt
Feb 08 2018-11-11T00:
source share
8 answers

17.4. signal - set handlers for asynchronous events

Although Python signal handlers are invoked asynchronously for the Python user, they can only occur between the "atomic" instructions of the Python interpreter. This means that signals received during long computations implemented exclusively in C language (for example, matches with regular expressions in large texts) can be delayed for an arbitrary amount of time.

This means that Python cannot process signals while the Qt event loop is running. The signal handler will be called only when the Python interpreter starts (when QApplication finishes or when the Python function from Qt is called).

The solution is to use QTimer to run the interpreter from time to time.

Please note that in the code below, if there are no open windows, the application will close after the message window regardless of the user's choice, because QApplication.quitOnLastWindowClosed () == True. This behavior is subject to change.

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_()) 

Another possible solution, as LinearOrbit points out , is signal.signal(signal.SIGINT, signal.SIG_DFL) signal.SIGINT , signal.signal(signal.SIGINT, signal.SIG_DFL) , but it does not allow custom handlers.

+40
Feb 08 2018-11-21T00:
source share
— -

If you just want ctrl-c to close the application - without being "pretty" / elegant - from http: //www.mail- archive.com/pyqt@riverbankcomputing.com/msg13758.html , you can use this:

 import signal signal.signal(signal.SIGINT, signal.SIG_DFL) import sys from PyQt4.QtCore import QCoreApplication app = QCoreApplication(sys.argv) app.exec_() 

Apparently this works on Linux, Windows, and OSX - I still tested it on Linux (and it works).

+36
May 20 '11 at 13:08
source share

I found a way to do this. The idea is to get qt to handle events often enough in python callabe to catch a SIGINT signal.

 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_() 
+5
Jul 28 '12 at 23:05
source share

18.8.1.1. Executing Python Signal Handlers

The Python signal handler is not executed in the low-level (C) signal handler. Instead, the low-level signal handler sets a flag that tells the virtual machine to execute the appropriate Python signal handler at a later point (for example, in the following bytecode instruction). This has consequences:
[...]
A long-term calculation implemented exclusively in C (for example, matching regular expressions in a large text) can work without interruption for an arbitrary period of time, regardless of the received signals. Python signal handlers will be called when the calculation completes.

The Qt event loop is implemented in C (++). This means that while it is running and Python code is not being called (for example, using a Qt signal connected to the Python slot), signals are marked, but Python signal handlers are not called.

But , since Python 2.6 and in Python 3, you can force Qt to run the Python function when the signal with the handler is received using signal.set_wakeup_fd() .

This is possible because, unlike the documentation, the low-level signal handler not only sets a flag for the virtual machine, but can also write a byte to the file descriptor set by set_wakeup_fd() . Python 2 writes a null byte, Python 3 writes a signal number.

So, subclassing the Qt class, which takes a file descriptor and provides a readReady() signal, for example, for example. QAbstractSocket , the event loop will execute a Python function every time a signal is received (with a handler), forcing the signal handler to execute instantly without the need for timers:

 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_()) 
+5
May 14 '16 at 16:42
source share

You can use the standard python unix signal processing engine:

 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 

where in signal_handler you can free all resources (close all db sessions, etc.) and gently close the application.

Sample code taken from here

+1
Feb 08 2018-11-11T00:
source share

I think I have a simpler solution:

 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) 

This simply tells the application to try to close all windows if ctrl + c is pressed. If there is an unsaved document, your application should open the save or cancel dialog as if it were completed.

You may also need to connect the QApplication lastWindowClosed () signal to the quit () slot to force the application to actually exit when the windows are closed.

+1
Feb 15 '11 at 16:47
source share

The answer from Arthur Gaspard worked for me when the terminal window was in the spotlight, but did not work when the GUI was in focus. To close my GUI (which inherits from QWidget), I had to define the following function in the class:

 def keyPressEvent(self,event): if event.key() == 67 and (event.modifiers() & QtCore.Qt.ControlModifier): sigint_handler() 

Check to make sure the event key is 67, press "c". Then checking the event modifiers determines if ctrl was pressed during the release of 'c'.

0
Jun 16 '14 at 21:08
source share

If anyone is interested, you can see how we did it for matplotlib based on cg909. Very good answer:

https://github.com/matplotlib/matplotlib/pull/13306

 class allow_interrupt(): def __init__(self, interrupted_qobject): QAS = QtNetwork.QAbstractSocket self.qt_socket = QAS(QAS.TcpSocket, qApp) self.old_fd = None self.old_sigint_handler = None self.interrupt_caught = False self.interrupted_qobject = interrupted_qobject # Create a socket pair self.wsock, self.rsock = socket.socketpair() # Let Qt listen on the one end self.qt_socket.setSocketDescriptor(self.rsock.fileno()) self.wsock.setblocking(False) # First Python code executed gets any exception from # the signal handler, so add a dummy handler first self.qt_socket.readyRead.connect(lambda: None) # Second handler does the real handling self.qt_socket.readyRead.connect(self._readSignal) def __enter__(self): # And let Python write on the other end self.old_fd = signal.set_wakeup_fd(self.wsock.fileno()) self.old_sigint_handler = signal.getsignal(signal.SIGINT) signal.signal(signal.SIGINT, self._handle) def __exit__(self, type, val, traceback): signal.set_wakeup_fd(self.old_fd) signal.signal(signal.SIGINT, self.old_sigint_handler) self.wsock.close() self.rsock.close() self.qt_socket.abort() if self.interrupt_caught: self.old_sigint_handler() def _readSignal(self): # Read the written byte. # Note: readyRead is blocked from occurring again until readData() # was called, so call it, even if you don't need the value. data = self.qt_socket.readData(1) def _handle(self, *args): self.interrupt_caught = True if self.interrupted_qobject is qApp: pyplot.close() self.interrupted_qobject.quit() 
0
Jan 31 '19 at 20:10
source share



All Articles