Search code examples
pythonmatplotlibpyqtpyqt4qthread

draw matplotlib graph with QThread issue


Beside main class Window, I have defined new class (test_graph_Data) which will draw graph with matplotlib. In order to avoid problem with alarm "QPixmap: It is not safe to use pixmaps outside the GUI thread", I have defined new class test_graph_Data, which will emit signal to main class for drawing the graph, but there are some problems to emit the signals... Look at the code below:

When I run the code, I got the warning: AttributeError: "PyQt4.QtCore.pyqtSignal" object has no attribute 'connect'!

import sys, time
from PyQt4 import QtGui, QtCore
import matplotlib.pyplot as plt

class Window(QtGui.QMainWindow):

     def __init__(self):
        super(Window, self).__init__()
        self.setGeometry(50, 50, 120, 90)
        self.home()

    def home(self):
        self.test = QtGui.QPushButton("Test", self)
        self.test.clicked.connect(self.test1_function)
        self.test.move(10,20)
        self.show()

    def test1_function(self):
        self.get_thread = test_graph_Data()
        self.connect(self.get_thread, QtCore.SIGNAL("finished()"),self.done_test1_function)
        self.get_thread.start()

    def done_test1_function(self):
        print 'Graph is displayed!'

class test_graph_Data(QtCore.QThread) :
    def __init__(self):
        QtCore.QThread.__init__(self)

    def __del__(self):
        self.wait()

    def graph_data(self):
        start = time.time()
        b = [1,0,1,0,1,0,1,1,1,1,0,1,0,1]
        plt.ion()
        fig1 = plt.figure()
        ax1 = fig1.add_subplot(111)
        ax1.plot(b, 'b')
        end = time.time()
        print end - start

    def run(self):
        top_post = self.graph_data()

def main():
    app = QtGui.QApplication(sys.argv)
    GUI = Window()
    GUI.show()
    sys.exit(app.exec_())


if __name__ == '__main__' :
    main()

Solution

  • You must use the thread to manipulate the data, not to graph. In this example I have made your list rotate. You must create a signal that communicates to the main thread that the data is ready to be updated. In the GUI you must connect that signal with a function that updates the graph.

    import sys
    import time
    import matplotlib as mpl
    mpl.use("QT4Agg")
    import matplotlib.pyplot as plt
    from PyQt4 import QtGui, QtCore
    
    
    class test_graph_Data(QtCore.QThread):
        updated = QtCore.pyqtSignal(list)
        running = True
    
        def __init__(self, parent=None):
            QtCore.QThread.__init__(self, parent)
            self.b = [1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1]
    
        def run(self):
            while self.running:
                # rotate list
                self.b = self.b[1:] + [self.b[0]]
                self.updated.emit(self.b)
                time.sleep(0.1)
    
    
    class Window(QtGui.QMainWindow):
        def __init__(self):
            super(Window, self).__init__()
            self.setGeometry(50, 50, 120, 90)
            self.home()
    
        def home(self):
            self.test = QtGui.QPushButton("Test", self)
            self.test.clicked.connect(self.test1_function)
            self.test.move(10, 20)
            self.show()
    
        def test1_function(self):
            self.get_thread = test_graph_Data(self)
            self.get_thread.finished.connect(self.done_test1_function)
            self.get_thread.updated.connect(self.graph_data)
            plt.ion()
            fig1 = plt.figure()
            self.ax1 = fig1.add_subplot(111)
            self.get_thread.start()
    
        def done_test1_function(self):
            print('Graph is displayed!')
    
        def graph_data(self, data):
            self.ax1.clear()
            self.ax1.plot(data, 'b')
    
        def closeEvent(self, event):
            self.get_thread.running = False
            self.get_thread.wait()
    
    
    def main():
        app = QtGui.QApplication(sys.argv)
        GUI = Window()
        GUI.show()
        sys.exit(app.exec_())
    
    
    if __name__ == '__main__':
        main()