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, вернется спать!

Одним из решений является вставка короткого (больше нуля) сна в фоновом потоке с стратегическими интервалами, чтобы графический интерфейс мог работать. Это не всегда возможно.

Ответ 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())