tutorial signal español custom python linux qt pyqt signals

python - signal - ¿Cuál es la forma correcta de cerrar mi aplicación PyQt cuando muere desde la consola(Ctrl-C)?



pyqt tutorial español pdf (7)

17.4. señal - Establecer controladores para eventos asincrónicos

Aunque los manejadores de señal de Python son llamados asincrónicamente en lo que respecta al usuario de Python, solo pueden ocurrir entre las instrucciones "atómicas" del intérprete de Python. Esto significa que las señales que llegan durante largos cálculos implementados puramente en C (como las coincidencias de expresiones regulares en cuerpos grandes de texto) pueden retrasarse durante un período de tiempo arbitrario.

Eso significa que Python no puede manejar las señales mientras se ejecuta el ciclo de eventos Qt. Solo cuando se ejecuta el intérprete de Python (cuando se cierra QApplication o cuando se llama a una función de Python desde Qt) se llamará al controlador de señal.

Una solución es usar un QTimer para permitir que el intérprete se ejecute de vez en cuando.

Tenga en cuenta que, en el código siguiente, si no hay ventanas abiertas, la aplicación se cerrará después del cuadro de mensaje independientemente de la elección del usuario porque QApplication.quitOnLastWindowClosed () == True. Este comportamiento puede ser cambiado.

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

Otra solución posible, como señaló LinearOrbit , es signal.signal(signal.SIGINT, signal.SIG_DFL) , pero no permite manipuladores personalizados.

¿Cuál es la forma correcta de cerrar mi aplicación PyQt cuando muere desde la consola (Ctrl-C)?

Actualmente (no he hecho nada especial para manejar las señales de Unix), mi aplicación PyQt ignora SIGINT (Ctrl + C). Quiero que se comporte bien y renuncie cuando muera. ¿Cómo debo hacer eso?


18.8.1.1. Ejecución de manejadores de señal Python

Un manejador de señal de Python no se ejecuta dentro del manejador de señal de bajo nivel (C). En cambio, el manejador de señal de bajo nivel establece un indicador que le dice a la máquina virtual que ejecute el manejador de señal de Python correspondiente en un momento posterior (por ejemplo, en la siguiente instrucción de bytecode). Esto tiene consecuencias:
[...]
Un cálculo de larga duración implementado puramente en C (como la coincidencia de expresiones regulares en un cuerpo grande de texto) puede ejecutarse sin interrupción durante un período de tiempo arbitrario, independientemente de las señales recibidas. Se llamarán a los manejadores de señal Python cuando termine el cálculo.

El ciclo de eventos Qt se implementa en C (++). Eso significa que mientras se ejecuta y no se llama ningún código Python (por ejemplo, mediante una señal Qt conectada a una ranura Python), las señales se anotan, pero los manejadores de señal Python no son llamados.

Pero , desde Python 2.6 y en Python 3 puede hacer que Qt ejecute una función de Python cuando se recibe una señal con un manejador usando signal.set_wakeup_fd() .

Esto es posible porque, contrariamente a la documentación, el manejador de señal de bajo nivel no solo establece un indicador para la máquina virtual, sino que también puede escribir un byte en el descriptor de archivo establecido por set_wakeup_fd() . Python 2 escribe un byte NUL, Python 3 escribe el número de señal.

Entonces al subclasificar una clase Qt que toma un descriptor de archivo y proporciona una señal readReady() como por ejemplo QAbstractSocket , el bucle de evento ejecutará una función Python cada vez que se reciba una señal (con un controlador) haciendo que el manejador de señal ejecute casi instantáneamente sin necesidad de temporizadores:

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


Creo que tengo una solución más simple:

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)

Esto simplemente le dice a la aplicación que intente cerrar todas las ventanas si se presiona ctrl + c. Si hay un documento no guardado, su aplicación debería mostrar un cuadro de diálogo para guardar o cancelar como si hubiera salido.

También es posible que deba conectar la señal QApplication lastWindowClosed () a la ranura para salir () para que la aplicación salga cuando las ventanas estén cerradas.


Encontré una manera de hacer esto. La idea es forzar qt para procesar eventos con la suficiente frecuencia y en un callabe de pitón para atrapar la señal 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_()


La respuesta de Artur Gaspar funcionó para mí cuando la ventana de la terminal estaba enfocada, pero no funcionaría cuando la GUI estaba enfocada. Para poder cerrar mi GUI (que hereda de QWidget) tuve que definir la siguiente función en la clase:

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

Verificando que la clave del evento sea 67 se asegura de que se haya presionado ''c''. Luego, al verificar los modificadores de evento se determina si se presionó ctrl cuando se lanzó ''c''.


Puede utilizar el mecanismo de manejo de señales python Unix estándar:

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

donde en signal_handler puede liberar todos los recursos (cerrar todas las sesiones de db, etc.) y cerrar suavemente su aplicación.

Ejemplo de código tomado de here


Si simplemente desea que ctrl-c cierre la aplicación, sin ser "agradable" / graciosa al respecto, desde http: //www.mail- archive.com/[email protected]/msg13758.html , puede usar esta:

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

Aparentemente esto funciona en Linux, Windows y OSX. Hasta ahora, solo he probado esto en Linux (y funciona).