<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 19 July 2018 at 14:30, J Barchan <span dir="ltr"><<a href="mailto:jnbarchan@gmail.com" target="_blank">jnbarchan@gmail.com</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div></div><div style="font-family:tahoma,sans-serif">PyQt 5.7.</div><div style="font-family:tahoma,sans-serif"><br></div><div style="font-family:tahoma,sans-serif">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 <span style="font-family:monospace,monospace">QDialog</span>s "leak" after calling <span style="font-family:monospace,monospace">QDialog.exec()</span>.</div><div style="font-family:tahoma,sans-serif"><br></div><div style="font-family:tahoma,sans-serif">My definition of "leak" here is: after executing from somewhere else<br></div><div style="font-family:tahoma,sans-serif"><br></div><div><span style="font-family:monospace,monospace">dlg = QDialog(self)<br></span></div><div style="font-family:tahoma,sans-serif"><span style="font-family:monospace,monospace">QDialog.exec()</span><br></div><div style="font-family:tahoma,sans-serif"><br></div><div style="font-family:tahoma,sans-serif">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.<br></div><div style="font-family:tahoma,sans-serif"><br></div><div style="font-family:tahoma,sans-serif">I discover this by inspecting <span style="font-family:monospace,monospace">QtWidgets.QApplication.<wbr>allWidgets() </span>and reporting all <span style="font-family:monospace,monospace">QDialogs</span> 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.</div><div style="font-family:tahoma,sans-serif"><br></div><div style="font-family:tahoma,sans-serif">I have finally tracked down the problematic line in the dialog's <span style="font-family:monospace,monospace">__init__()</span>. Some of them have:</div><div style="font-family:tahoma,sans-serif"><br></div><div style="font-family:tahoma,sans-serif"><span style="font-family:monospace,monospace">from elsewhere import ensureValidDecimal<br>self.lineEdit = QLineEdit(self)<br>self.lineEdit.editingFinished.<wbr>connect(lambda: ensureValidDecimal(self))</span><br></div><div style="font-family:tahoma,sans-serif"><br></div><div style="font-family:tahoma,sans-serif"><u><i>The vital bit is: they <span style="font-family:monospace,monospace">connect()</span> to a <span style="font-family:monospace,monospace">lambda</span> which references <span style="font-family:monospace,monospace"><b>self</b></span>.</i></u></div><div style="font-family:tahoma,sans-serif"><br></div><div style="font-family:tahoma,sans-serif">If the <span style="font-family:monospace,monospace">lambda</span> does not need to pass <span style="font-family:monospace,monospace">self</span> out as an argument, there will be no leak.</div><div style="font-family:tahoma,sans-serif"><br></div><div style="font-family:tahoma,sans-serif">If I go define (in this case) in the dialog (I actually sub-class from all my <span style="font-family:monospace,monospace">QDialog</span>s so I can add stuff) a dedicated function to avoid the <span style="font-family:monospace,monospace">lambda</span>:</div><div><span style="font-family:monospace,monospace"><br></span></div><div><span style="font-family:monospace,monospace"> def selfEnsureValidDecimal(self)</span></div><div><span style="font-family:monospace,monospace"> ensureValidDecimal(self)</span></div><div><span style="font-family:monospace,monospace"><br></span></div><div style="font-family:tahoma,sans-serif"><span style="font-family:monospace,monospace"> self.lineEdit.editingFinished.<wbr>connect(self.<wbr>selfEnsureValidDecimal)</span><br></div><div style="font-family:tahoma,sans-serif"><br></div><div style="font-family:tahoma,sans-serif">then there will also be no leak.</div><div style="font-family:tahoma,sans-serif"><br></div><div style="font-family:tahoma,sans-serif">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 <span style="font-family:monospace,monospace">lambda</span> which passes <span style="font-family:monospace,monospace">self</span> to the outside world must mean Python/PyQt wants to keep a reference to the dialog and this must be preventing its destruction.</div><div style="font-family:tahoma,sans-serif"><br></div><div style="font-family:tahoma,sans-serif">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 <i>not</i> wish to use <span style="font-family:monospace,monospace">QDialog.setAttribute(QtCore.<wbr>Qt.WA_DeleteOnClose, True)</span> on all my dialogs (I <i>believe</i> that would solve the leak, but it's not the point). What must I <i>not</i> do if I do not expect such a self-reference to be left around preventing Python/PyQt from actually freeing up the dialog?<span class="gmail-HOEnZb"><font color="#888888"><br></font></span></div><span class="gmail-HOEnZb"><font color="#888888"><br><div>-- <br><div class="gmail-m_-8076735599705967855gmail_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></font></span></div>
</blockquote></div><br><div style="font-family:tahoma,sans-serif" class="gmail_default"></div><div style="font-family:tahoma,sans-serif" class="gmail_default">Oh dear! Googling hard, I have just come across <a href="https://stackoverflow.com/a/48501804/489865">https://stackoverflow.com/a/48501804/489865</a>, stating:</div><div style="font-family:tahoma,sans-serif" class="gmail_default"><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Beware! As soon as you connect your signal to a lambda slot with a
reference to self, your widget will not be garbage-collected! That's
because lambda creates a closure with yet another uncollectable
reference to the widget.</blockquote><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">Thus, self.someUIwidget.someSignal.connect(lambda p: self.someMethod(p)) is very evil :)
</blockquote><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default">which confirms exactly what I have spent days discovering and typing in :(</div><div style="font-family:tahoma,sans-serif" class="gmail_default"><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default">So, that is indeed the problem? And so I am required to go through all code looking for exactly the pattern to change it? Does this only apply to <span style="font-family:monospace,monospace">QWidget.connect()</span>, I only need to look at all <span style="font-family:monospace,monospace">connect()</span> calls, or are there other similar cases?<br></div><div style="font-family:tahoma,sans-serif" class="gmail_default"><br></div><div style="font-family:tahoma,sans-serif" class="gmail_default"><br></div><br clear="all"><br>-- <br><div class="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>