<div dir="ltr"><div class="gmail_default" style="font-family:tahoma,sans-serif"><br></div><div class="gmail_extra"><br><div class="gmail_quote">On 23 July 2018 at 13:07, Kovid Goyal <span dir="ltr"><<a href="mailto:kovid@kovidgoyal.net" target="_blank">kovid@kovidgoyal.net</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">The way I work around this is to sue the following function to connect<br>
signals to lambdas<br>
<br>
def connect_lambda(bound_signal, self, func, **kw):<br>
    r = weakref.ref(self)<br>
    del self<br>
    num_args = func.__code__.co_argcount - 1<br>
    if num_args < 0:<br>
        raise TypeError('lambda must take at least one argument')<br>
<br>
    def slot(*args):<br>
        ctx = r()<br>
        if ctx is not None:<br>
            if len(args) != num_args:<br>
                args = args[:num_args]<br>
            func(ctx, *args)<br>
<br>
    bound_signal.connect(slot, **kw)<br>
<br>
<br>
It can be used like this:<br>
<br>
Instead of<br>
<br>
self.editingFinished.connect(<wbr>lambda: self.whatever())<br>
<br>
do<br>
<br>
connect_lambda(self.<wbr>editingFinished, self, lambda self: self.whatever())<br>
<br>
<br>
it would be nice if Phil added a connect_lambda or similar method to the<br>
bound method class, so it could be used conveniently.<br>
<br>
Kovid.<br>
<span class=""><br>
On Thu, Jul 19, 2018 at 02:30:30PM +0100, J Barchan wrote:<br>
> ​​<br>
> PyQt 5.7.<br>
> <br>
> I have a large body of existing UI code.  I have spent two days commenting<br>
> in & out bits of code to try to discover why some of its QDialogs "leak"<br>
> after calling QDialog.exec().<br>
> <br>
> My definition of "leak" here is: after executing from somewhere else<br>
> <br>
> dlg = QDialog(self)<br>
> QDialog.exec()<br>
> <br>
> the instance of the dialog stays in existence permanently (as long as the<br>
> caller exists, which for me is till end of program).  That means that every<br>
> time that code gets executed, yet another new dialog is left around in<br>
> memory, which adds up over time.  All I do to test is go into the dialog<br>
> and immediately close it.<br>
> <br>
> I discover this by inspecting QtWidgets.QApplication.<wbr>allWidgets() and<br>
> reporting all QDialogs which are still in existence.  I see an<br>
> ever-increasing number of these dialogs, one per each time it's constructed<br>
> and executed, when & only when the code in the dialog is as follows.<br>
> <br>
> I have finally tracked down the problematic line in the dialog's __init__().<br>
> Some of them have:<br>
> <br>
> from elsewhere import ensureValidDecimal<br>
> self.lineEdit = QLineEdit(self)<br>
> self.lineEdit.editingFinished.<wbr>connect(lambda: ensureValidDecimal(self))<br>
> <br>
</span>> *The vital bit is: they connect() to a lambda which references self.*<br>
<span class="">> <br>
> If the lambda does not need to pass self out as an argument, there will be<br>
> no leak.<br>
> <br>
> If I go define (in this case) in the dialog (I actually sub-class from all<br>
> my QDialogs so I can add stuff) a dedicated function to avoid the lambda:<br>
> <br>
>     def selfEnsureValidDecimal(self)<br>
>         ensureValidDecimal(self)<br>
> <br>
>     self.lineEdit.editingFinished.<wbr>connect(self.<wbr>selfEnsureValidDecimal)<br>
> <br>
> then there will also be no leak.<br>
> <br>
> I can see that at some deep level there must be a reference counting issue<br>
> here.  In some shape or form, the fact that we have a lambda which passes<br>
> self to the outside world must mean Python/PyQt wants to keep a reference<br>
> to the dialog and this must be preventing its destruction.<br>
> <br>
> But I don't know what to do about it.  There is a lot of code with a lot of<br>
> dialogs with all sorts of code attached.  So I need some kind of<br>
> explanation of what exactly can or cannot be done here, what to look for in<br>
</span>> code, etc.  Note that I do *not* wish to use<br>
> QDialog.setAttribute(QtCore.<wbr>Qt.WA_DeleteOnClose,<br>
> True) on all my dialogs (I *believe* that would solve the leak, but it's<br>
> not the point).  What must I *not* do if I do not expect such a<br>
<span class="">> self-reference to be left around preventing Python/PyQt from actually<br>
> freeing up the dialog?<br>
> <br>
> -- <br>
> Kindest,<br>
> Jonathan<br>
<br>
</span>> ______________________________<wbr>_________________<br>
> PyQt mailing list    <a href="mailto:PyQt@riverbankcomputing.com">PyQt@riverbankcomputing.com</a><br>
> <a href="https://www.riverbankcomputing.com/mailman/listinfo/pyqt" rel="noreferrer" target="_blank">https://www.<wbr>riverbankcomputing.com/<wbr>mailman/listinfo/pyqt</a><br>
<span class="HOEnZb"><font color="#888888"><br>
<br>
-- <br>
______________________________<wbr>_______<br>
<br>
Dr. Kovid Goyal <br>
<a href="https://www.kovidgoyal.net" rel="noreferrer" target="_blank">https://www.kovidgoyal.net</a><br>
<a href="https://calibre-ebook.com" rel="noreferrer" target="_blank">https://calibre-ebook.com</a><br>
______________________________<wbr>_______<br>
______________________________<wbr>_________________<br>
PyQt mailing list    <a href="mailto:PyQt@riverbankcomputing.com">PyQt@riverbankcomputing.com</a><br>
<a href="https://www.riverbankcomputing.com/mailman/listinfo/pyqt" rel="noreferrer" target="_blank">https://www.<wbr>riverbankcomputing.com/<wbr>mailman/listinfo/pyqt</a></font></span></blockquote></div><br><div style="font-family:tahoma,sans-serif" class="gmail_default">​@Kovid Goyal</div><div style="font-family:tahoma,sans-serif" class="gmail_default">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 :)​</div><br clear="all"><br>-- <br><div class="gmail_signature" data-smartmail="gmail_signature"><div dir="ltr"><div><div dir="ltr"><div><span style="font-family:tahoma,sans-serif">Kindest,</span></div><div><span style="font-family:tahoma,sans-serif">Jonathan</span></div></div></div></div></div>
</div></div>