Error "QObject::startTimer: QTimer can only be used with threads started with QThread" many times when closing application

There seem to be two closely-related issues in the example.

The first one causes Qt to print the QObject::startTimer: QTimer can only be used with threads started with QThread messages on exit.

The second one (which may not affect all users) causes Qt to print QPixmap: Must construct a QApplication before a QPaintDevice, and then dump core on exit.

Both of these issues are caused by python deleting objects in an unpredicable order when it exits.

In the example, the second issue can be fixed by adding the following line to the __init__ of the top-level window:

    self.setAttribute(QtCore.Qt.WA_DeleteOnClose)

Unless QApplication.setQuitOnLastWindowClosed has been changed to False, this will ensure that the application quits at the right time, and that Qt has a chance to automatically delete all the children of the top-level window before the python garbage-collector gets to work.

However, for this to be completely successful, all the relevant objects must be linked together in a parent-child hierarchy. The example code does this where it can, but there seem to be some critical places in the initialization of the PlotWidget class where it is not done.

In particular, there is nothing to ensure that the central item of the PlotWidget has a parent set when it is created. If the relevant part of the code is changed to this:

class PlotWidget(GraphicsView):
    ...
    def __init__(self, parent=None, background='default', **kargs):
        GraphicsView.__init__(self, parent, background=background)
        ...
        self.plotItem = PlotItem(**kargs)
        # make sure the item gets a parent
        self.plotItem.setParent(self)
        self.setCentralItem(self.plotItem)

then the first issue with the QTimer messages also goes away.


Here's a better answer:

You are allowing the QApplication to be collected before python exits. This causes two different issues:

  1. The QTimer error messages are caused by pyqtgraph trying to track its ViewBoxes after the QApplication has been destroyed.

  2. The crash appears to be intrinsic to Qt / PyQt. The following crashes in the same way:

    from PyQt4 import Qt, QtGui, QtCore
    
    def main() :
        app = QtGui.QApplication([])
        x = QtGui.QGraphicsView()
        s = QtGui.QGraphicsScene()
        x.setScene(s)
        x.show()
        app.exec_()
    
    main()
    

You can fix it by adding global app to your main function, or by creating the QApplication at the module level.