[PyQt] QDialog with connect lambda "leaks"
Kovid Goyal
kovid at kovidgoyal.net
Mon Jul 23 13:07:11 BST 2018
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
_____________________________________
More information about the PyQt
mailing list