4

Using the below code, the __del__ method of my Preview widget never gets called. If I uncomment the "del window" line, it does. Why?

#!/usr/bin/env python

from PyQt4 import QtGui

class Preview(QtGui.QWidget):
  def __init__(self, parent):
    QtGui.QWidget.__init__(self, parent)

  def __del__(self):
    print("Deleting Preview")

class PreviewWindow(QtGui.QMainWindow):
  def __init__(self):
    QtGui.QMainWindow.__init__(self)

    self.widget = Preview(self)
    self.setCentralWidget(self.widget)

  def __del__(self):
    print("Deleting PreviewWindow")

if __name__ == "__main__":
  app = QtGui.QApplication(["Dimension Preview"])
  window = PreviewWindow()
  window.show()
  app.exec()
  # del window
Tavian Barnes
  • 12,477
  • 4
  • 45
  • 118
  • It works for me on Fedora Linux 14 (PyQt 4.8.3, Python 2.7). I had to change app.exec() to app.exec_() though. – xioxox Nov 03 '11 at 14:43
  • @xioxox. Are you sure that _both_ `__del__` methods get called? – ekhumoro Nov 03 '11 at 17:13
  • @xioxox. For me, using pyqt-4.8.6, only one `__del__` method gets called; but with pyqt-4.8.5, both do. I wonder if there is a bug in the latest version? – ekhumoro Nov 03 '11 at 23:04

1 Answers1

2

If a QObject subclass has a parent, then Qt will delete it when the parent is deleted. On the other hand, if a QObject subclass has no parent, it will (eventually) be deleted by python.

Hopefully this example will make things somewhat clearer:

from PyQt4 import QtGui

class Widget(QtGui.QWidget):
    def __init__(self, parent):
        QtGui.QWidget.__init__(self, parent)
        self.destroyed.connect(self.handleDestroyed)

    def __del__(self):
        print ('__del__:', self)

    def handleDestroyed(self, source):
        print ('destroyed:', source)

class Foo(Widget):
    def __init__(self, parent):
        Widget.__init__(self, parent)

class Bar(Widget):
    def __init__(self, parent):
        Widget.__init__(self, parent)

class Window(Widget):
    def __init__(self, parent=None):
        Widget.__init__(self, parent)
        self.foo = Foo(self)
        self.bar = Bar(None)

if __name__ == "__main__":

    app = QtGui.QApplication([__file__, '-widgetcount'])
    window = Window()
    window.show()
    app.exec_()

Which outputs:

__del__: <__main__.Window object at 0x88f514c>
destroyed: <__main__.Foo object at 0x88f5194>
__del__: <__main__.Bar object at 0x88f51dc>
Widgets left: 0    Max widgets: 4 

EDIT

On second thoughts, it appears that there may be a bug (or at least a difference in behaviour) with some versions of PyQt4.

As a possible workaround, it seems that creating two python names for the main widget and then explicitly deleting each of them may help to ensure that both the C++ and python sides of the object get destroyed.

If the following line is added to the above script:

tmp = window; del tmp, window

Then the output becomes:

__del__: <__main__.Window object at 0x8d3a14c>
__del__: <__main__.Foo object at 0x8d3a194>
__del__: <__main__.Bar object at 0x8d3a1dc>
Widgets left: 0    Max widgets: 4
ekhumoro
  • 115,249
  • 20
  • 229
  • 336
  • So `__del__` never being called is expected behaviour in that case? That leads to leaks in my application. And using `parent=None` doesn't help if you do `self.setCentralWidget(self.widget)`. – Tavian Barnes Nov 03 '11 at 21:17
  • @TavianBarnes. I have edited my answer and added a possible solution to your problem. – ekhumoro Nov 03 '11 at 22:56
  • 1
    For the record, this was the response: http://www.riverbankcomputing.com/pipermail/pyqt/2011-November/030656.html – Tavian Barnes Nov 06 '11 at 04:55