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 ----


_______________________________________________
PyQt mailing list    [email protected]
http://www.riverbankcomputing.com/mailman/listinfo/pyqt

Reply via email to