콘솔 (Ctrl-C)에서 종료 될 때 PyQt 애플리케이션을 종료하는 올바른 방법은 무엇입니까?
콘솔 (Ctrl-C)에서 종료 될 때 PyQt 애플리케이션을 종료하는 올바른 방법은 무엇입니까?
현재 (유닉스 신호를 처리하기 위해 특별한 작업을 수행하지 않습니다) 내 PyQt 애플리케이션은 SIGINT (Ctrl + C)를 무시합니다. 나는 그것이 잘 작동하고 죽으면 종료되기를 원합니다. 어떻게해야합니까?
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에서 테스트했습니다 (작동합니다).
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을 눌렀는지 여부를 확인합니다.
'ProgramingTip' 카테고리의 다른 글
Android 웹 애플리케이션 구성 (0) | 2020.11.21 |
---|---|
C ++에서 배열 타임에 프로그래밍 방식으로 정적 배열 만들기 (0) | 2020.11.21 |
입력 유형 = "텍스트"요소 HTML / CSS 내부 텍스트 기 (0) | 2020.11.21 |
요소가 화면에 표시 확인 (0) | 2020.11.20 |
Postgres에서 CSV 파일의 값으로 선택한 행을 업데이트하는 방법은 무엇입니까? (0) | 2020.11.20 |