On Monday 11 July 2005 4:50 pm, Yann Cointepas wrote: > On Sunday 10 July 2005 19:44, you wrote: > > With current snapshots I don't get the double delete and get a SIGKILL > > rather than a seg fault. > > > > The above code definately breaks Qt's rules - you must must create > > QWidgets in the main GUI thread. I need another example that > > demonstrate's the problem but conforms to Qt's rules. > > > > Phil > > I thought it was legal to create widgets in any thread between > qt.qApp.lock() and qt.qApp.lock(). I used this to make a smaller example. > > The error is still here with legal code. I joined a modification of the > program that uses a queue of functions which are called from the main GUI > thread via a QTimer. It produces exactly the same error as the previous > sample code but uses Qt only from the main thread. > > I noticed a different behavior for two different python/sip versions. With > Fedora core 2 versions (Python 2.3.3/sip 3.10.1/Qt 3.3.3), I have the > following output: > > !threadedFunction! <Thread(Thread-1, started)> > !createWidget! <_MainThread(MainThread, started)> > !init TestWidget! <_MainThread(MainThread, started)> > !del TestWidget! <_MainThread(MainThread, started)> > python: Objects/classobject.c:631: instance_dealloc: Assertion > `g->gc.gc_refs != (-2)' failed. Abort > > > With my own Python/sip/PyQt compiled from sources > (Python 2.4.1/sip 4.2.1/qt 3.3.3), the result is: > > !threadedFunction! <Thread(Thread-1, started)> > !createWidget! <_MainThread(MainThread, started)> > !init TestWidget! <_MainThread(MainThread, started)> > !del TestWidget! <_MainThread(MainThread, started)> > !del TestWidget! <_MainThread(MainThread, started)> > Segmentation fault > > #-------------------------------------------------------------------------- >-- import sys, threading > from qt import * > > class EventFilter( QObject ): > def eventFilter( self, o, e ): > return False > > class TestWidget( QWidget ): > def __init__( self ): > print '!init TestWidget!', threading.currentThread() > QWidget.__init__( self ) > > def __del__( self ): > print '!del TestWidget!', threading.currentThread() > > > class MainThreadActions( QObject ): > '''Queue of functions that are called from the main thread > via a QTimer. Functions and arguments can be pushed in > the queue (from any thread) with the push method.''' > def __init__( self, parent = None ): > QObject.__init__( self, parent ) > self.lock = threading.RLock() > self.actions = [] > self.timer = QTimer( self ) > self.connect( self.timer, SIGNAL( 'timeout()' ), self.__doAction ) > self.timer.start( 100, 0 ) > > def push( self, function, *args, **kwargs ): > self.lock.acquire() > try: > self.actions.append( ( function, args, kwargs ) ) > finally: > self.lock.release() > > def __doAction( self ): > self.lock.acquire() > try: > actions = self.actions > self.actions = [] > finally: > self.lock.release() > for ( function, args, kwargs ) in actions: > if kwargs is None or len( kwargs ) == 0: > apply( function, args ) > else: > apply( function, args, kwargs ) > > > def threadedFunction(): > '''This function is *not* called from the main GUI thread''' > print '!threadedFunction!', threading.currentThread() > mainThread.push( createWidget ) > > > def createWidget(): > '''This function is called from the main GUI thread''' > print '!createWidget!', threading.currentThread() > w = TestWidget() > w.show() > del w > > > app = QApplication( sys.argv ) > > theEventFilter = EventFilter() > qApp.installEventFilter( theEventFilter ) > > mainThread = MainThreadActions() > > mainWindow = QLabel( 'main', None ) > mainWindow.show() > app.setMainWidget( mainWindow ) > > t = threading.Thread( target=threadedFunction ) > t.start() > > app.exec_loop()
Apologies for the delay in replying. The fix should be in tonight's SIP snapshot. This proved to be rather subtle... It's nothing to do with threading - you can remove all the thread related code in your example and still get a crash. When your TestWidget gets garbage collected the underlying C++ dtor is called which causes a QHideEvent to be generated. When calling the Python eventFilter() method with the TestWidget object as an argument, the object's reference count gets incremented again, and then decremented back to 0 (when eventFilter() returns) causing it to be garbage collected for a second time - resulting in the crash. The fix is to remove the TestWidget object from SIP's internal map of Python objects and C++ pointers before the QWidget dtor is called so that passing it as an argument to eventFilter() causes it to be re-wrapped as a new Python object. Phil _______________________________________________ PyKDE mailing list [email protected] http://mats.imk.fraunhofer.de/mailman/listinfo/pykde
