[PyQt] GIL related problem? Code inside.
V. Armando Sole
sole at esrf.fr
Thu Apr 19 13:37:02 BST 2007
Phil Thompson wrote:
"""
3. Automatically release the GIL whenever any Qt call is made from a QObject
derived class. The assumption is that this will cover the vast majority of
cases. Any other cases would have to be identified through bug reports.
"""
I guess I have one bug report.
We have got some problems with old PyQt3 applications when upgrading to the
latest sip and PyQt. The problem comes from the new GIL handling and the
use of QApplication.postEvent inside QThreads.
While we agree on the above described behaviour, perhaps an exception
should be made for qt.QApplication.postEvent (at least from inside a
QThread). The cleanest way we found to solve our problem is to replace
every appearence of QApplication.postEvent by QThread.postEvent if the call
is made from inside a qthread. At the qt level both calls are exactly the
same, but Trolltech says in the documentation that QThread.postEvent is
obsolete.
The code below reproduces the problem if desired and shows the solution too.
Best regards,
Armando
# --- BEGIN OF CODE ----
import sys
if len(sys.argv) > 1:
option = int(sys.argv[1])
else:
print "Usage:"
print "python PostEventDeadlock.py number"
print "number = 0 -> Use PyQt4"
print "number = 1 -> Use PyQt without crashing"
print "number = 2 -> Use PyQt and crash"
sys.exit(0)
import qt
IWILLCRASH = False
if option == 1:
import qt
elif option == 2:
import qt
IWILLCRASH = True
else:
import PyQt4.Qt as qt
QTVERSION = qt.qVersion()
if QTVERSION > '4.0.0':
MYEVENT = qt.QEvent.User
class MyCustomEvent(qt.QEvent):
def __init__(self, ddict={}):
self.dict = ddict
qt.QEvent.__init__(self, MYEVENT)
def type(self):
print "called"
return MYEVENT
else:
MYEVENT = qt.QEvent.User + 1
class MyCustomEvent(qt.QCustomEvent):
def __init__(self, ddict={}):
qt.QCustomEvent.__init__(self, MYEVENT)
self.dict = ddict
class CounterWidget(qt.QWidget):
def __init__(self):
qt.QWidget.__init__(self)
self.count = 0
self.layout = qt.QHBoxLayout(self)
self.label = qt.QLabel(self)
self.label.setText("COUNT: %d" % self.count)
self.layout.addWidget(self.label)
self.stock = []
for i in range(10):
thread = CounterThread(self)
thread.start()
self.stock.append(thread)
def customEvent(self,event):
self.count += 1
self.label.setText("COUNT: %d" % self.count)
self.label.update()
if QTVERSION < '4.0.0':
class CounterThread(qt.QThread):
def __init__(self, receiver):
qt.QThread.__init__(self)
self.object = qt.QObject()
self.receiver = receiver
def run(self):
self.work_loop()
def work_loop(self):
for x in xrange(500):
self.postTheEvent(x)
self.msleep(1)
def postTheEvent(self, value):
ddict = {'value': value}
if IWILLCRASH:
#this crashes
qt.QApplication.postEvent(self.receiver, MyCustomEvent(ddict))
else:
#this does not crash
self.postEvent(self.receiver, MyCustomEvent(ddict))
else:
class CounterThread(qt.QThread):
def __init__(self, receiver = None):
qt.QThread.__init__(self)
self.receiver = receiver
def run(self):
if 0:
#This does not update the label
qt.QTimer.singleShot(0, self.work_loop)
self.exec_()
else:
#This is fine
self.work_loop()
def work_loop(self):
for x in xrange(500):
self.postTheEvent(x)
self.msleep(1)
def postTheEvent(self, value):
ddict = {'value': value}
qt.QApplication.postEvent(self.receiver, MyCustomEvent(ddict))
if __name__ == "__main__":
app = qt.QApplication([])
w = CounterWidget()
w.show()
app.connect(app, qt.SIGNAL('lastWindowClosed()'),
app, qt.SLOT('quit()'))
if QTVERSION < '4.0.0':
app.exec_loop()
else:
app.exec_()
# --- END OF CODE ----
More information about the PyQt
mailing list