I know this has been asked many times before. I read all of those threads, and my case seems different. Everybody else who has this trouble has a few straightforward causes that I think I’ve ruled out, such as:

  • Starting a timer with no event loop running
  • Starting/stopping a timer from a thread other than the one that created the timer
  • Failing to set the parent property of a widget, leading to problems with the order of destruction

Below I have a minimal code sample that demonstrates the problem. Notice that I’ve started no threads or timers. I also have set the parent of every widget. If I remove the graph widgets, the problem goes away, so one is tempted to blame pyQtGraph, however, if I include the plot widgets but exclude all the blank tabs (i.e. every tab except tabCatchaTiger), the problem also goes away, and that seems to vindicate pyQtGraph.

Versions:

  • Windows 7
  • Python 2.7.8
  • Wing IDE 5.0.9-1
  • PyQt 4.11.1
  • PyQwt 5.2.1
  • PyQtGraph 0.9.8

Test case:

from PyQt4 import Qt, QtGui, QtCore
import PyQt4.Qwt5 as Qwt
import pyqtgraph as pg

pg.functions.USE_WEAVE = False # Lets pyqtgraph plot without gcc

pg.setConfigOption('background', 'w')
pg.setConfigOption('foreground', 'k')

# GUI for visualizing data from database
class crashyGUI(QtGui.QWidget) :

    def __init__(self) :
        # Make the window
        QtGui.QWidget.__init__(self)
        self.resize(700, QtGui.QDesktopWidget().screenGeometry(self).height()*.85)
        self.setWindowTitle('Data Visualization')

        # Create tab interface
        tabWidget = QtGui.QTabWidget(self)

        # define the tab objects
        self.tabEeny = QtGui.QWidget(tabWidget)
        self.tabMeeny = QtGui.QWidget(tabWidget)
        self.tabMiney = QtGui.QWidget(tabWidget)
        self.tabMoe = QtGui.QWidget(tabWidget)
        self.tabCatchaTiger = QtGui.QWidget(tabWidget)
        self.tabByThe = QtGui.QWidget(tabWidget)
        self.tabToe = QtGui.QWidget(tabWidget)

        # Initialize the tab objects
        self.initTabCatchaTiger()

        ###########################################
        ############### Main Layout ###############
        ###########################################

        tabWidget.addTab(self.tabEeny, 'Eeny')
        tabWidget.addTab(self.tabMeeny, 'Meeny')
        tabWidget.addTab(self.tabMiney, 'Miney')
        tabWidget.addTab(self.tabMoe, 'Moe')
        tabWidget.addTab(self.tabCatchaTiger, 'Catch a Tiger')
        tabWidget.addTab(self.tabByThe, 'By The')
        tabWidget.addTab(self.tabToe, 'Toe')

        self.mainLayout = QtGui.QVBoxLayout(self)
        self.mainLayout.addWidget(tabWidget)

        self.setLayout(self.mainLayout)

    def initTabCatchaTiger(self):
        ###########################################
        ############# ADC Capture Tab #############
        ###########################################
        # define tab layout
        grid = QtGui.QGridLayout(self.tabCatchaTiger)

        # create copy of adc plot and add to row 3 of the grid
        self.catchaTigerPlot1 = pg.PlotWidget(name = 'Catch a Tiger 1', parent = self.tabCatchaTiger)
        self.catchaTigerPlot1.setTitle('Catch a Tiger 1')
        grid.addWidget(self.catchaTigerPlot1, 2, 0, 1, 8)

        self.catchaTigerPlot2 = pg.PlotWidget(name = 'Catch a Tiger 2', parent = self.tabCatchaTiger)
        self.catchaTigerPlot2.setTitle('Catch a Tiger 2')
        grid.addWidget(self.catchaTigerPlot2, 3, 0, 1, 8)

        # set layout for tab
        self.tabCatchaTiger.setLayout(grid)

    def closeEvent(self, event) :
            pass

def main() :
    # open a QApplication and dialog() GUI
    app = QtGui.QApplication([])

    windowCrashy = crashyGUI()
    windowCrashy.show()
    app.exec_()

main()
5

There are 5 answers

6
Luke On BEST ANSWER

Personally, I don't put any effort into chasing exit crashes anymore--just use pg.exit() and be done with it.

(but if you do happen to find a bug in pyqtgraph, don't hesitate to open an issue on github)

5
Luke On

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.

2
ekhumoro On

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.

0
Avo Asatryan On

Try to write this in block of __init__:

self.setAttribute(Qt.WA_DeleteOnClose)
0
Lgum On

I had this happen as well and in my case it was caused by a call to deleteLater() on the aboutToQuit-Signal of the application, like so:

def closeEvent(self, *args, **kwargs):
   self.deleteLater()

if __name__ == "__main__":

   application = QtWidgets.QApplication(sys.argv)

   window = testApplication()
   # Handle application exit
   application.aboutToQuit.connect(window.closeEvent)
   # System exit
   sys.exit(application.exec_())

Getting rid of the deleteLater on the whole window seemed to solve it.