ProgramingTip

콘솔 (Ctrl-C)에서 종료 될 때 PyQt 애플리케이션을 종료하는 올바른 방법은 무엇입니까?

bestdevel 2020. 11. 21. 09:29
반응형

콘솔 (Ctrl-C)에서 종료 될 때 PyQt 애플리케이션을 종료하는 올바른 방법은 무엇입니까?


콘솔 (Ctrl-C)에서 종료 될 때 PyQt 애플리케이션을 종료하는 올바른 방법은 무엇입니까?

현재 (유닉스 신호를 처리하기 위해 특별한 작업을 수행하지 않습니다) 내 PyQt 애플리케이션은 SIGINT (Ctrl + C)를 무시합니다. 나는 그것이 잘 작동하고 죽으면 종료되기를 원합니다. 어떻게해야합니까?


17.4. signal — 이벤트에 대한 설정 설정

Python 신호 처리기는 Python 사용자에 관한 한 수 있습니다. Python 호출 Python 인터프리터의 "원자"명령 사이에만 있습니다. 이는 순전히 C로 구현 된 긴 계산 (예 : 큰 텍스트 본문에 대한 정규 될 일치) 중에 도착하는 신호가 임의의 시간 동안 지연 수 있음을 의미합니다.

즉, Qt 이벤트 루프가 실행되는 동안 Python은 신호를 처리 할 수 ​​없습니다. Python 인터프리터가 실행될 때 (QApplication이 종료되거나 Qt에서 Python 함수가 호출 될 때)에만 신호가 호출됩니다.

해결은 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)이지만 사용자 정의를 사용할 수 있습니다.


ctrl-c가 응용 프로그램을 "좋은"/ 우아하게 닫지 않고 닫으려면 http : //www.mail- archive.com/pyqt@riverbankcomputing.com/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에서 테스트했습니다 (작동합니다).


18.8.1.1. Python 신호 처리기 실행

Python 신호 처리기는 저수준 (C) 신호 처리기 내에서 실행되지 않습니다. 대신, 저수준 신호 처리기는 가상 머신이 나중 시점 (예 : 다음 바이트 코드 명령에서)에 해당하는 Python 신호 처리기를 실행하도록 지시하는 플래그를 설정합니다. 결과는 다음과 가변적입니다.
[...]
순전히 C로 구현 된 장기 실행 계산 (예 : 텍스트의 큰 본문에 대한 정규식 일치)은 수신 된 신호에 관계없이 임의의 시간 동안 중단없이 실행될 수 있습니다. 계산이 완료되면 Python 신호 처리기가 호출됩니다.

Qt 이벤트 루프는 C (++)로 구현됩니다. 즉, 실행되고 있고 호출되지 않는 동안 (예 : Python 무언에 사용되는 Qt 신호에 의해) 신호가 기록되는 Python 신호 파이썬 코드는 호출되지 않습니다.

그러나

Python 2.6 및 Python 3부터는 사용하여 사용할 수있는 신호가 수신 될 때 Qt가 Python 함수를 실행할 수 있습니다 .signal.set_wakeup_fd()

이는 처리 문서와 달리 저수준 신호를 처리 할 가상 머신에 대한 플래그를 효율적으로 가상 머신에 대한 파일 설명자에 바이트를 쓸 수도 있기 때문에 가능합니다 set_wakeup_fd(). Python 2는 NUL 바이트를 쓰고 Python 3은 신호 번호를 씁니다.

따라서 파일 설명은 취하고 readReady()신호를 제공하는 Qt 클래스를 서브 클래 싱 처리 될 ( 예 :) 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_())

나는 이것을 할 방법을 찾았다. 아이디어는 qt가 이벤트를 충분히 자주 처리하고 파이썬 callabe에서 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_()

표준 python 유닉스 신호 처리 메커니즘을 사용할 수 있습니다.

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당신은 모든 자원을 확보 할 수 있습니다 부드럽게 가까이 위해 appliction (모든 DB 세션 등을 닫습니다).

여기 에서 가져온 코드 예제


더 간단한 해결책이 있다고 생각합니다.

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)

이것은 ctrl + c를 눌렀을 때 모든 창을 닫으라고 응용 프로그램에 지시합니다. 저장되지 않은 문서가있는 경우 종료 된 것처럼 앱에 저장 또는 취소 대화 상자가 표시됩니다.

윈도우가 닫힐 때 애플리케이션이 실제로 종료되도록하려면 QApplication 신호 lastWindowClosed ()를 슬롯 quit ()에 연결해야 할 수도 있습니다.


Artur Gaspar의 답변은 터미널 창에 포커스가있을 때 저에게 효과적 이었지만 GUI에 포커스가있을 때는 작동하지 않았습니다. 내 GUI를 닫으려면 (QWidget에서 상 속됨) 클래스에서 다음 함수를 정의해야했습니다.

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

이벤트 키가 67인지 확인하면 'c'를 눌렀는지 확인합니다. 그런 다음 이벤트 수정자를 확인하여 'c'를 놓을 때 Ctrl을 눌렀는지 여부를 확인합니다.

참고 URL : https://stackoverflow.com/questions/4938723/what-is-the-correct-way-to-make-my-pyqt-application-quit-when-killed-from-the-co

반응형