I've got some issues about blitting a matplotlib plot, which is itself embedded in a Tkinter GUI - the whole program will eventually run on a Raspberry Pi. The question involves various levels, this is my first question, so sorry in advance for any unclarities.
In few words, what I'm doing is this: I'm working on a Tk GUI to read out a number of sensors simultaneously and I'd like to have some real-time updating of the sensor data on said GUI. I'd like to have each measurable quantity on a separate frame, which is why I decided to set up a class for each Sensor. One of the sensors is a Flow Sensor, which is read out and plotted as follows:
import Tkinter as Tk
import numpy as np
import matplotlib.pyplot as plt
from datetime import datetime
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from Backend import Backend #self written data acquisition handler
#global variables
t0 = datetime.now() #start time of program
#Returns time difference in seconds
def time_difference(t1, t2):
delta = t2-t1
delta = delta.total_seconds()
return delta
# Define Class for Flow data display
class FlowFig():
def __init__(self, master): #master:Parent frame for plot
#Initialize plot data
self.t = []
self.Flow = []
#Initialize plot for FlowFig
self.fig = plt.figure(figsize=(4,4))
self.ax = self.fig.add_subplot(111)
self.ax.set_title("Flow Control Monitor")
self.ax.set_xlabel("Time")
self.ax.set_ylabel("Flow")
self.ax.axis([0,100,0,5])
self.line = self.ax.plot(self.t, self.Flow, '-')
#Set up canvas
self.canvas = FigureCanvasTkAgg(self.fig, master = master)
self.canvas.show()
self.background = self.canvas.copy_from_bbox(self.ax.bbox)
self.ax.grid(True)
# Initialize handler for data aqcuisition
self.Data= Backend()
self.Data.initialize()
#update figure
def update(self):
# get new data values
self.t.append(time_difference(t0, datetime.now()))
Flow,_,_ = self.Data.get_adc_val(1)
self.Flow.append(Flow)
# shorten data vector if too long
if len(self.t) > 200:
del self.t[0]
del self.Flow[0]
#adjust xlims, add new data to plot
self.ax.set_xlim([np.min(self.t), np.max(self.t)])
self.line[0].set_data(self.t, self.Flow)
#blit new data into old frame
self.canvas.restore_region(self.background)
self.ax.draw_artist(self.line[0])
self.canvas.blit(self.ax.bbox)
root.after(25, Flowmonitor.Flowdata.update) #Recursive update
#Flow Frame of GUI
class FlowPage(Tk.Frame):
def __init__(self, parent, controller):
Tk.Frame.__init__(self,parent)
self.parent = parent
self.FlowPlot = FlowFig(self)
self.FlowPlot.canvas.get_tk_widget().grid(row=0, column=0, rowspan=9, columnspan=9)
# Mainloop
root= Tk.Tk()
root.rowconfigure(0, weight=1)
root.columnconfigure(0, weight=1)
Flowmonitor = FlowPage(root, root)
Flowmonitor.grid(row =0, column=0, rowspan =10, columnspan=10)
Flowmonitor.rowconfigure(0, weight=1)
Flowmonitor.columnconfigure(0, weight=1)
root.after(25, Flowmonitor.FlowPlot.update)
root.mainloop()
What troubles me with my resulting images is this:
When I use the statement copy_from_bbox(self.ax.bbox)
I get a graph like this
Obviously, the size of the blitted background doesn't fit the image into which it is blitted. So, I tried to blit the figure's bbox instead (copy_from_bbox(self.fig.bbox)
) and got this
Versions of these shifts happen with all combinations of fig.bbox
and ax.bbox
,
So here come my actual questions:
Can anybody help me find the bug in my above code which causes the missmatch? I'm aware that it is probably very simple yet subtle misstake. It seems very much related to this thread, yet I can't quite glue it together, using
bbox.expanded()
in the argument ofcopy_from_bbox()
doesn't do much of a difference.blit()
vs..draw()
has already been discussed here. But since speed is of the essence for my application I think I have to blit. Redrawing the plot gives me framerates of fps=10, whereas blitting runs almost 10x faster. In any case - is there a way to update one of the axes (e.g. time axis) while using blit? (The answer to this is probably closely related to question No.1 )Lastly, a rather basic question about my application alltogether: Since my sensordata is currently fetched within an infinite, recursive loop - is it possible to run several of such loops in parallel or should I rather go for threading instead, making my code considerably more complex? What are the risks of running infinite, recursive loops? Or should these be avoided in general?
After days of blitting back and forth I'm rather confused about the possibilities regarding ax/fig blitting, so any help regarding the matter is much, much appreciated^^ Please let me know if you need more info about anything, I hope I could illustrate my problem well.
Thanks a lot for your help!