Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Progress Line in Matplotlib Graphs

Objective: To plot a graph(x,y) and move a vertical line over the graph w.r.t to timer.

I started implementing this using matplotlib. Its possible to implement this using draw() feature of matplotlib , but it consumes cpu as it redraws every time and doesn't allow me to interact with the graph. so i decided to use animation feature of matplotlib. in Future i would aslo like to pause the moving line. So i cant use matplotlib.animation.FuncAnimatin

Problem: I use canvas.copy_from_bbox(ax.bbox), ax.draw_artist(line),canvas.blit(ax.bbox). But, I am not able to store the graph in the background and move a line over it. When i try to store, it overwrites in a quite weird way.

This is the code i have built. Could any one please help me ? Thanks in advance.

import sys
import matplotlib.pyplot as p
import time
fig=p.figure();
ax = fig.add_subplot(1,1,1)

y=[];x=[];y1=[0,1000];x1=[0,0]
y=numpy.random.randn(1000,1)*100
x=numpy.arange(0,1000)
line1, = ax.plot(x,y,color='black');
ax.set_ylim(0, 1000);
line, = ax.plot(x1,y1,color='r',alpha=1,animated=True); # this is the line which i wanted to move over the graph w.r.t to time. ( i can also use axvline , but i guess its the same).
canvas = ax.figure.canvas
canvas.draw()
background = canvas.copy_from_bbox(ax.bbox); #my problem is here
starttime=time.time();
mytimer=0;
mytimer_ref=0;
def update(canvas,line,ax):
    canvas.restore_region(background) #my problem is here 
    t=time.time()-starttime;
    mytimer=t+mytimer_ref;
    x1=[mytimer,mytimer];
    line.set_xdata(x1);
    ax.draw_artist(line)
    canvas.blit(ax.bbox) #my problem is here

def onclick(event):
    global starttime
    starttime=time.time();
    global mytimer_ref;
    mytimer_ref=event.xdata;
    print "starttime",starttime;


cid=line1.figure.canvas.mpl_connect('button_press_event',onclick); # when i click the mouse over a point, line goes to that point and start moving from there. 
timer=fig.canvas.new_timer(interval=100);
args=[canvas,line,ax];
timer.add_callback(update,*args); # every 100ms it calls update function
timer.start();
p.show();
like image 788
Thothadri Rajesh Avatar asked Aug 13 '12 21:08

Thothadri Rajesh


1 Answers

So it looks like the "quite weird way" you are referring to is essentially that the wrong bbox has been captured with your background = canvas.copy_from_bbox(ax.bbox). I believe this is a known problem with most of the backends where the addition of toolbars etc. affect the position of the bbox for blitting.

Essentially, if you can capture the background after the window has popped up, then everything should be working for you. This can be done in a number of ways, in your case the simplest would be to replace your canvas.draw() command with a plt.show(block=False), which will bring up the window, without making it a blocking command.

As a slight addition, I'm sure you are aware that semicolons are not necessary in python code, but while I was debugging, I tidied up your code a little (didn't quite get to the end):

import sys
import matplotlib.pyplot as plt
import time
import numpy


fig = plt.figure()
ax = fig.add_subplot(111)


max_height = 100
n_pts = 100
y1 = [0, max_height]
x1 = [0, 0]
y = numpy.random.randn(n_pts) * max_height
x = numpy.arange(0, n_pts)

# draw the data
line1, = ax.plot(x, y, color='black')

# fix the limits of the plot
ax.set_ylim(0, max_height)
ax.set_xlim(0, n_pts)

# draw the plot so that we can capture the background and then use blitting
plt.show(block=False)

# get the canvas object
canvas = ax.figure.canvas
background = canvas.copy_from_bbox(ax.bbox)

# add the progress line.
# XXX consider using axvline
line, = ax.plot(x1, y1, color='r', animated=True) 


starttime=time.time()
mytimer=0
mytimer_ref=0

def update(canvas, line, ax):
    # revert the canvas to the state before any progress line was drawn
    canvas.restore_region(background)

    # compute the distance that the progress line has made (based on running time) 
    t = time.time() - starttime
    mytimer = t + mytimer_ref
    x1 = [mytimer,mytimer]
    # update the progress line with its new position
    line.set_xdata(x1)
    # draw the line, and blit the axes
    ax.draw_artist(line)
    canvas.blit(ax.bbox)

def onclick(event):
    global starttime
    starttime=time.time()
    global mytimer_ref
    mytimer_ref=event.xdata
    print "starttime",starttime


cid=line1.figure.canvas.mpl_connect('button_press_event',onclick) # when i click the mouse over a point, line goes to that point and start moving from there. 
timer=fig.canvas.new_timer(interval=100)
args=[canvas,line,ax]
timer.add_callback(update,*args) # every 100ms it calls update function
timer.start()
plt.show()

HTH

like image 57
pelson Avatar answered Sep 30 '22 19:09

pelson