[PyQt] QDialog with connect lambda "leaks"

J Barchan jnbarchan at gmail.com
Thu Jul 19 14:30:30 BST 2018


​​
PyQt 5.7.

I have a large body of existing UI code.  I have spent two days commenting
in & out bits of code to try to discover why some of its QDialogs "leak"
after calling QDialog.exec().

My definition of "leak" here is: after executing from somewhere else

dlg = QDialog(self)
QDialog.exec()

the instance of the dialog stays in existence permanently (as long as the
caller exists, which for me is till end of program).  That means that every
time that code gets executed, yet another new dialog is left around in
memory, which adds up over time.  All I do to test is go into the dialog
and immediately close it.

I discover this by inspecting QtWidgets.QApplication.allWidgets() and
reporting all QDialogs which are still in existence.  I see an
ever-increasing number of these dialogs, one per each time it's constructed
and executed, when & only when the code in the dialog is as follows.

I have finally tracked down the problematic line in the dialog's __init__().
Some of them have:

from elsewhere import ensureValidDecimal
self.lineEdit = QLineEdit(self)
self.lineEdit.editingFinished.connect(lambda: ensureValidDecimal(self))

*The vital bit is: they connect() to a lambda which references self.*

If the lambda does not need to pass self out as an argument, there will be
no leak.

If I go define (in this case) in the dialog (I actually sub-class from all
my QDialogs so I can add stuff) a dedicated function to avoid the lambda:

    def selfEnsureValidDecimal(self)
        ensureValidDecimal(self)

    self.lineEdit.editingFinished.connect(self.selfEnsureValidDecimal)

then there will also be no leak.

I can see that at some deep level there must be a reference counting issue
here.  In some shape or form, the fact that we have a lambda which passes
self to the outside world must mean Python/PyQt wants to keep a reference
to the dialog and this must be preventing its destruction.

But I don't know what to do about it.  There is a lot of code with a lot of
dialogs with all sorts of code attached.  So I need some kind of
explanation of what exactly can or cannot be done here, what to look for in
code, etc.  Note that I do *not* wish to use
QDialog.setAttribute(QtCore.Qt.WA_DeleteOnClose,
True) on all my dialogs (I *believe* that would solve the leak, but it's
not the point).  What must I *not* do if I do not expect such a
self-reference to be left around preventing Python/PyQt from actually
freeing up the dialog?

-- 
Kindest,
Jonathan
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20180719/ba7945c4/attachment.html>


More information about the PyQt mailing list