Вопросы:
- Какова лучшая практика для
отслеживание протектора
прогресс без блокировки графического интерфейса
(«Не отвечает»)?
- Вообще, каковы лучшие практики для
потоки, как это относится к GUI
развитие?
Справочная информация:
- У меня есть PyQt GUI для Windows.
- Используется для обработки наборов HTML
документы.
- Это занимает от трех секунд
до трех часов, чтобы обработать набор
документы.
- Я хочу иметь возможность обрабатывать
несколько комплектов одновременно.
- Я не хочу, чтобы графический интерфейс блокировался.
- Я смотрю на модуль потоков
чтобы достичь этого.
- Я относительно новичок в потоках.
- В GUI есть один индикатор выполнения.
- Я хочу, чтобы он отображал прогресс
выбранная тема.
- Отображение результатов выбранного
нить, если она закончена.
- Я использую Python 2.5.
Моя идея: Пусть потоки испускают QtSignal при обновлении прогресса, который запускает некоторую функцию, которая обновляет индикатор выполнения. Также подайте сигнал о завершении обработки, чтобы можно было отобразить результаты.
#NOTE: this is example code for my idea, you do not have
# to read this to answer the question(s).
import threading
from PyQt4 import QtCore, QtGui
import re
import copy
class ProcessingThread(threading.Thread, QtCore.QObject):
__pyqtSignals__ = ( "progressUpdated(str)",
"resultsReady(str)")
def __init__(self, docs):
self.docs = docs
self.progress = 0 #int between 0 and 100
self.results = []
threading.Thread.__init__(self)
def getResults(self):
return copy.deepcopy(self.results)
def run(self):
num_docs = len(self.docs) - 1
for i, doc in enumerate(self.docs):
processed_doc = self.processDoc(doc)
self.results.append(processed_doc)
new_progress = int((float(i)/num_docs)*100)
#emit signal only if progress has changed
if self.progress != new_progress:
self.emit(QtCore.SIGNAL("progressUpdated(str)"), self.getName())
self.progress = new_progress
if self.progress == 100:
self.emit(QtCore.SIGNAL("resultsReady(str)"), self.getName())
def processDoc(self, doc):
''' this is tivial for shortness sake '''
return re.findall('<a [^>]*>.*?</a>', doc)
class GuiApp(QtGui.QMainWindow):
def __init__(self):
self.processing_threads = {} #{'thread_name': Thread(processing_thread)}
self.progress_object = {} #{'thread_name': int(thread_progress)}
self.results_object = {} #{'thread_name': []}
self.selected_thread = '' #'thread_name'
def processDocs(self, docs):
#create new thread
p_thread = ProcessingThread(docs)
thread_name = "example_thread_name"
p_thread.setName(thread_name)
p_thread.start()
#add thread to dict of threads
self.processing_threads[thread_name] = p_thread
#init progress_object for this thread
self.progress_object[thread_name] = p_thread.progress
#connect thread signals to GuiApp functions
QtCore.QObject.connect(p_thread, QtCore.SIGNAL('progressUpdated(str)'), self.updateProgressObject(thread_name))
QtCore.QObject.connect(p_thread, QtCore.SIGNAL('resultsReady(str)'), self.updateResultsObject(thread_name))
def updateProgressObject(self, thread_name):
#update progress_object for all threads
self.progress_object[thread_name] = self.processing_threads[thread_name].progress
#update progress bar for selected thread
if self.selected_thread == thread_name:
self.setProgressBar(self.progress_object[self.selected_thread])
def updateResultsObject(self, thread_name):
#update results_object for thread with results
self.results_object[thread_name] = self.processing_threads[thread_name].getResults()
#update results widget for selected thread
try:
self.setResultsWidget(self.results_object[thread_name])
except KeyError:
self.setResultsWidget(None)
Будут оценены любые комментарии по этому подходу (например, недостатки, подводные камни, похвалы и т. Д.).
Разрешение:
В итоге я использовал класс QThread и связанные с ним сигналы и слоты для связи между потоками. Это в первую очередь потому, что моя программа уже использует Qt / PyQt4 для объектов / виджетов GUI. Это решение также потребовало меньше изменений в моем существующем коде для реализации.
Вот ссылка на соответствующую статью Qt, которая объясняет, как Qt обрабатывает потоки и сигналы, http://www.linuxjournal.com/article/9602. Выдержка ниже:
К счастью, Qt разрешает
сигналы и слоты для подключения
через потоки - до тех пор, пока потоки
запускают свои собственные циклы событий.
Это гораздо более чистый метод
связь по сравнению с отправкой и
получать события, потому что это избегает
вся бухгалтерия и промежуточная
Производные от QEvent классы, которые становятся
необходимо в любом нетривиальном
приложение. Общение между
темы теперь становится вопросом
подключение сигналов от одного потока к
слоты в другом и мьютексинг
и вопросы безопасности потоков обмена
данные между потоками обрабатываются
Qt.
Зачем нужно запускать событие
цикл внутри каждого потока, к которому вы
хотите подключить сигналы? Причина
имеет отношение к внутреннему потоку
механизм связи, используемый Qt
при подключении сигналов от одного
нить в слот другой нити.
Когда такое соединение сделано, это
упоминается как соединение в очереди.
Когда сигналы испускаются через
соединение в очереди, слот вызывается
в следующий раз объект назначения
цикл обработки событий выполнен. Если слот
вместо этого был вызван непосредственно
сигнал из другого потока, этот слот
будет выполняться в том же контексте, что и
вызывающая нить. Обычно это
не то, что вы хотите (и особенно нет
что вы хотите, если вы используете
подключение к базе данных, как база данных
соединение может использоваться только
нить которая его создала). В очереди
соединение правильно отправляет
сигнал объекту потока и
вызывает свой слот в своем собственном контексте
спекуляция на системе событий.
Это именно то, что мы хотим для
связь между потоками, в которой
некоторые потоки обрабатывают
соединения с базой данных. Qt
Сигнал / слот механизм находится в корне
реализация межпотоковой
схема передачи событий, описанная выше,
но с гораздо чище и
более простой в использовании интерфейс.
ПРИМЕЧАНИЕ: eliben также имеет хороший ответ, и если бы я не использовал PyQt4, который обрабатывает безопасность потоков и мьютексирование, его решение было бы моим выбором.