<div dir="ltr"><div>I've been looking into frustratingly frequent segfaults with a PyQt5 application today. The go-to reason is usually that some C-side Qt instance has been deleted without PyQt being aware of it. PyQt is decent at handling this case gracefully sometimes (with a nice "RuntimeError: wrapped C/C++ object of type <whatever> has been deleted" exception), but it is definitely not perfect.</div><div><br></div><div>Experimentation seems to show that when python is aware of the hierarchy/parentage via python-side constructors, it is handled well, as in this short example:</div><div><br></div><div><font face="monospace, monospace" color="#351c75">>>> from PyQt5 import QtCore</font></div><div><font face="monospace, monospace" color="#351c75">>>> obj1 = QtCore.QObject()</font></div><div><font face="monospace, monospace" color="#351c75">>>> obj2 = QtCore.QObject(parent = obj1)</font></div><div><font face="monospace, monospace" color="#351c75">>>> del obj1 # <-- Qt will also delete the obj2 child</font></div><div><font face="monospace, monospace" color="#351c75">>>> obj2.objectName()</font></div><div><font face="monospace, monospace" color="#351c75">Traceback (most recent call last):</font></div><div><font face="monospace, monospace" color="#351c75"> File "<input>", line 1, in <module></font></div><div><font face="monospace, monospace" color="#351c75"> obj2.objectName()</font></div><div><font face="monospace, monospace" color="#351c75">RuntimeError: wrapped C/C++ object of type QObject has been deleted</font></div><div><br></div><div>This exception is great. However, when the parentage is set internally to the C++ code (without python knowing about it), segfaults can result. For example:</div><div><br></div><div><font face="monospace, monospace" color="#351c75">>>> from PyQt5 import QtWidgets</font></div><div><font face="monospace, monospace" color="#351c75">>>> app = QtWidgets.QApplication([])</font></div><div><font face="monospace, monospace" color="#351c75">>>> w = QtWidgets.QMainWindow()</font></div><div><font face="monospace, monospace" color="#351c75">>>> sb = w.statusBar() # <-- Qt internals set the child relationship</font></div><div><font face="monospace, monospace" color="#351c75">>>> del w # <-- Qt deletes sb child w/o python knowing</font></div><div><font face="monospace, monospace"><font color="#351c75">>>> sb.objectName(</font>)<font color="#ff0000">Segmentation fault</font></font></div><div><br></div><div>That segfault is clearly undesirable. However... fixing it is conceptually trivial, since you just need to hook sip.setdeleted() up to the 'destroyed' signal:</div><div><br></div><div><font face="monospace, monospace" color="#351c75">>>> import sip</font></div><div><font face="monospace, monospace" color="#351c75">>>> from PyQt5 import QtWidgets</font></div><div><font face="monospace, monospace" color="#351c75">>>> def on_destroy(obj):</font></div><div><font face="monospace, monospace" color="#351c75">... sip.setdeleted(obj)</font></div><div><font face="monospace, monospace" color="#351c75">... </font></div><div><font face="monospace, monospace" color="#351c75">>>> app = QtWidgets.QApplication([])</font></div><div><font face="monospace, monospace" color="#351c75">>>> w = QtWidgets.QMainWindow()</font></div><div><font face="monospace, monospace" color="#351c75">>>> sb = w.statusBar()</font></div><div><font face="monospace, monospace" color="#351c75">>>> sb.destroyed.connect(on_destroy)</font></div><div><font face="monospace, monospace" color="#351c75">>>> del w</font></div><div><font face="monospace, monospace" color="#351c75">>>> sb.objectName()</font></div><div><font face="monospace, monospace" color="#351c75">Traceback (most recent call last):</font></div><div><font face="monospace, monospace" color="#351c75"> File "<input>", line 1, in <module></font></div><div><font face="monospace, monospace" color="#351c75"> sb.objectName()</font></div><div><font face="monospace, monospace" color="#351c75">RuntimeError: wrapped C/C++ object of type QStatusBar has been deleted</font></div><div><font face="monospace, monospace" color="#351c75">>>> # rejoice! no segfault!</font></div><div><br></div><div>Getting that RuntimeError is clearly MUCH better than getting a segfault. but having to manually connect/manage all of those 'destroyed' connections in application code is obviously not ideal, and is prone to errors.</div><div><br></div><div>It seems like a good solution would be to ALWAYS connect the QObject.destroyed signal of wrapped QObject instances to sip.setdeleted.</div><div><br></div><div>Is there a way to do this destroyed->sip.setdeleted hookup automatically for all wrapped QObjects without needing to manage it for each PyQt5.QObject instance at the application layer?</div><div><br></div><div>Thanks,</div><div>Russ</div><div><br></div>
</div>