Is deleteLater() necessary in PyQt/PySide?

One application of deleteLater can be cleaning up of yourself, i.e. scheduling a delete of an QObject (for example in threading) to free resources from within the object itself.

Here for example someone is using it in connection with the signal thread.finished. It might be restricted to cases with heavy signalling though.


It depends what you mean by "necessary".

An application could potentially consume a lot of memory if (for example) care is not taken when closing widgets. The QObject-based classes are designed to be (optionally) linked together in a hierarchy. When a top-level object is deleted, Qt will automatically delete all its child objects as well. However, when closing widgets (which are sub-classes of QObject), automatic deletion will only happen if the Qt.WA_DeleteOnClose attribute is set (which, by default, it usually isn't).

To illustrate, try repeatedly opening and closing the dialog in this demo script, and watch how the global list of objects grows:

import sys
from PyQt5 import QtCore, QtWidgets

class Window(QtWidgets.QWidget):
    def __init__(self):
        super().__init__()
        self.checkbox = QtWidgets.QCheckBox('Delete')
        self.button = QtWidgets.QPushButton('Open', self)
        self.button.clicked.connect(self.openDialog)
        layout = QtWidgets.QHBoxLayout(self)
        layout.addWidget(self.checkbox)
        layout.addWidget(self.button)

    def openDialog(self):
        widget = QtWidgets.QDialog(self)
        if (self.checkbox.isChecked() and
            not widget.testAttribute(QtCore.Qt.WA_DeleteOnClose)):
            widget.setAttribute(QtCore.Qt.WA_DeleteOnClose)
            for child in self.findChildren(QtWidgets.QDialog):
                if child is not widget:
                    child.deleteLater()
        label = QtWidgets.QLabel(widget)
        button = QtWidgets.QPushButton('Close', widget)
        button.clicked.connect(widget.close)
        layout = QtWidgets.QVBoxLayout(widget)
        layout.addWidget(label)
        layout.addWidget(button)
        objects = self.findChildren(QtCore.QObject)
        label.setText('Objects = %d' % len(objects))
        print(objects)
        widget.show()

if __name__ == '__main__':

    app = QtWidgets.QApplication(sys.argv)
    window = Window()
    window.setGeometry(500, 300, 100, 50)
    window.show()
    sys.exit(app.exec_())

With PyQt/PySide, there are two aspects to object ownership: the Python part, and the Qt part. Often, removing the last Python reference to an object won't be enough to fully clean up, because there could still be a reference held on the Qt side.

In general, Qt tends not to implicity delete objects. So if your application creates and removes lots of QObjects (or opens and closes lots of QWidgets), you may need to take steps to delete them explicitly if memory usage is a concern.

UPDATE:

Just to add to the points above on object ownership. Sometimes, it is possible to hold a Python reference to an object, whilst the Qt part gets deleted. When this happens, you will see an error like this:

RuntimeError: underlying C/C++ object has been deleted

Usually, the Qt documentation will give some hints about when this might happen. For instance, QAbstractItemView.setModel gives this warning:

The view does not take ownership of the model unless it is the model's parent object...

This is telling you that you must either keep a Python reference to the object, or pass a suitable parent object to the object's constructor, because Qt will not always automatically reparent it.