[PyQt] Automatically avoiding segfaults with sip.setdeleted()?
Russell Warren
russ at perspexis.com
Wed Apr 19 21:30:20 BST 2017
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.
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:
>>> from PyQt5 import QtCore
>>> obj1 = QtCore.QObject()
>>> obj2 = QtCore.QObject(parent = obj1)
>>> del obj1 # <-- Qt will also delete the obj2 child
>>> obj2.objectName()
Traceback (most recent call last):
File "<input>", line 1, in <module>
obj2.objectName()
RuntimeError: wrapped C/C++ object of type QObject has been deleted
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:
>>> from PyQt5 import QtWidgets
>>> app = QtWidgets.QApplication([])
>>> w = QtWidgets.QMainWindow()
>>> sb = w.statusBar() # <-- Qt internals set the child relationship
>>> del w # <-- Qt deletes sb child w/o python knowing
>>> sb.objectName()Segmentation fault
That segfault is clearly undesirable. However... fixing it is conceptually
trivial, since you just need to hook sip.setdeleted() up to the 'destroyed'
signal:
>>> import sip
>>> from PyQt5 import QtWidgets
>>> def on_destroy(obj):
... sip.setdeleted(obj)
...
>>> app = QtWidgets.QApplication([])
>>> w = QtWidgets.QMainWindow()
>>> sb = w.statusBar()
>>> sb.destroyed.connect(on_destroy)
>>> del w
>>> sb.objectName()
Traceback (most recent call last):
File "<input>", line 1, in <module>
sb.objectName()
RuntimeError: wrapped C/C++ object of type QStatusBar has been deleted
>>> # rejoice! no segfault!
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.
It seems like a good solution would be to ALWAYS connect the
QObject.destroyed signal of wrapped QObject instances to sip.setdeleted.
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?
Thanks,
Russ
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20170419/a03ad4ed/attachment.html>
More information about the PyQt
mailing list