This is the components of auto-scrolled plots with matplotlib:
class Plot:
"""Base attributes of an any plot"""
def __init__(self,
fig=None, # matplotlib Figure
canvas=None, # matplotlib Canvas
tkw=None, # Tk widget
title="", #
xdata=[], # x data
ydata=[], # y data
xmin=None, # left xlim:datetime (chages in scroll with set_xlim)
xmax=None, # right xlim:datetime (chages in scroll with set_xlim)
tdelta=None, # step (period of polling of sensors, etc.):timedelta
ylabel="", # y label
lnc="red", # line color
lnpt="-", # line point type
lnw=1, # line width
intc="#707070", # internal rect. color
outc="#9E9E9E", # outter rect. color
ylimpad=10, # padding around y axis limit (ylim is +/- ylimpad)
):
self.fig = fig
self.canvas = canvas
self.tkw = tkw
self.title = title
self.xdata = xdata
self.ydata = ydata
self.xmin = xmin
self.xmax = xmax
self.tdelta = tdelta
self.xband = self.xmax - self.xmin # width of axe 0x (timedelta)
self.ylabel = ylabel
self.lnc = lnc
self.lnpt = lnpt
self.lnw = lnw
self.intc = intc
self.outc = outc
self.ylimpad = ylimpad
self.ymin = 0
self.ymax = 0
class AnimPlot(Frame):
"""One-line plot in own frame"""
# TODO: blit not used
def __init__(self, master, app, blit=False, title="", npoints=60, ylabel="", lnc="red", lnpt="-", lnw=1, intc="#707070", outc="#9E9E9E", ylimpad=10, period=1, *a, **kw):
Frame.__init__(self, master, *a, **kw)
self.app = app
xmin = self.app.begin
xmax = xmin + timedelta(seconds=npoints*period) # period is secs
tdelta = timedelta(seconds=period)
self.p = Plot(title=title, ylabel=ylabel, lnc=lnc, lnpt=lnpt, lnw=lnw, intc=intc, outc=outc, xdata=deque(maxlen=npoints), ydata=deque(maxlen=npoints), xmin=xmin, xmax=xmax, tdelta=tdelta, ylimpad=ylimpad)
self.blit = blit # use double-buffering?
self._create_matplot()
self.p.tkw.pack()
def _create_matplot(self):
"""Creates of matplotlib plot and changes
self.p"""
# calculate plot size in inches; dpi is the pixels count per inch
dpi = 75.
figh = self.winfo_screenheight()
figh = figh/dpi - 1 # 1 for menu bar
figh = figh/2 # two plots
figw = figh*2.5 # aspect
fig = Figure(figsize=(figw, figh), dpi=dpi)
fig.patch.set_facecolor(self.p.outc)
ax = fig.add_subplot(111)
ax.patch.set_facecolor(self.p.intc)
ax.set_title(self.p.title)
ax.grid(True)
ax.set_ylabel(self.p.ylabel)
ax.plot_date([], [], self.p.lnpt, color=self.p.lnc, lw=self.p.lnw)
ax.set_xlim(mdates.date2num(self.p.xmin), mdates.date2num(self.p.xmax))
ax.xaxis.set_major_formatter(mdates.DateFormatter('%H:%M:%S', utils.tzinfo()))
ax.fmt_xdata = mdates.DateFormatter('%H:%M:%S', utils.tzinfo())
canvas = FigureCanvas(fig, master=self)
tkw = canvas.get_tk_widget()
tkw.config(relief=FLAT, bd=0, takefocus=0, highlightthickness=0)
self.p.fig = fig
self.p.canvas = canvas
self.p.tkw = tkw
def _redraw(self):
"""Redraws plot with data from self.p automatically after append of the new data"""
ax = self.p.fig.axes[0]
line = ax.lines[0]
line.set_xdata(self.p.xdata)
line.set_ydata(self.p.ydata)
x1 = self.p.xdata[-1]
tm0 = mdates.num2date(x1) - self.p.xband + self.p.tdelta
x0 = mdates.date2num(tm0)
ax.set_xlim(x0, x1)
y0 = 0
y1 = self.p.ymax
ax.set_ylim(y0 - self.p.ylimpad, y1 + self.p.ylimpad)
self.p.canvas.draw()
def appendy(self, y):
tm = utils.now()
tm = mdates.date2num(tm)
self.p.xdata.append(tm)
self.p.ymax = max(self.p.ymax, y)
self.p.ydata.append(y)
self._redraw()
Here is an example of usage:
aplt1 = AnimPlot(self.pltfr, app=self.app, name="tpm200plot1", lnc="red", lnw=2, title=u"Sensor PV1", ylabel=u"C", period=3)
aplt1.pack(side=TOP, expand=YES, fill=Y)
# ...
def onpolling():
val = read_sensor()
aplt1.appendy(pv1)