@canol said in Removing a QGraphicsProxyWidget from QGraphicsScene crashes the app, but only when using a button on a toolbar:
Hello, I have a medium sized app which had some random crashing problems. The crashes don't print any error messages. After a long investigation, getting rid of a lot of code, now I have a minimal example.
For problems that don't appear to produce any usable output, turning on all QLoggingCategory categories can help. QT_LOGGING_RULES="*.*=true" python test.py should flood the console with information about what Qt is doing prior to the crash.
In addition, installing a QObject::eventFilter() on the widget or QApplication can provide insight into how far input processing proceeded.
class Filter(QObject):
def __init__(self, target, *args, **kwargs):
super().__init__(*args, **kwargs)
target.installEventFilter(self)
def eventFilter(self, target, event):
print("Attempting to deliver {} to {}".format(event.type(), target))
return super().eventFilter(target, event)
app = QtWidgets.QApplication([])
filter = Filter(app)
The app has a QGraphicsView, and I add and remove QWidgets dynamically on the graphics view. The widgets have a toolbar, and the toolbar had a close button on it, which removes and deletes the widget from QGraphicsView. The crashes would randomly happen when I used this close button.
Here is a complete example (it is a PySide2 program, I haven't tried it with C++):
Porting the example to PyQt5 had crashes for the same input, on every attempt. Maybe PyQt is more aggressive in object reclamation than PySide.
My questions are:
**1) Why does it crash when I trigger the action using mouse click on the button?
The issue appears to be a deletion of the recipient object while there are events for it remaining in the queue. As mentioned in the documentation for QObject::~QObject(), this is a problem.
In C++, QObject::deleteLater() provides a convenient solution. Unfortunately, PySide and PyQt interfere by deleting the object when the last python reference is removed.
Why does it NOT crash, when I use the keyboard shortcut for the action?**
Different triggering mechanisms lead to a different sequence of events. If the last event triggers the action, deleting the object is fine. If there's a mouse release or a paint event after, deletion in the slot connected to the action is unsafe.
The example above uses a QPushButton, instead of a QToolBar+QAction, and it does not crash. So another question:
3) Why does it not crash when I use QPushButton instead of QToolBar+QAction?
As with #2, the events delivered are probably different.
This example won't crash. I guess waiting for the control to return to event loop before removing the item helps. So my final question is:
4) Why does using QTimer.singleShot() helps here?
Using QTimer.singleShot() schedules the slot for the next iteration of the event loop, after the currently pending events have been processed. The same thing can be accomplished using a queued connection for the action's slot instead of a 0-timer.