What Is the Correct Way to Make My Pyqt Application Quit When Killed from the Console (Ctrl-C)

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

17.4. signal — Set handlers for asynchronous events

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.

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.

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

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

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

Ctrl-C doesn't work with PyQt

CTRL+C causes a signal to be sent to
the process. Python catches the
signal, and sets a global variable,
something like CTRL_C_PRESSED = True.
Then, whenever the Python interpreter
gets to execute a new opcode, it sees
the variable set and raises a
KeybordInterrupt.

This means that CTRL+C works only if
the Python interpreter is spinning. If
the interpreter is executing an
extension module written in C that
executes a long-running operation,
CTRL+C won't interrupt it, unless it
explicitly "cooperates" with Python.
Eg: time.sleep() is theoretically a
blocking operation, but the
implementation of that function
"cooperates" with the Python
interpreter to make CTRL+C work.

This is all by design: CTRL+C is meant
to do a "clean abort"; this is why it
gets turned into an exception by
Python (so that the cleanups are
executed during stack unwind), and its
support by extension modules is sort
of "opt-in". If you want to totally
abort the process, without giving it a
chance to cleanup, you can use CTRL+.

When Python calls QApplication::exec()
(the C++ function), Qt doesn't know
how to "cooperate" with Python for
CTRL+C, and this is why it does not
work. I don't think there's a good way
to "make it work"; you may want to see
if you can handle it through a global
event filter.
— Giovanni Bajo

Adding this to the main program solved the problem.

import signal

signal.signal(signal.SIGINT, signal.SIG_DFL)

I'm not sure what this has to do with the explanation.

QApplication: How to shutdown gracefully on Ctrl-C

As it isn't documented, QApplication::watchUnixSignal shouldn't be used. And, from reading the code, it will not work properly when using the glib event dispatcher (which is the default on Linux).

However, in general you can safely catch Unix signals in Qt applications, you just have to write a bit of the code yourself. There is even an example in the documentation - Calling Qt Functions From Unix Signal Handlers.

How to handle Ctrl+C in python app with PyQt?

CTRL+C causes a signal to be sent to the process. Python catches the signal, and sets a global variable, something like CTRL_C_PRESSED = True. Then, whenever the Python interpreter gets to execute a new opcode, it sees the variable set and raises a KeybordInterrupt.

This means that CTRL+C works only if the Python interpreter is spinning. If the interpreter is executing an extension module written in C that executes a long-running operation, CTRL+C won't interrupt it, unless it explicitly "cooperates" with Python. Eg: time.sleep() is theoretically a blocking operation, but the implementation of that function "cooperates" with the Python interpreter to make CTRL+C work.

This is all by design: CTRL+C is meant to do a "clean abort"; this is why it gets turned into an exception by Python (so that the cleanups are executed during stack unwind), and its support by extension modules is sort of "opt-in". If you want to totally abort the process, without giving it a chance to cleanup, you can use CTRL+.

When Python calls QApplication::exec() (the C++ function), Qt doesn't know how to "cooperate" with Python for CTRL+C, and this is why it does not work. I don't think there's a good way to "make it work"; you may want to see if you can handle it through a global event filter. — Giovanni Bajo

Adding this to the main program solved the problem.

import signal

signal.signal(signal.SIGINT, signal.SIG_DFL)

I'm not sure what this has to do with the explanation.

Pyqt Terminal hangs after excuting close window command

It's not working because you're calling GUI methods on the dialog (close()) outside of the event loop. The event loop doesn't start until you call app.exec_().

If you really want to close the dialog immediately after it opens without using a QTimer, you can override the showEvent() method and call termination() from there, which gets called when the dialog is first displayed.

class ProgressMeter(progressMeter_simple.Ui_Dialog, QtGui.QDialog):
def __init__(self):
QtGui.QDialog.__init__(self)
progressMeter_simple.Ui_Dialog.__init__(self)
self.setupUi(self)
self.progressBar.setRange(0, 0)

def showEvent(self, event):
super(ProgressMeter, self).showEvent(event)
self.termination()

Qt5 application not terminating on QMainWindow.close() after opening QFileDialog

It seems this issue may be related to QTBUG-59184, which affects native GTK file dialogs opened by Qt5. The bug is still unresolved and has critical priority, so hopefully it will be fixed reasonably quickly. In the meantime, the work-around is to use the built-in Qt file-dialog, which can be specified via the options, like this:

QFileDialog.getOpenFileName(parent, caption, options=QFileDialog.DontUseNativeDialog)

or like this:

dialog = QFileDialog(parent, caption)
dialog.setOption(QFileDialog.DontUseNativeDialog, True)

How to close windows in pyqtgraph

Your call to QApplication.exec_() does not return--it blocks until the application has exited (in this case, the application exits when you close the window). So the lines that would close the window are not called until after you have already closed it.

Ctrl-C for quitting Python in Powershell now not working

This is a bug that recently appeared in Windows 10 Insider build 15002.

A work around is to change the Mapped Keys from Ctrl C to something like Ctrl K

If you are not familar how to do this, You can look up or at stty -a

You can run this command on each bash session that will map your Terminate to Ctrl + K

stty intr \^k

As a TEMP solution you could include this in your Bashrc so it is executed on each new session

This bug has been reported already on Github #1569



Related Topics



Leave a reply



Submit