GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Was ist der richtige Weg, um meine PyQt-Anwendung zu beenden, wenn sie von der Konsole aus beendet wird (Strg-C)?

17.4. signal — Handler für asynchrone Ereignisse festlegen

Obwohl Python-Signalhandler aus Sicht des Python-Benutzers asynchron aufgerufen werden, können sie nur zwischen den „atomaren“ Anweisungen des Python-Interpreters auftreten. Das bedeutet, dass Signale, die während langer, rein in C implementierter Berechnungen (wie z. B. Übereinstimmungen mit regulären Ausdrücken in großen Textkörpern) ankommen, um eine beliebige Zeit verzögert werden können.

Das bedeutet, dass Python keine Signale verarbeiten kann, während die Qt-Ereignisschleife ausgeführt wird. Nur wenn der Python-Interpreter läuft (wenn die QApplication beendet wird oder wenn eine Python-Funktion von Qt aufgerufen wird) wird der Signal-Handler aufgerufen.

Eine Lösung besteht darin, einen QTimer zu verwenden, um den Interpreter von Zeit zu Zeit laufen zu lassen.

Beachten Sie, dass im folgenden Code die Anwendung unabhängig von der Auswahl des Benutzers nach dem Meldungsfeld beendet wird, wenn keine geöffneten Fenster vorhanden sind, da QApplication.quitOnLastWindowClosed() ==True. Dieses Verhalten kann geändert werden.

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

Eine andere mögliche Lösung, wie von LinearOrbit gezeigt, ist signal.signal(signal.SIGINT, signal.SIG_DFL) , erlaubt aber keine benutzerdefinierten Handler.


Wenn Sie einfach möchten, dass ctrl-c die Anwendung schließt – ohne dabei „nett“/anmutig zu sein – dann können Sie von http://www.mail-archive.com/[email protected]/msg13758.html verwenden das:

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

import sys
from PyQt4.QtCore import QCoreApplication
app = QCoreApplication(sys.argv)
app.exec_()

Anscheinend funktioniert das unter Linux, Windows und OSX - ich habe das bisher nur unter Linux getestet (und es funktioniert).


18.8.1.1. Ausführung von Python-Signal-Handlern

Ein Python-Signalhandler wird nicht innerhalb des Low-Level-Signalhandlers (C) ausgeführt. Stattdessen setzt der Low-Level-Signalhandler ein Flag, das die virtuelle Maschine anweist, den entsprechenden Python-Signalhandler zu einem späteren Zeitpunkt auszuführen (z. B. bei der nächsten Bytecode-Anweisung). Das hat Folgen:
[...]
Eine lang andauernde Berechnung, die ausschließlich in C implementiert ist (z. B. das Abgleichen regulärer Ausdrücke in einem großen Textkörper), kann für eine beliebige Zeit ununterbrochen ausgeführt werden, unabhängig von empfangenen Signalen. Die Python-Signal-Handler werden aufgerufen, wenn die Berechnung abgeschlossen ist.

Die Qt-Ereignisschleife ist in C(++) implementiert. Das heißt, während es läuft und kein Python-Code aufgerufen wird (z. B. durch ein Qt-Signal, das mit einem Python-Slot verbunden ist), werden die Signale notiert, aber die Python-Signal-Handler werden nicht aufgerufen.

Aber , seit Python 2.6 und in Python 3 können Sie Qt veranlassen, eine Python-Funktion auszuführen, wenn ein Signal mit einem Handler mit signal.set_wakeup_fd() empfangen wird .

Dies ist möglich, da der Low-Level-Signalhandler entgegen der Dokumentation nicht nur ein Flag für die virtuelle Maschine setzt, sondern auch ein Byte in den durch set_wakeup_fd() gesetzten Dateideskriptor schreiben kann . Python 2 schreibt ein NUL-Byte, Python 3 schreibt die Signalnummer.

Also durch Unterklassen einer Qt-Klasse, die einen Dateideskriptor nimmt und einen readReady() bereitstellt Signal, wie z.B. QAbstractSocket , führt die Ereignisschleife jedes Mal, wenn ein Signal (mit einem Handler) empfangen wird, eine Python-Funktion aus, wodurch der Signalhandler nahezu augenblicklich ausgeführt wird, ohne dass Timer erforderlich sind:

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

Linux
  1. Ändern des Tastaturlayouts / der Tastaturzuordnung sowohl auf der Konsole (tty) als auch auf X auf eine X / Konsolen-agnostische Weise?

  2. Python – Der richtige Weg, um Pip in Stretch Debian zu aktualisieren?

  3. Was ist der beste Weg, um ein Signal an alle Mitglieder einer Prozessgruppe zu senden?

  4. Was ist der einfachste Weg zu SSH mit Python?

  5. Was ist der richtige Weg, um einen Mongod-Dienst unter Linux / OS X zu starten?

Was ist der richtige Weg, um zu verhindern, dass Benutzer ohne Rootberechtigung heruntergefahren oder neu gestartet werden?

Was ist der schnellste Weg, um unter Linux eine Million Bilder von einem Verzeichnis in ein anderes zu verschieben?

Was genau bestimmt, ob ein Hintergrundjob beendet wird, wenn die Shell beendet wird, oder ob er beendet wird?

Was ist der einfachste Weg, Daten von stdout als Diagramm zu visualisieren?

Was ist der schnellste Weg, um Text über die Befehlszeile zu einer Datei hinzuzufügen?

Was ist der richtige Weg, um Ihre CPU-Geschwindigkeit unter Linux anzuzeigen?