понедельник, 30 мая 2011 г.

Animated plot with matplotlib

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)

Комментариев нет:

Отправить комментарий

Thanks for your posting!