[PyQt] QDialog with connect lambda "leaks"

J Barchan jnbarchan at gmail.com
Tue Jul 24 08:30:50 BST 2018


On 23 July 2018 at 13:07, Kovid Goyal <kovid at kovidgoyal.net> wrote:

> The way I work around this is to sue the following function to connect
> signals to lambdas
>
> def connect_lambda(bound_signal, self, func, **kw):
>     r = weakref.ref(self)
>     del self
>     num_args = func.__code__.co_argcount - 1
>     if num_args < 0:
>         raise TypeError('lambda must take at least one argument')
>
>     def slot(*args):
>         ctx = r()
>         if ctx is not None:
>             if len(args) != num_args:
>                 args = args[:num_args]
>             func(ctx, *args)
>
>     bound_signal.connect(slot, **kw)
>
>
> It can be used like this:
>
> Instead of
>
> self.editingFinished.connect(lambda: self.whatever())
>
> do
>
> connect_lambda(self.editingFinished, self, lambda self: self.whatever())
>
>
> it would be nice if Phil added a connect_lambda or similar method to the
> bound method class, so it could be used conveniently.
>
> Kovid.
>
> On Thu, Jul 19, 2018 at 02:30:30PM +0100, J Barchan wrote:
> > ​​
> > 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
>
> > _______________________________________________
> > PyQt mailing list    PyQt at riverbankcomputing.com
> > https://www.riverbankcomputing.com/mailman/listinfo/pyqt
>
>
> --
> _____________________________________
>
> Dr. Kovid Goyal
> https://www.kovidgoyal.net
> https://calibre-ebook.com
> _____________________________________
> _______________________________________________
> PyQt mailing list    PyQt at riverbankcomputing.com
> https://www.riverbankcomputing.com/mailman/listinfo/pyqt


​@Kovid Goyal
Blimey, that looks complicated, I'm a Python noob!  Thank you for the
code.  I'm not going to use it right now --- I prefer to rewrite code now
so as not to use self-referencing lambdas --- but have copied it to my
"utility functions" for the future :)​


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


More information about the PyQt mailing list