3

I would like to ask how to read a big file from disk and maintain the PyQt4 UI responsive (not blocked). I had moved the load of the file to a QThread subclass but my GUI thread get freezed. Any suggestions? I think it must be something with the GIL but I don't know how to sort it?

EDIT: I am using vtkGDCMImageReader from the GDCM project to read a multiframe DICOM image and display it with vtk and pyqt4. I do this load in a different thread (QThread) but my app freeze until the image is loaded. here is an example code:

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())
jmrbcu
  • 41
  • 1
  • 5

4 Answers4

3

I'm guessing you're directly calling the run to begin the thread. That would make the GUI freeze because you're not activating the thread.

So you'd be missing the start there, which would indirectly and properly call the 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()
cregox
  • 17,674
  • 15
  • 85
  • 116
  • @pygabriel sorry, I removed the broken link now. But you can still [use the way back machine](http://web.archive.org/web/20120411061825/http://diotavelli.net/PyQtWiki/Threading,_Signals_and_Slots) to see it if you want. – cregox Oct 11 '13 at 19:27
  • Thanks so much for providing the chached link! – pygabriel Oct 12 '13 at 01:34
2

A bit late, but I think I can pinpoint the problem you face.

The image is large, and the unpacking is probably a CPU intensive task. This means that your GUI thread goes to sleep and the loading thread is CPU bound. At that point the loading thread has the GIL, and the GUI cannot start.

Even if you can get into the loading thread, and introduce a sleep(0), to alow the GUI to continue, this will not help on a mulit-core or multi-processor machine. What happens is the O/S has two threads and thinks it can run both. The loading thread is set up on (say) core 1 and the GUI can be loaded and run on (say) core 2. So after initiating the load and start of the GUI thread on core 2, the O/S resumes the loading thread on core 1 - which promtply grabs the GIL. Moments later the GUI thread is ready to start, and attempts to aquire the GIL, which fails. All it can do without the GIL is go back to sleep!

One solution is to insert a short (greater than zero) sleep in the background thread at strategic intervals, so that the GUI can run. This is not always possible.

Ian
  • 1,941
  • 2
  • 20
  • 35
  • yes, thanks for the reasoning, right now the only way to successfully avoid this is releasing the GIL in the internal C++ code of the loader. – jmrbcu Feb 14 '11 at 14:14
  • I understand that there is a fix in the pipeline for 3.2, that will flip between the two threads every 5ms. That might be a solution. – Ian Feb 15 '11 at 23:10
  • Does it matter whether it is Python's time.sleep or PyQt's QThread.sleep? – eric.frederich Aug 30 '11 at 15:01
1

Take a look at threading-in-a-pyqt-application-use-qt-threads-or-python-threads

Community
  • 1
  • 1
Martin Beckett
  • 94,801
  • 28
  • 188
  • 263
  • yes, I have been reading that article but, the fact is that when I try to read a big dicom multiframe image file with vtkGDCMImageReader from gdcm in a separate QThread or threading.Thread, my UI get blocked and THIS IS AN I/O OPERATION!!! – jmrbcu Nov 25 '09 at 19:58
0

Maybe create your reader object in the tread:

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())
Gary van der Merwe
  • 9,134
  • 3
  • 49
  • 80