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()"))
***********************************************************

--
Lorenzo Mancini
Develer S.r.l.
http://www.develer.com/
_______________________________________________
PyQt mailing list    [email protected]
http://www.riverbankcomputing.com/mailman/listinfo/pyqt

Reply via email to