PyQt4, QThread и открытие больших файлов без замораживания GUI
Я хотел бы спросить, как читать большой файл с диска и поддерживать пользовательский интерфейс PyQt4 отзывчивым (не заблокированным). Я переместил загрузку файла в подкласс QThread, но мой поток графического интерфейса зависает. Какие-либо предложения? Я думаю, что это должно быть что-то с GIL, но я не знаю, как его сортировать?
EDIT:
Я использую vtkGDCMImageReader из проекта GDCM, чтобы прочитать многокадровое изображение DICOM и отображать его с помощью vtk и pyqt4. Я делаю эту загрузку в другом потоке (QThread), но мое приложение замирает до загрузки изображения. вот пример кода:
class ReadThread(QThread):
def __init__(self, file_name):
super(ReadThread, self).__init__(self)
self.file_name = file_name
self.reader.vtkgdcm.vtkGDCMImageReader()
def run(self):
self.reader.SetFileName(self.file_name)
self.reader.Update()
self.emit(QtCore.SIGNAL('image_loaded'), self.reader.GetOutput())
Ответы
Ответ 1
Я предполагаю, что вы прямо вызываете run
, чтобы начать поток. Это заставит GUI замерзнуть, потому что вы не активируете поток.
Таким образом, вам будет отсутствовать start
, что косвенно и правильно вызовет run
:
thread = ReadThread()
thread.begin()
class ReadThread(QThread):
def __init__(self, file_name):
super(ReadThread, self).__init__(self)
self.file_name = file_name
self.reader.vtkgdcm.vtkGDCMImageReader()
def run(self):
self.reader.SetFileName(self.file_name)
self.reader.Update()
self.emit(QtCore.SIGNAL('image_loaded'), self.reader.GetOutput())
def begin(self):
self.start()
Ответ 2
Немного поздно, но я думаю, что смогу определить проблему, с которой вы столкнулись.
Изображение велико, и распаковка - это, вероятно, задача с интенсивным использованием ЦП. Это означает, что ваш поток GUI переходит в спящий режим, а загрузочный поток связан с ЦП. В этот момент загрузочный поток имеет GIL, и GUI не может запускаться.
Даже если вы можете попасть в загрузочный поток и ввести sleep (0), чтобы продолжить работу с графическим интерфейсом, это не поможет на многоуровневой или многопроцессорной машине. Что происходит, так это то, что O/S имеет два потока и думает, что он может запускать оба. Загрузочный поток настроен на (скажем) ядро 1, и GUI может быть загружен и запущен на (скажем) ядре 2. Таким образом, после запуска загрузки и начала потока GUI на ядре 2 O/S возобновляет загрузочную нить на ядре 1 - который promtply захватывает GIL. Спустя несколько минут поток GUI готов к запуску и пытается поглотить GIL, который терпит неудачу. Все, что он может сделать без GIL, вернется спать!
Одним из решений является вставка короткого (больше нуля) сна в фоновом потоке с стратегическими интервалами, чтобы графический интерфейс мог работать. Это не всегда возможно.
Ответ 3
Взгляните на threading-in-a-pyqt-application-use-qt-threads-or-python-threads
Ответ 4
Возможно, создайте объект-читатель в протекторе:
class ReadThread(QThread):
def __init__(self, file_name):
super(ReadThread, self).__init__(self)
self.file_name = file_name
- self.reader = vtkgdcm.vtkGDCMImageReader()
def run(self):
+ self.reader = vtkgdcm.vtkGDCMImageReader()
self.reader.SetFileName(self.file_name)
self.reader.Update()
self.emit(QtCore.SIGNAL('image_loaded'), self.reader.GetOutput())