Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Tkinter matplotlib canvas updates too slowly for real time data

I am working with a device that sends 100 readings per second and I want my GUI to have a plot of this data showing the last 300 points collected. However, I have found that:

  1. appending a new data point to the y-axis queue

  2. clearing the plot that exists already

  3. plotting the new data list

  4. redrawing the canvas

after each point takes nearly 0.2 - 0.4 seconds, which is incredibly slow.

This is the code I am using currently. A while loop keeps checking a queue, and once a new element is pushed to it, it calls update with the element as a parameter. Would anyone be able to suggest some improvements to the efficiency or alternatives to matplotlib?

class GraphFrame:
    def __init__(self,root,channel,index):
        self.root=root
        self.frame=tk.Frame(self.root)
        self.frame.pack(side=tk.LEFT)
     
        self.y = Queue(maxsize = 300)
        
        self.fig, self.axes = plt.subplots(1,1)
        self.axes.plot(list(self.y.queue))

        self.canvas = FigureCanvasTkAgg(self.fig, master=self.frame)
        self.canvas.draw()
        self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, padx=5, pady=5)

    def update(self, new_point):
        if self.y.full():
            self.y.get()
        self.y.put(new_point)
        self.fig.axes[0].clear()
        self.fig.axes[0].plot(list(self.y.queue))
        self.canvas.draw()

Edit: managed to solve the issue using blitting for matplotlib and replacing the Tkinter canvas with a label with the image of the Graph. Will try to upload code on github and link it.

like image 586
Mohamed Moustafa Avatar asked Jul 10 '20 16:07

Mohamed Moustafa


1 Answers

Instead of rebuilding your figure for each new point, I would modify the LineArtist you already have.

class GraphFrame:
    def __init__(self,root,channel,index):
        self.root=root
        self.frame=tk.Frame(self.root)
        self.frame.pack(side=tk.LEFT)
     
        self.y = Queue(maxsize = 300)
        
        self.fig, self.axes = plt.subplots(1,1)

        # capture the artist
        self.line, = self.axes.plot(list(self.y.queue))

        self.canvas = FigureCanvasTkAgg(self.fig, master=self.frame)
        self.canvas.draw()
        self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, padx=5, pady=5)

    def update(self, new_point):
        if self.y.full():
            self.y.get()
        self.y.put(new_point)
        
        # update the artist
        self.line.set_xdata(list(range(len(self.y))))
        self.line.set_ydata(list(self.y.queue))

like image 195
Paul H Avatar answered Nov 15 '22 18:11

Paul H