简体   繁体   中英

How can I modify this code so that I am not replotting my graph everytime, matplotlib

I am grabbing data from a device and wanting to plot the voltage of it, with the plot being embedded into the UI . I used the example here: http://matplotlib.org/examples/user_interfaces/embedding_in_qt4.html

This example works fine but when I add 2 or more graphs, the whole UI becomes really slow (using a RPi3) and CPU usage is really high. I realise this is probably because the graph is constantly being cleared and replotted.

My code looks like this:

class MyMplCanvas(FigureCanvas):
    def __init__(self, parent=None, width=5, height=2, dpi=100):
        fig = Figure(figsize=(width, height), dpi=dpi)
        self.axes = fig.add_subplot(111)

        self.compute_initial_figure()

        FigureCanvas.__init__(self, fig)
        self.setParent(parent)

        FigureCanvas.setSizePolicy(self,QtGui.QSizePolicy.Expanding,QtGui.QSizePolicy.Expanding)
        FigureCanvas.updateGeometry(self)

    def compute_initial_figure(self):
        pass

class MyDynamicMplCanvas(MyMplCanvas):

    def __init__(self, *args, **kwargs):
        MyMplCanvas.__init__(self, *args, **kwargs)

    def compute_initial_figure(self):
        self.axes.cla()

    def update_figure(self,voltage):
        self.axes.cla()
        self.axes.plot(np.linspace(0,len(voltage)-1,num = len(voltage)), voltage, 'b')  
        self.axes.set_xlabel("Time")
        self.draw()


class worker_thread(QThread):
... #worker thread stuff here

class Monitor(QtGui.QMainWindow):

    def __init__(self, parent = None):
        QtGui.QMainWindow.__init__(self, parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.ui.exit_button.clicked.connect(exit)
        self.ui.go_button.clicked.connect(self.start_monitoring)
        self.ui.print_button.clicked.connect(self.test_print)

        self.ac_graph = QtGui.QWidget(self)
        self.ac_1_graph = MyDynamicMplCanvas(self.ac_graph,width = 10, height =3 , dpi = 60)
        self.ui.mplvl.addWidget(self.ac_1_graph)
        self.ac_1_graph.axes.set_xlabel("Time")

        self.dc_graph = QtGui.QWidget(self)
        self.dc_2_graph = MyDynamicMplCanvas(self.dc_graph,width = 10, height =3 , dpi = 60)
        self.ui.mplvl_2.addWidget(self.dc_2_graph)      

        self.ac1_voltage_values = []
        self.ac1_current_values = []
        self.dc2_voltage_values = []

    def start_monitoring(self): 

        self.worker_thread = worker_thread()

        self.connect(self.worker_thread,SIGNAL('grid_done'),      self.update_ac_dc_info)

    def update_plot_values(self, y_value, y_list):
        y_list.append(y_value)
        if (len(y_list) == 61):
            del y_list[0]
        return y_list

    def update_ac_dc_info(self,grid_info):
        self.ac1_voltage_values = self.update_plot_values((grid_info['ac1_voltage']/10),self.ac1_voltage_values)
        self.ac_1_graph.update_figure(self.ac1_voltage_values)

Essentially, when data comes back from my device, I emit a signal from the worker_thread which triggers my UI to update in the main thread, as well as the plot. At this point, how do I just get matplotlib to just accept the new point that came in WITHOUT replotting the whole thing? Many of the examples I've read utilise pyplot which I can't use because I need to embed this in an existing UI.

Instead of clearing the axes everytime there is new data, you can just redraw the line of points. This would save some time.

class MyDynamicMplCanvas(MyMplCanvas):

    def __init__(self, *args, **kwargs):
        MyMplCanvas.__init__(self, *args, **kwargs)

    def compute_initial_figure(self):
        # empty plot
        self.line,  = self.axes.plot([],[], color="b")
        self.axes.set_xlabel("Time") # xlabel will not change over time

    def update_figure(self,voltage):

        self.line.set_data(np.linspace(0,len(voltage)-1,num = len(voltage)), voltage)  
        # now you need to take care of the axis limits yourself
        self.axes.set_xlim([0,len(voltage])
        self.draw()

Updating only the last new point is a bit tricky, since the plot consist of a single Line2D object. You could opt for a point plot, where you can plot a single new point every time new data arrives.

ImportanceOfBeingErnest's method works just fine. To rescale the axis based on the data:

self.axes.relim()
self.axes.autoscale_view(True,True,True)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM