Pyqt: Простая Работа С Потоками

Очень часто программам приходится использовать многопоточность.

Иногда это чудовищные пулы потоков со сложными взаимодействиями, но гораздо чаще это простой код, главное требование которого — не замораживать интерфейс.

PyQt имеет два основных инструмента для работы с потоками высокого уровня: потоки Python и QThread Qt. Для меня QThread оказался предпочтительнее из-за его лучшей связи с механизмом signal-slot в Qt. Итак, инструмент выбран, все отлично работает, но со временем мне захотелось сделать работу с потоками немного проще и удобнее.

Возникла идея сделать велосипедный модуль для работы с нитками для простых случаев.



простой_поток
Этот модуль предназначен для работы с потоками в классах, унаследованных от QObject. Используя его, вы можете заставить любой метод класса выполняться в отдельном потоке, при этом изнутри метода вы можете получить доступ (хотя и ограниченный) к атрибутам и методам класса.

Давайте посмотрим на простой пример:

  
  
  
   

#!/usr/bin/env python # -*- coding: utf-8 -*- import sys from time import sleep from PyQt4.QtCore import * from PyQt4.QtGui import * from simple_thread import SimpleThread class Foo(QLabel): def __init__(self, parent = None): QLabel.__init__(self, parent) self.setFixedSize(320, 240) self.digits = ['one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'ten'] @SimpleThread def bar(self, primaryText): rows = [] digits = self.digits for item in digits: rows.append('%s: %s' % (primaryText, item)) self.setText('\n'.

join(rows), thr_method = 'b') sleep(0.5) def setText(self, text): QLabel.setText(self, text) if __name__ == "__main__": app = QApplication(sys.argv) foo = Foo() foo.show() foo.bar('From thread', thr_start = True) app.exec_()

Сорт Фу унаследовано от QLabel , и мы хотим менять текст метки каждые полсекунды, не зависая при этом интерфейс.

Мы будем использовать метод для вывода текста бар .

Чтобы этот метод работал в отдельном потоке, перед объявлением метода мы размещаем декоратор.

@SimpleThread .

Изнутри метода нам понадобится доступ к атрибуту цифры — список слов для отображения.

Также нам необходимо обновить текст метки, для этого мы вызовем метод setText .



Доступ к атрибутам
Первая проблема цифры может использоваться не только этим потоком.

Модуль simple_thread решает эту проблему.

Когда мы получаем атрибут, нам возвращается не ссылка на этот атрибут, а его копия.

В этом случае все действия происходят в контексте основного потока, и нам не нужно беспокоиться об одновременном доступе к атрибуту из нескольких потоков.

Таким образом вы можете получить доступ к спискам, словарям и всем неизменяемым атрибутам классов (строкам, кортежам и т. д.).

Здесь есть один момент — он работает достаточно медленно, поэтому не стоит переусердствовать с доступом к атрибутам.



Методы вызова
Проблема номер два — вызов метода setText .

Проблема аналогична первой — Qt выдаст исключение при попытке доступа к методу графического класса извне основного потока.

Как и в первом случае, это решается приостановкой нашего потока и вызовом метода из основного потока.

Существует три разных варианта вызова методов в зависимости от аргумента.

thr_method :

  • thr_method = 'b' : при вызове метода наш поток приостанавливается до завершения метода.

    Выполнение метода происходит в основном потоке.

    Только в этом варианте можно получить возвращаемое значение метода;

  • thr_method = 'д' : В этом случае наша тема не останавливается.

    Метод также выполняется в основном потоке;

  • thr_method = Нет или не указано : выполнение метода происходит в контексте нашего потока.

    Здесь нужно быть осторожным — вступают в силу все ограничения, связанные с многопоточностью, в частности, нельзя получить доступ к атрибутам класса в методе.

Аргумент thr_method не передается вызываемому методу.



Установка атрибутов
Также возможна установка атрибутов из другого потока.



self.newAttr = 'text'

Атрибуты могут быть любого типа.

Как и в других случаях, вся работа осуществляется в основном потоке и не вызывает проблем, вам просто нужно помнить, что установив атрибут, вы можете перезаписать уже существующий одноименный атрибут.

Начало темы
Чтобы запустить наш код, мы просто выполняем метод бар указав аргумент thr_start = Истина так что поток запускается немедленно.

Есть еще один способ, он нам пригодится, если мы хотим обрабатывать сигналы, вызываемые из другого потока или сигналы класса.

QThread ( начал , законченный , прекращено ).



thread = foo.bar('From thread') thread.finished.connect(self.barFinished) thread.start()

Здесь мы связали сигнал конца потока законченный с методом барЗавершен и запустил стрим.



Остановка потока
Если по какой-то причине нам необходимо остановить работающий поток, мы можем сделать это, вызвав метод thr_stop .



thread = foo.bar('From thread', thr_start = True) .

thread.thr_stop()

Этот метод устанавливает флаг thr_stopFlag=Истина , состояние которого мы должны отслеживать в нашем методе и, если его истинное значение, завершить наш метод. Стоит отметить, что метод thr_stop не ждет остановки потока, немедленно возвращая управление.

Если нам нужно дождаться завершения работы потока, то thr_stop вам нужно вызвать метод ждать .

Модуль simple_thread имеет две функции для остановки всех активных потоков: завершить потоки И закрытьТемы .

Первая функция жестко прерывает выполнение всех потоков, что может быть небезопасно.

Вторая функция работает так, как если бы мы вызвали thr_stop для каждого потока, а затем ждать .

Все комментарии, предложения и тому подобное приветствуются.

Если что-то подобное уже кем-то реализовано, буду рад получить ссылку.

Теги: #python #pyqt4 #threads #python

Вместе с данным постом часто просматривают: