[PyQt] Memory leak

Giovanni Bajo rasky at develer.com
Sun Jul 6 14:18:10 BST 2008


On Sat, 2008-07-05 at 23:04 +0100, Phil Thompson wrote:
> On Thu, 03 Jul 2008 16:04:42 +0200, Giovanni Bajo <rasky at develer.com>
> wrote:
> > Hi Phil,
> > 
> > with SIP 4.7.6, PyQt 4.2.2, Qt 4.4.0:
> > 
> > ========================================
> > import sip
> > import weakref
> > from PyQt4.Qt import *
> > 
> > class MyWidget(QWidget):
> >      def sizeHint(self):
> >          return QSize(900, 700)
> > 
> > app = QApplication([])
> > 
> > ws = MyWidget(None)
> > wr = weakref.ref(ws)
> > 
> > L = QVBoxLayout(None)
> > L.addWidget(ws)
> > L.activate()
> > del L
> > del ws
> > 
> > import gc
> > gc.collect()
> > 
> > assert wr() is None
> > ========================================
> > 
> > The assert triggers, meaning that the object of type MyWidget is not 
> > released.
> 
> This appears to be a Qt problem. Although the docs say that a layout takes
> ownership of the widget when addWidget() is called it leaves the
> destruction of the widget to the eventual owner of the layout and doesn't
> call the widget's dtor itself.
> 
> If the layout is never used (ie. never passed as an argument to
> QWidget.setLayout()) then all the widgets in the layout will leak.
> 
> An equivalent C++ version behaves in the same way.
> 
> I could change addWidget() so that the layout doesn't take ownership of the
> widget (ie. to match the implementation rather than the documentation) but
> that will break any code that creates a populated layout and returns it
> from a function.

I believe that it's more correct if you fix this code not to have any
memory leak. Either that, or you get smarter wrt when the ownership is
transferred from Python to C++ (that is, when the layout is reparented
to a widget, if ever). And in any case, you should probably raise this
issue with Trolltech (it's at least a documentation issue, I'd say).

Anyway, it turns out that I had reduced the testcase a little too much:
it was not the actual memory leak I was seeing in my application.

The correct testcase is this one:

========================================
#!/usr/bin/env python
import sip
import weakref
from PyQt4.Qt import *

class MyWidget(QWidget):
   def sizeHint(self):
       return QSize(900, 700)

app = QApplication([])

ww = QWidget(None)
ws = MyWidget(None)
wr = weakref.ref(ws)

L = QVBoxLayout(ww)
L.addWidget(ws)
L.activate()
del L
del ws
del ww

import gc
gc.collect()

assert wr() is None
========================================

This memory leak is related to the sizeHint() method (if you comment it,
there's no leak anymore) and to the fact that it is actually called at
least once (if you comment the .activate() call, there's no leak
anymore).
-- 
Giovanni Bajo
Develer S.r.l.
http://www.develer.com




More information about the PyQt mailing list