GNU/Linux >> Belajar Linux >  >> Linux

Apa cara yang benar untuk membuat aplikasi PyQt saya berhenti saat dimatikan dari konsol (Ctrl-C)?

17.4. sinyal — Tetapkan penangan untuk kejadian asinkron

Meskipun penangan sinyal Python dipanggil secara asinkron sejauh menyangkut pengguna Python, mereka hanya dapat terjadi di antara instruksi "atomik" dari juru bahasa Python. Ini berarti bahwa sinyal yang tiba selama kalkulasi panjang yang diimplementasikan murni dalam C (seperti pencocokan ekspresi reguler pada kumpulan teks besar) dapat tertunda untuk waktu yang sewenang-wenang.

Itu berarti Python tidak dapat menangani sinyal saat Qt event loop sedang berjalan. Hanya ketika juru bahasa Python berjalan (ketika QApplication berhenti, atau ketika fungsi Python dipanggil dari Qt), penangan sinyal akan dipanggil.

Solusinya adalah dengan menggunakan QTimer untuk membiarkan juru bahasa berjalan dari waktu ke waktu.

Perhatikan bahwa, dalam kode di bawah ini, jika tidak ada jendela yang terbuka, aplikasi akan berhenti setelah kotak pesan terlepas dari pilihan pengguna karena QApplication.quitOnLastWindowClosed() ==True. Perilaku ini dapat diubah.

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

Solusi lain yang mungkin, seperti yang ditunjukkan oleh LinearOrbit, adalah signal.signal(signal.SIGINT, signal.SIG_DFL) , tetapi tidak mengizinkan penangan khusus.


Jika Anda hanya ingin ctrl-c menutup aplikasi - tanpa "baik"/anggun tentangnya - lalu dari http://www.mail-archive.com/[email protected]/msg13758.html, Anda dapat menggunakan ini:

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

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

Tampaknya ini berfungsi di Linux, Windows, dan OSX - sejauh ini saya hanya mengujinya di Linux (dan berhasil).


18.8.1.1. Eksekusi penangan sinyal Python

Handler sinyal Python tidak dieksekusi di dalam handler sinyal level rendah (C). Sebagai gantinya, penangan sinyal tingkat rendah menyetel bendera yang memberi tahu mesin virtual untuk mengeksekusi penangan sinyal Python yang sesuai di titik selanjutnya (misalnya pada instruksi bytecode berikutnya). Ini memiliki konsekuensi:
[...]
Penghitungan jangka panjang yang diimplementasikan murni dalam C (seperti pencocokan ekspresi reguler pada kumpulan teks yang besar) dapat berjalan tanpa gangguan untuk waktu yang sewenang-wenang, terlepas dari sinyal apa pun yang diterima. Penangan sinyal Python akan dipanggil saat perhitungan selesai.

Qt event loop diimplementasikan dalam C(++). Artinya, saat berjalan dan tidak ada kode Python yang dipanggil (mis. dengan sinyal Qt yang terhubung ke slot Python), sinyal dicatat, tetapi penangan sinyal Python tidak dipanggil.

Tapi , sejak Python 2.6 dan di Python 3 Anda dapat menyebabkan Qt menjalankan fungsi Python saat sinyal dengan handler diterima menggunakan signal.set_wakeup_fd() .

Hal ini dimungkinkan, karena, bertentangan dengan dokumentasi, penangan sinyal tingkat rendah tidak hanya menetapkan tanda untuk mesin virtual, tetapi juga dapat menulis byte ke dalam deskriptor file yang diatur oleh set_wakeup_fd() . Python 2 menulis byte NUL, Python 3 menulis nomor sinyal.

Jadi dengan mensubklasifikasikan kelas Qt yang mengambil deskriptor file dan menyediakan readReady() sinyal, seperti mis. QAbstractSocket , loop peristiwa akan mengeksekusi fungsi Python setiap kali sinyal (dengan penangan) diterima menyebabkan penangan sinyal mengeksekusi hampir secara instan tanpa perlu pengatur waktu:

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. Python – Cara yang Benar Untuk Meningkatkan Pip Di Stretch Debian?

  2. Apa cara terbaik untuk mengirim sinyal ke semua anggota grup proses?

  3. Apa cara termudah untuk SSH menggunakan Python?

  1. Apa cara yang benar untuk memulai layanan mongod di linux / OS X?

  2. Mengizinkan Ctrl-C untuk menginterupsi ekstensi-C python

  3. Apa cara yang benar untuk menginstal jdk di linux

  1. Gunakan Python default daripada instalasi Anaconda saat dipanggil dari terminal

  2. Apa cara termudah untuk mendeteksi penekanan tombol di python 3 di mesin linux?

  3. Apa cara tercepat untuk memindahkan sejuta gambar dari satu direktori ke direktori lain di Linux?