Шаг 14.
Библиотека PyQt5. Знакомство с PyQt5. Многопоточные приложения. Класс QThread: создание потока (окончание)

    На этом шаге мы рассмотрим пример создания потока.

    Рассмотрим использование класса QThread на примере.

# -*- coding: utf-8 -*-
from PyQt5 import QtCore, QtWidgets

class MyThread(QtCore.QThread):
    mysignal = QtCore.pyqtSignal(str)
    def  __init__(self, parent=None):
        QtCore.QThread.__init__(self, parent)
    def run(self):
        for i in range(1, 21):
            self.sleep(3)	# "Засыпаем" на 3 секунды
            # Передача данных из потока через сигнал
            self.mysignal.emit("i = %s" % 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.mythread = MyThread()    # Создаем экземпляр класса
        self.button.clicked.connect(self.on_clicked)
        self.mythread.started.connect(self.on_started)
        self.mythread.finished.connect(self.on_finished)
        self.mythread.mysignal.connect(self.on_change, QtCore.Qt.QueuedConnection)
    def on_clicked(self):
        self.button.setDisabled(True) # Делаем кнопку неактивной
        self.mythread.start()         # Запускаем поток
    def on_started(self):	# Вызывается при запуске потока
        self.label.setText("Вызван метод on_started ()")
    def on_finished(self):      # Вызывается при завершении потока
        self.label.setText("Вызван метод on_finished()")
        self.button.setDisabled(False) # Делаем кнопку активной
    def on_change(self, s):
        self.label.setText(s)
        
if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    window = MyWindow()
    window.setWindowTitle("Использование класса QThread")
    window.resize(300, 70)
    window.show()
    sys.exit(app.exec_())
Архив с файлом можно взять здесь.

    Здесь мы создали класс MyThread, который является наследником класса QThread. В нем мы определили свой собственный сигнал mysignal, для чего создали атрибут с таким же именем и занесли в него значение, возвращенное функцией pyqtSignal() из модуля QtCore. Функции pyqtSignal() мы передали в качестве параметра тип str (строка Python), тем самым указав PyQt, что вновь определенный сигнал будет принимать единственный параметр строкового типа:

  mysignal = QtCore.pyqtSignal(str)

    В том же классе мы определили обязательный для потоков метод run() - в нем производится имитация процесса с помощью цикла for и метода sleep(): каждые три секунды выполняется генерация сигнала mysignal и передача текущего значения переменной i в составе строки:

  self.mysignal.emit("i = %s" % i)

    Внутри конструктора класса MyWindow мы назначили обработчик этого сигнала с помощью выражения:

  self.mythread.mysignal.connect(self.on_change,  QtCore.Qt.QueuedConnection)

    Здесь все нам уже знакомо: у свойства mysignal потока, которое представляет одноименный сигнал, вызывается метод connect(), и ему первым параметром передается обработчик. Во втором параметре метода connect() с помощью атрибута QueuedConnection указывается, что сигнал помещается в очередь обработки событий, и обработчик должен выполняться в потоке приемника сигнала, т. е. в GUI-потоке. Из GUI-потока мы можем смело изменять свойства компонентов интерфейса.

    Теперь рассмотрим код метода класса MyWindow, который станет обработчиком сигнала mysignal:

    def on_change(self, s):
        self.label.setText(s)

    Второй параметр этого метода служит для приема параметра, переданного этому сигналу. Значение этого параметра будет выведено в надписи с помощью метода setText().

    Еще внутри конструктора класса MyWindow производится создание надписи и кнопки, а затем их размещение внутри вертикального контейнера. Далее выполняется создание экземпляра класса MyThread и сохранение его в атрибуте mythread. С помощью этого атрибута мы можем управлять потоком и назначить обработчики сигналов started(), finished() и mysignal. Запуск потока производится с помощью метода start() внутри обработчика нажатия кнопки. Чтобы исключить повторный запуск потока, мы с помощью метода setDisabled() делаем кнопку неактивной, а после окончания работы потока внутри обработчика сигнала finished() опять делаем кнопку активной.

    Обратите внимание, что для имитации длительного процесса мы использовали статический метод sleep() из класса QThread, а не функцию sleep() из модуля time. Вообще, приостановить выполнение потока позволяют следующие статические методы класса QThread:

    Еще один полезный статичный метод класса QThread - yieldCurrentThread() - немедленно приостанавливает выполнение текущего потока и передает управление следующему ожидающему выполнения потоку, если таковой есть. Пример:

  QtCore.QThread.yieldCurrentThread()

    На следующем шаге мы рассмотрим управление циклом внутри потока.




Предыдущий шаг Содержание Следующий шаг