[PySide] QObject.destroyed() is not emitted
Christian Tismer
tismer at stackless.com
Thu Nov 29 15:54:00 CET 2012
Hi Alexey,
found it! :-)
This is in fact a PySide quirk:
Your deletion method is an attribute that lives inside the object to be
deleted, and that is the problem:
Destruction seems not to care about the order of destruction,
and the __dict__ that also holds the onDestroy bound method
gets destructed before it is called.
I tried that for other kinds of objects, it is always the same.
If you use a function from elsewhere, it works.
I tried to put the onDestroy method into an external list, with no success,
because that reference disables destruction.
A general work-around could use a weakref, but for your case I think
this simple solution is fine:
from PySide.QtCore import QAbstractTableModel
class MyModel(QAbstractTableModel):
def __init__(self, *args):
super(MyModel, self).__init__(*args)
self.destroyed.connect(self.onDestroy)
@staticmethod
def onDestroy():
print('destroyed')
m = MyModel()
del m
prints "destroyed" :=)
cheers - chris
On 11/29/12 1:05 PM, Alexey Vihorev wrote:
> Ok, let's get less abstract. I got a QTableModel that has to free up some external resources upon its destruction (rollback/close a DB transaction). In this particular case It's parent is QMdiSubWindow with self.setAttribute(QtCore.Qt.WA_DeleteOnClose), but that is not necessary - it can be used inside complex widgets, etc. The code is trivial, but does not work:
>
> class MyModel(QAbstractTableModel):
>
> def onDestroy(self):
> #Do some housekeeping
>
> def __init__(self, entity_cls, parent=None, load=True, criteria=None):
> ...
> self.destroyed.connect(self.onDestroy)
> ...
>
>
>
> -----Original Message-----
> From: Christian Tismer [mailto:tismer at stackless.com]
> Sent: 29 ноября 2012 г. 6:25
> To: Alexey Vihorev
> Cc: 'Stephan Deibel'; pyside at qt-project.org
> Subject: Re: [PySide] QObject.destroyed() is not emitted
>
> Alexey,
>
> there is not enough context to see your problem.
>
> The behavior in our similar examples is correct:
>
> Noting gets destroyed until we release the references from Python.
> As long as you have a reference, no destruction occurs.
>
> By the assignment
>
> obj = QtCore.Object(),
>
> you create a reference. You do not actively destroy the object by
>
> del ob
>
> but you just remove one reference. And because nothing else has a reference to it, _then_ the destruction takes place.
>
> The question is about the interaction with other objects.
> If everything is correct with PySide (which is certainly still not), then destruction should just work automatically when it's time.
>
> So please give us more of your code.
> We cannot see what the real problem is.
>
> cheers - chris
>
> On 29.11.12 04:26, Alexey Vihorev wrote:
>> Yes, I tried something like that:
>>
>> def onDestroy(*args):
>> print('destroyed')
>>
>> obj = QtCore.QObject()
>> obj.destroyed.connect(onDestroy)
>>
>> obj2 = QtCore.QObject(obj)
>> obj2.destroyed.connect(onDestroy)
>>
>> del(obj)
>>
>> In this case onDestroy() is called twice, just as it should. But once
>> again
>> - omit the last line and nothing Is called. So for it to work explicit
>> destruction must be called at the top of the chain. Which is
>> problematic in my case.
>>
>> -----Original Message-----
>> From: Christian Tismer [mailto:tismer at stackless.com]
>> Sent: 29 ноября 2012 г. 0:58
>> To: Alexey Vihorev
>> Cc: 'Stephan Deibel'; pyside at qt-project.org
>> Subject: Re: [PySide] QObject.destroyed() is not emitted
>>
>> Hi Alexey,
>>
>> I tried your example and added a parent.
>> Right now it looks exactly as it should be:
>>
>> >>> def onDestroy(*args):
>> ... print('destroyed')
>> ...
>> >>> obj = QtCore.QObject()
>> >>> obj.destroyed.connect(onDestroy) True
>> >>> par = QtCore.QObject()
>> >>> obj.setParent(par)
>> >>> del obj
>> >>> del par
>> destroyed
>> >>>
>>
>> This is PySide on Mac OS X,
>> >>> PySide.__version__
>> '1.1.2'
>> >>> QtCore.__version__
>> '4.8.3'
>> >>>
>>
>> Maybe you have some more references to the object?
>> For simple refcount checking, you can use sys.getrefcount .
>>
>> >>> g=sys.getrefcount
>> >>> g(obj)
>> 2
>>
>> Note that this prints 2 because of the reference from the call. So the
>> object should go away when the one real reference goes away.
>> Maybe you have a different problem, involving more objects?
>>
>> ---------
>>
>> By the way, PySide seems to be not happy with reference cycles, while
>> pure python is ok with that:
>>
>> >>> obj = QtCore.QObject()
>> >>> obj.setParent(obj)
>> >>> g(obj)
>> 2
>>
>> Eeek, that should be 3.
>> And if fact.......
>>
>> >>> del(obj)
>> Segmentation fault: 11
>>
>> ciao -- Chris
>>
>>
>> On 11/28/12 9:24 PM, Alexey Vihorev wrote:
>>> Hi!
>>> Well, I understand that if what I need to do is achievable with
>>> explicit deletion, I should go for it :) The problem is that in my
>>> case the deletion of the object is triggered by deletion of another
>>> object - its parent. But I can't catch the deletion event - neither
>>> of the
>> object nor its parent.
>>> That's the problem. The whole reason I started looking into that is
>>> that one of my objects needed to free some external resources when
>> destroyed.
>>> -----Original Message-----
>>> From: Stephan Deibel [mailto:sdeibel at wingware.com]
>>> Sent: 28 ноября 2012 г. 16:33
>>> To: Alexey Vihorev
>>> Cc: pyside at qt-project.org
>>> Subject: Re: [PySide] QObject.destroyed() is not emitted
>>>
>>> Alexey Vihorev wrote:
>>>> Got problems with the following code:
>>>>
>>>> from PySide import QtCore
>>>>
>>>> def onDestroy(*args):
>>>>
>>>> print('destroyed')
>>>>
>>>> obj = QtCore.QObject()
>>>>
>>>> obj.destroyed.connect(onDestroy)
>>>>
>>>> The onDestroy() method is not called, unless del(obj) is called
>>>> explicitly. In PyQt4 it is called without del(obj). Is that a bug or
>>>> intended behavior? I'm using Qt 4.8.2, PySide 1.1.2 32bit, Windows
>>>> 7-64
>>>>
>>> Since no one responded I'll take a stab at this:
>>>
>>> I suspect the life cycle of the QObject under PySide is a little
>>> different and the instance will be deleted later during garbage
>>> collection and not immediately when it goes out of scope. I'm
>>> actually not sure why that is happening in PyQt (seems slightly surprising).
>>>
>>> In general if you want to make sure an instance is deleted at a
>>> particular moment in time, you need to delete it explicitly. By that,
>>> I mean calling some sort of delete method (in PySide I think it's
>>> shiboken.delete(obj) and not just "del obj". The latter just deletes
>>> your reference to the instance but doesn't destroy the instance until
>>> all other references are gone and it is garbage collected. Of course
>>> calling
>>> shiboken.delete(obj) will be a problem if you still have other
>>> references and try to use them!
>>>
>>> There is also obj.deleteLater() which marks an object for deletion
>>> but it's not deleted until the event loop is reached again. Depending
>>> on what you're doing this may be a safer way to delete it.
>>>
>>> At first I thought this might be a refcount bug in PySide. However,
>>> I'm thinking not since "del obj" just deletes your reference to it
>>> and if there were extra references hanging around (either on purpose
>>> or as a result of a refcount bug) then it would not be deleting the
>>> instance at all even after "del obj".
>>>
>>> I don't understand PySide internals that well so it's possible I'm
>>> missing some subtlety here. I'm going more on my knowledge of Python
>>> in
>> general.
>>> - Stephan
>>>
>>> _______________________________________________
>>> PySide mailing list
>>> PySide at qt-project.org
>>> http://lists.qt-project.org/mailman/listinfo/pyside
>
--
Christian Tismer :^) <mailto:tismer at stackless.com>
Software Consulting : Have a break! Take a ride on Python's
Karl-Liebknecht-Str. 121 : *Starship* http://starship.python.net/
14482 Potsdam : PGP key -> http://pgp.uni-mainz.de
phone +49 173 24 18 776 fax +49 (30) 700143-0023
PGP 0x57F3BF04 9064 F4E1 D754 C2FF 1619 305B C09C 5A3B 57F3 BF04
whom do you want to sponsor today? http://www.stackless.com/
More information about the PySide
mailing list