On Thu, 22 Jan 2009 15:56:28 +0100, Lorenzo Mancini <[email protected]> wrote: > Phil Thompson wrote: >> On Sat, 26 Jul 2008 21:07:09 +0200, Giovanni Bajo <[email protected]> >> wrote: >>> On Sat, 2008-07-26 at 17:10 +0100, Phil Thompson wrote: >>>> On Wed, 23 Jul 2008 15:42:18 +0200, Giovanni Bajo <[email protected]> >>>> wrote: >>>>> Hi Phil: >>>>> >>>>> >> ============================================================================= >>>>> import sip >>>>> from PyQt4.Qt import * >>>>> >>>>> called = [] >>>>> >>>>> class Core(QObject): >>>>> def __init__(self, parent=None): >>>>> QWidget.__init__(self, parent) >>>>> QObject.connect(self, SIGNAL("destroyed()"), self.callback) >>>>> QObject.connect(self, SIGNAL("destroyed()"), lambda: >>>>> self.callback()) >>>>> def callback(self): >>>>> called.append("done") >>>>> >>>>> app = QApplication([]) >>>>> core = Core(app) >>>>> sip.delete(core) >>>>> >>>>> assert len(called) == 2, called >>>>> >> ============================================================================= >>>>> Traceback (most recent call last): >>>>> File "bugpyqt.py", line 18, in <module> >>>>> assert len(called) == 2, called >>>>> AssertionError: ['done'] >>>>> >>>>> The slot with "lambda" is called, but the other one is not. >>>> The reason is that PyQt knows that self is being destroyed and >>>> self.callback may be a wrapped C++ method (although it isn't in this >>> case) >>>> so it won't invoke it. >>> Why does it refuse to invoke a wrapped C++ method? Even if self is being >>> destroyed (that is: within its C++ destructor) is still valid to invoke >>> methods AFAICT. After all, it's something that's perfectly doable in C++ >>> as well. Or not? >> >> I had second thoughts after my first response. A wrapped C++ method will >> check to see if the C++ instance has been destroyed anyway, so there is >> no >> need to check it here as well. >> >> However, a wrapped C++ method still won't work in exactly the same way as >> a >> C++ application because the "C++ instance has been destroyed" flag has to >> be set before the destroyed() signal has been emitted. But it's ok for >> pure >> Python methods. > > Hi Phil, it seems that the commit originated from this discussion (grep > "2008/07/26" in the sip changelog, just before 4.7.7 was released) > introduced a bug in how PyQt manages autodisconnection when delete is > called on a connected C++ object. Please consider the following snippet: > > *********************************************************** > import sip > from PyQt4.Qt import * > > class Receiver(QObject): > def myslot(self): > print self.objectName() > > emitter = QObject(None) > receiver = Receiver(None) > > QObject.connect(emitter, SIGNAL("TEST()"), receiver.myslot) > emitter.emit(SIGNAL("TEST()")) # myslot gets called > sip.delete(receiver) # autodisconnection expected > > # PyQt 4.4.2, SIP 4.7.6 - Disconnected as expected > # PyQt 4.4.4, SIP 4.7.8/4.7.9 - Still connected > > emitter.emit(SIGNAL("TEST()")) > *********************************************************** > > One would expect the runtime to break the connection after the > sip.delete() invocation, instead the connection is still in place with > recent versions of sip, so when the "TEST()" signal is emitted for the > second time, execution aborts with the feared "RuntimeError: underlying > C/C++ object has been deleted". > > > If you test the same behaviour with a wrapped C++ slot, you'll see that > sip breaks the connection as expected. > *********************************************************** > import sip > from PyQt4.Qt import * > > app = QApplication([]) > emitter = QObject(None) > receiver = QWidget(None) > QObject.connect(emitter, SIGNAL("TEST()"), receiver, SLOT("show()")) > emitter.emit(SIGNAL("TEST()")) > sip.delete(receiver) > emitter.emit(SIGNAL("TEST()")) > ***********************************************************
I think the current behaviour is correct and consistent with how PyQt handles disappearing C++ objects. In your first case you are connecting to a Python callable which is still a valid reference even though the underlying QObject has gone. PyQt will only complain about that if you try to use it, as you are doing by calling objectName(). If, instead, you had "del receiver" rather than "sip.delete(receiver)" then the autodisconnect would happen. If, as you show in the second example, you had connected to a Qt slot (eg. by decorating myslot() with pyqtSignature) then you would also get the autodisconnect. Phil _______________________________________________ PyQt mailing list [email protected] http://www.riverbankcomputing.com/mailman/listinfo/pyqt
