На этом шаге мы рассмотрим создание в потоке собственного цикла обработки сигналов.
Каждый поток может иметь собственный цикл обработки сигналов, который запускается с помощью метода ехес_(). В этом случае потоки могут обмениваться сигналами между собой. Чтобы прервать цикл, следует вызвать слот quit() или метод exit([returnCode=0]). Рассмотрим обмен сигналами между потоками на примере.
# -*- coding: utf-8 -*- from PyQt5 import QtCore, QtWidgets class Thread1(QtCore.QThread): s1 = QtCore.pyqtSignal(int) def __init__(self, parent=None): QtCore.QThread.__init__(self, parent) self.count = 0 def run(self): self.exec_() # Запускаем цикл обработки сигналов def on_start(self): self.count += 1 self.s1.emit(self.count) class Thread2(QtCore.QThread): s2 = QtCore.pyqtSignal(str) def __init__ (self, parent=None): QtCore.QThread.__init__(self, parent) def run(self): self.exec_() # Запускаем цикл обработки сигналов def on_change(self, i): i += 10 self.s2.emit("%d" % i) class MyWindow(QtWidgets.QWidget): def __init__(self, parent=None): QtWidgets.QWidget.__init__(self, parent) self.label = QtWidgets.QLabel("Нажмите кнопку") self.label.setAlignment(QtCore.Qt.AlignHCenter) self.button = QtWidgets.QPushButton("Сгенерировать сигнал") self.vbox = QtWidgets.QVBoxLayout() self.vbox.addWidget(self.label) self.vbox.addWidget(self.button) self.setLayout(self.vbox) self.thread1 = Thread1() self.thread2 = Thread2() self.thread1.start() self.thread2.start() self.button.clicked.connect(self.thread1.on_start) self.thread1.s1.connect(self.thread2.on_change) self.thread2.s2.connect(self.on_thread2_s2) def on_thread2_s2(self, s): self.label.setText(s) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) window = MyWindow() window.setWindowTitle("Обмен сигналами между потоками") window.resize(300, 70) window.show() sys.exit(app.exec_())
Результат работы приложения изображен на рисунке 1.
Рис.1. Результат работы приложения
В этом примере мы создали классы Thread1, Thread2 и MyWindow. Первые два класса представляют собой потоки. Внутри них в методе run() вызывается метод ехес_(), который запускает цикл обработки событий. Внутри конструктора класса MyWindow производится создание надписи, кнопки и экземпляров классов Threadl и Thread2. Далее выполняется запуск сразу двух потоков.
В следующей инструкции сигнал нажатия кнопки соединяется с методом on_start() первого потока. Внутри этого метода производится какая-либо операция (в нашем случае - увеличение значения атрибута count), а затем с помощью метода emit() генерируется сигнал s1, и в параметре передается результат выполнения метода. Сигнал s1 соединен с методом on_change() второго потока. Внутри этого метода также производится какая-либо операция, а затем генерируется сигнал s2, и передается результат выполнения метода. В свою очередь сигнал s2 соединен со слотом on_thread2_s2 объекта окна, который выводит в надпись значение, переданное с этим сигналом. Таким образом, при нажатии кнопки Сгенерировать сигнал вначале будет вызван метод on_start() из класса Thread1, затем метод on_change() из класса Thread2, а потом метод on_thread2_s2 класса MyWindow, который выведет результат выполнения на экран.
На следующем шаге мы рассмотрим создание очереди заданий.