18.8.1.1. Выполнение обработчиков сигналов Python
Обработчик сигналов Python не выполняется внутри обработчика сигналов низкого уровня (C). Вместо этого низкоуровневый обработчик сигнала устанавливает флаг, который указывает виртуальной машине выполнить соответствующий обработчик сигнала Python на более позднем этапе (например, при следующей инструкции байт-кода). Это имеет последствия:
[...]
Долгосрочные вычисления, реализованные исключительно на языке C (например, сопоставление регулярных выражений для большого объема текста), могут выполняться непрерывно в течение произвольного промежутка времени, независимо от принятых сигналов. Обработчики сигналов Python будут вызваны после завершения расчета.
Цикл событий Qt реализован на C (++). Это означает, что хотя он работает и код Python не вызывается (например, с помощью сигнала Qt, подключенного к слоту Python), сигналы записываются, но обработчики сигналов Python не вызываются.
Но , начиная с 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_())