PyQt QDialog.show() doesn't work because of thread suspension
-
I got no answer on Stackoverflow "(see this)":http://stackoverflow.com/questions/19350517/qdialog-show-doesnt-work-because-of-thread-suspension, so I try here.
I'm writing a program with a window that displays an image which requires a relatively long time to be generated. Because of this, when the program starts, it starts a thread that prepares the image while the user can interact with the main window. If the user wants to see the image, imageWindow.show() is called. It checks whether the image is ready and, if necessary, it waits before showing the window.
I have managed to implement this, but now I want the method to show a window with the message "Drawing image" as it waits. I've found that this window doesn't appear, probably because the thread stops to wait for the image. After searching for a similar problem, I added a call to processEvents, which resulted in a slight improvement: now the windows appears and then disappears when the image is ready, but it is completely empty. Does somebody know hot to get the QLabel visible, too?I have tried both QThread and the standard Python threading library.
Here is an example, (note the sleep in the long thread):@#!/usr/bin/env python2
#-- coding: UTF-8 --
import sys, datetime, time
from PyQt4.QtGui import QApplication, QDialog, QVBoxLayout, QLabel
from PyQt4.QtCore import QThreadclass SplashScreen(QDialog):
def init(self):
super(SplashScreen, self).init()
self.layout=QVBoxLayout()
self.setLayout(self.layout)
self.label=QLabel('waiting for the loop')
self.layout.addWidget(self.label)
self.show()class MainWin(QDialog):
def init(self):
super(MainWin, self).init()def show(self): if not t.isFinished(): print('still processing') sw=SplashScreen() app.processEvents() print('the splashscreen should have appeared') t.wait() del sw print('the loop is over') self.layout=QVBoxLayout() self.setLayout(self.layout) self.label=QLabel('Ready') self.layout.addWidget(self.label) super(MainWin, self).show()
class WasteCPUTime(QThread):
def run(self):
print('loop started')
start=datetime.datetime.today()
while (datetime.datetime.today()-start).total_seconds()<6:
t.sleep(1)#NOTE: without this nothing shows up, with this only the empty window
pass
print('loop ended')if name=='main':
app=QApplication(sys.argv)
m=MainWin()
t=WasteCPUTime()
t.start()
m.show()
sys.exit(app.exec_())@ -
The following example should do what you want:
@
from PyQt4.QtCore import *
from PyQt4.QtGui import *class Dialog(QDialog):
def init(self, parent=None, **kwargs):
QDialog.init(self, parent, **kwargs)l=QVBoxLayout(self) l.addWidget(QLabel("Waiting...", self)) l.addWidget(QProgressBar(self, minimum=0, maximum=0))
class MainWin(QDialog):
def init(self, parent=None, **kwargs):
QDialog.init(self, parent, **kwargs)l=QVBoxLayout(self) l.addWidget(QLabel("Ready!", self))
class WasteCPUTime(QObject):
finished=pyqtSignal()@pyqtSlot() def waste(self): self._timer=self.startTimer(10000) def timerEvent(self, event): self.finished.emit()
if name=="main":
from sys import argv, exita=QApplication(argv) d=Dialog() d.show() m=MainWin() w=WasteCPUTime() t=QThread(started=w.waste) w.finished.connect(t.quit) t.finished.connect(m.show) t.finished.connect(d.hide) w.moveToThread(t) t.start() exit(a.exec_())
@
Generally speaking, its better to create an object with the functionality you require and move it to a QThread than it is to sub-class QThread directly. Also, this example uses signals and slots to communicate between threads rather than referencing the objects directly which I think is the cause of your issues.
Also, you may want to look at "QSplashScreen":http://pyqt.sourceforge.net/Docs/PyQt4/qsplashscreen.html.
Hope this helps ;o)