python شرح ما هي الطريقة الصحيحة لإنهاء تطبيق PyQt عند قتلي من وحدة التحكم(Ctrl-C)؟



تعلم لغة البايثون من الصفر الى الاحتراف (5)

17.4. signal - تعيين معالجات للأحداث غير المتزامنة

على الرغم من أن معالجات إشارة بايثون تسمى بشكل غير متزامن بقدر ما يتعلق الأمر بمستخدم بايثون ، إلا أنها لا يمكن أن تحدث إلا بين الإرشادات "الذرية" لمترجم بايثون. وهذا يعني أن الإشارات التي تصل خلال العمليات الحسابية الطويلة التي يتم تنفيذها بحتة في C (مثل تطابقات التعبير العادي في المساحات الكبيرة من النص) قد تتأخر لمدة زمنية تعسفية.

وهذا يعني أن بايثون لا يمكنها معالجة الإشارات أثناء تشغيل حلقة الحدث كيو تي. فقط عند تشغيل مترجم Python (عندما يتم إنهاء QApplication ، أو عندما يتم استدعاء وظيفة Python من Qt) ، سيتم استدعاء معالج الإشارة.

الحل هو استخدام QTimer للسماح بتشغيل المترجم من وقت لآخر.

لاحظ أنه ، في التعليمات البرمجية أدناه ، إذا لم تكن هناك إطارات مفتوحة ، فسيتم إنهاء التطبيق بعد مربع الرسالة بغض النظر عن اختيار المستخدم لأن QApplication.quitOnLastWindowClosed () == True. يمكن تغيير هذا السلوك.

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

حل آخر ممكن ، كما أشار إليه LinearOrbit ، هو signal.signal(signal.SIGINT, signal.SIG_DFL) ، ولكنه لا يسمح signal.signal(signal.SIGINT, signal.SIG_DFL) المخصصة.

https://ffff65535.com

ما هي الطريقة الصحيحة لإنهاء تطبيق PyQt عند قتلي من وحدة التحكم (Ctrl-C)؟

حاليًا (لم أقم بشيء خاص للتعامل مع إشارات Unix) ، يتجاهل تطبيق PyQt SIGINT (Ctrl + C). أريد أن تتصرف بشكل جيد وأستقيل عندما تقتل. كيف أفعل ذلك؟


18.8.1.1. تنفيذ معالجات إشارة بايثون

لا يتم تنفيذ معالج إشارة Python داخل معالج إشارة المستوى المنخفض (C). بدلاً من ذلك ، يقوم معالج الإشارة ذو المستوى المنخفض بتعيين علامة تشير إلى أن الجهاز الظاهري يقوم بتنفيذ معالج إشارة Python المقابل في نقطة لاحقة (على سبيل المثال في التعليمات البرمجية البايتية التالية). هذا له عواقب:
[...]
يمكن تشغيل عملية حسابية مستمرة منذ فترة طويلة في C (مثل مطابقة التعبير العادي على نص كبير) دون انقطاع لفترة زمنية تعسفية ، بغض النظر عن أي إشارات يتم تلقيها. سيتم استدعاء معالجات إشارة Python عند انتهاء الحساب.

يتم تنفيذ حلقة الحدث Qt في C (++). ويعني ذلك أنه في الوقت الذي يتم تشغيله ولا يطلق عليه رمز بايثون (على سبيل المثال بواسطة إشارة كيو تي متصلة بفتحة بايثون) ، يتم الإشارة إلى الإشارات ، ولكن لا يتم استدعاء معالجات إشارة بايثون.

ولكن ، بما أن Python 2.6 و Python 3 يمكن أن تسبب Qt لتشغيل وظيفة Python عند تلقي إشارة باستخدام معالج باستخدام signal.set_wakeup_fd() .

وهذا ممكن ، لأنه ، على عكس الوثائق ، لا يقوم معالج الإشارات ذات المستوى المنخفض بتعيين علامة للماكينة الافتراضية فحسب ، بل يمكنه أيضًا كتابة بايت في واصف الملف الذي تم تعيينه بواسطة set_wakeup_fd() . يكتب Python 2 NUL بايت ، يكتب Python 3 رقم الإشارة.

لذلك من خلال تصنيف فئة Qt التي تأخذ واصف ملف وتوفر إشارة readReady() ، مثل QAbstractSocket مثلاً ، تقوم حلقة الحدث بتنفيذ وظيفة Python في كل مرة يتم فيها استقبال إشارة (مع معالج) مما يؤدي إلى تنفيذ معالج الإشارة بشكل فوري تقريبًا دون الحاجة إلى أجهزة ضبط الوقت:

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

إذا كنت ترغب ببساطة في إغلاق ctrl-c للتطبيق - دون أن تكون "لطيفًا" / رشيقًا حول ذلك - من http: //www.mail- archive.com/[email protected]/msg13758.html ، فيمكنك استخدام هذه:

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

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

يبدو أن هذا يعمل على Linux و Windows و OSX - لقد قمت باختبار هذا فقط على Linux حتى الآن (وهو يعمل).


عملت إجابة من Artur Gaspar لي عندما كانت نافذة المحطة الطرفية في بؤرة التركيز ، ولكن لن تنجح عندما كان التركيز على واجهة المستخدم الرسومية. من أجل الحصول على واجهة المستخدم الرسومية ليغلق (الذي يرث من QWidget) كان علي تحديد الوظيفة التالية في الصف:

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

التحقق للتأكد من أن مفتاح الحدث هو 67 تأكد من الضغط على 'c'. ثم التحقق من معدّلات الحدث يحدد ما إذا كان يتم الضغط على ctrl عند إصدار "c".


يمكنك استخدام آلية التعامل مع إشارات اشارات بيثون القياسية:

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

حيث في signal_handler يمكنك تحرير جميع الموارد (إغلاق جميع الدورات ديسيبل وغيرها) وإغلاق التطبيق الخاص بك بلطف.

مثال الكود المأخوذ من here





signals