[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