Search code examples
matplotlibpyqt4qtgui

Why does a pyplot window behave differently when it is generated from a stand-alone function vs. a function called from an event handler?


When I run my setup_plot and dataq functions on their own the pyplot window behaves as expected, i.e. after the run finishes the buttons (pan/zoom etc) in the navigation toolbar are active. However, when I import the functions and call them from a button event handler in my main program the pyplot window stays active after the run finishes: it is still accepting data and the toolbar buttons are inactive. How should I stop plotting without closing the plot window? Here is a snippet:

class MyWindowClass(QtGui.QWidget, form_class):
    def __init__(self, parent=None):
       QtGui.QWidget.__init__(self, parent)
       self.setupUi(self)
       self.pushButton_Run.clicked.connect(self.pushbutton_run_clicked)
       self.pushButton_Stop.clicked.connect(self.pushbutton_stop_clicked)

    def pushbutton_run_clicked(self):
        # get line edit values here 
        functions.setup_plot()
        functions.dataq(steptime, runtime, CO2_1, CO2_2)

Solution

  • I tried to reproduce your problem by creating a simple MCVE based on the code snippet you gave us. The only thing I could find that would make my simple application to behave as you described is if I do not set the backend of matplotlib to Qt4Agg.

    from PyQt4 import QtGui
    import sys
    import numpy as np
    
    import matplotlib as mpl
    mpl.use('Qt4Agg')
    import matplotlib.pyplot as plt
    
    class MyWindowClass(QtGui.QWidget):
        def __init__(self, parent=None):
           super(MyWindowClass, self).__init__(parent)
    
           pushButton_Run = QtGui.QPushButton('Run')
           pushButton_Run.clicked.connect(self.pushbutton_run_clicked)
    
           layout = QtGui.QGridLayout()
           layout.addWidget(pushButton_Run, 0, 0)
           self.setLayout(layout)
    
        def pushbutton_run_clicked(self):
    
            fig, ax = setup_plot()                        
            dataq(fig, ax)
    
    def setup_plot():
    
        fig, ax = plt.subplots()
        fig.canvas.manager.show()
    
        return fig, ax
    
    def dataq(fig, ax):
        for i in range(200):
            ax.plot(np.random.rand(1), np.random.rand(1), 'o')
            fig.canvas.draw()
            fig.canvas.flush_events()
    
    if __name__ == '__main__':
    
        app = QtGui.QApplication(sys.argv)    
        mywindow = MyWindowClass()
        mywindow.show()      
        sys.exit(app.exec_())
    

    enter image description here

    Update - Second Example:

    This is another way to setup your artists that may allow you to more easily used them within multiple functions and can make their manipulation more convenient.

    from PyQt4 import QtGui
    import sys
    import numpy as np
    
    import matplotlib as mpl
    mpl.use('Qt4Agg')
    import matplotlib.pyplot as plt
    
    class MyWindowClass(QtGui.QWidget):
        def __init__(self, parent=None):
           super(MyWindowClass, self).__init__(parent)
    
           #---- generate some data ----
    
           self.x = np.random.rand(50)
           self.y = np.random.rand(50)
    
           #---- create a layout ----
    
           pushButton_Run = QtGui.QPushButton('Run')
           pushButton_Run.clicked.connect(self.pushbutton_run_clicked)
    
           pushButton_Clear = QtGui.QPushButton('Clear')
           pushButton_Clear.clicked.connect(self.clear_plot)
    
           layout = QtGui.QGridLayout()
           layout.addWidget(pushButton_Run, 0, 0)
           layout.addWidget(pushButton_Clear, 1, 0)
           self.setLayout(layout)
    
           #---- init artists ----
    
           self.fig, self.ax = plt.subplots()
           self.fig.canvas.manager.show()
    
        def clear_plot(self):
            self.ax.cla()
            self.fig.canvas.draw()
            self.fig.canvas.flush_events()
    
        def pushbutton_run_clicked(self):
            self.setEnabled(False)
            self.ax.cla()   
            dataq(self.fig, self.ax, self.x, self.y)
            dataq2(self.fig, self.ax, self.x, self.y)
            self.setEnabled(True)
    
    def dataq(fig, ax, x, y):            
        for i in range(len(x)):
            ax.plot(x[i], y[i], 'o')
            fig.canvas.draw()
            fig.canvas.flush_events()
    
    def dataq2(fig, ax, x, y):
        for i in range(len(x)-1):
            ax.plot(x[i:i+2], y[i:i+2], '-')
            fig.canvas.draw()
            fig.canvas.flush_events()
    
    if __name__ == '__main__':
    
        app = QtGui.QApplication(sys.argv)    
        mywindow = MyWindowClass()
        mywindow.show()      
        sys.exit(app.exec_())