I have a little app that allows me to change an input value with a tKinter scale widget and see how a graph reacts to different changes in inputs. Every time I move the scale, it's bound to an event that redoes the calculations for a list and replots. It's kind of slow.
Now, I'm replotting the entire thing, but it's stacking one axis on top of the other, hundreds after a few minutes of use.
deltaPlot = Figure(figsize=(4,3.5), dpi=75, frameon=False)
c = deltaPlot.add_subplot(111)
c.set_title('Delta')
deltaDataPlot = FigureCanvasTkAgg(deltaPlot, master=master)
deltaDataPlot.get_tk_widget().grid(row=0,rowspan=2)
and the main loop runs
c.cla()
c.plot(timeSpread,tdeltas,'g-')
deltaDataPlot.show()
It's clearing the initial plot, but like I said the axes are stacking (because it's redrawing one each time, corresponding to the slightly altered data points). Anyone know a fix?
To improve speed there are a couple of things you could do:
Either Run the remove method on the line produced by plot:
# inside the loop
line, = c.plot(timeSpread,tdeltas,'g-')
deltaDataPlot.show()
...
line.remove()
Or Re-use the line, updating its coordinates appropriately:
# outside the loop
line, = c.plot(timeSpread,tdeltas,'g-')
# inside the loop
deltaDataPlot.show()
line.set_data(timeSpread,tdeltas)
The documentation of Line2d can be found here.
You might also like to read the cookbook article on animation.
HTH
Related
I have a matplotlib Axes object self.a defined on a matplotlib canvas object self.fold_canvas. During its construction, at some point I plot a bunch of transparent boxes on the plot, via
self.a.axvspan(bins[lower],bins[upper],color=palette[i],alpha=0.3, gid='foldstate')
Later, I want to get rid of them. I do this using the loop:
for p in self.a.patches:
if p.get_gid() == 'foldstate':
p.remove()
self.fold_canvas.draw()
For some reason, this loop removes all but one of the axvspan instances. If I then do it again, ie
for p in self.a.patches:
if p.get_gid() == 'foldstate':
p.remove()
for p in self.a.patches:
if p.get_gid() == 'foldstate':
p.remove()
self.fold_canvas.draw()
it works as intended, and all of the axvspans are removed.
Why do I need to do the loop twice to remove all of them?
EDIT: it appears that labelling them all with unique gid fixes the problem, so it may relate to an internal issue when multiple patches share the same gid.
Since this hasn't been touched: there seems to be an issue internal to matplotlib where gid needs to be unique for every labelled item. It seems to cause some silent failures if you reuse a gid anywhere.
I have a python code that plots several figures. The relevant parts are
import matplotlib.pyplot as plt
plt.ion()
for p in range(np):
for g in range(ng):
figs = []
for n in range(ns):
figs.append(plt.figure())
gsg = # object I am working with
cg = plt.contourf(gsg.x, gsg.y, gsg.u[:,:])
cb = plt.colorbar()
figs[n].suptitle(title)
print title, gsg.u[ex_index,ex_index,:]
go_ahead = raw_input()
For certain cases, I can't run this straight through and just look at all the figures because too many figures are produced and python starts automatically closing several of them.
So what I want is for that inner loop to plot ns figures, then I pause the execution (hence the go_ahead input), look at the displayed figures, close them on the screen, and let the code go to the next batch. I can't figure out how to make this work.
The closest I have come is by putting plt.show(block=True) command after figs[n].suptitle(). This does result in the display of a figure during run time, but I can only see one figure at a time. plt.show() and plt.show(block=False) does not result in any display at all, until the end of execution, which is what I am trying to avoid.
Any ideas?
I am trying to use matplotlib's animation feature to animate multiple objects. By default, the animation feature erases old instances of the object each time it calls your animation function and you change the position of the line or patch or whatever. (Of course, this is the whole point of animation). But I have 2 different objects to be animated: a line that sweeps a polar plot in azimuth, and scatter markers. I don't want the markers to disappear. I want them to persist throughout the animation once they've been drawn.
I've tried a couple different things like appending the 'marker' scatter points to a list and returning it, but I'm having trouble with the return of the animate function. Here is the basic code without trying to append to a list. I trimmed down a lot of the figure and axis setup. As this code sits, the line sweeps around the plot and when it reaches a point where a marker is, the marker flashes then disappears.
fig = plt.figure(1)
ax = fig.add_subplot(111,projection='polar')
ax.set_theta_zero_location('N',offset=0)
ax.set_theta_direction(-1)
line, = ax.plot((0,0),(0,0))
list = []
def animate(i):
line.set_data((0,line_az_time[i][0]),(0,300))
if(line_az_time[i][1] in marker_time):
marker = plt.scatter(marker_az[0],marker_range[0])
list.append(marker)
del marker_az[0]
del marker_range[0]
return line, list # wrong return type?
return line, list # how to handle empty list before 1st marker appended?
def init():
line.set_data((0,0),(0,300))
return line,
ani = animation.FuncAnimation(fig,animate,np.arange(len(line_az_time)),
blit=True,interval=0,repeat=True,init_func=init)
plt.show()
'line_az_time' is a nested list of the format [[angle1, time1],[angle2,time2]...]
Basically it saves an angle and the time that angle occurs. the 'marker_xxx' variables are the angles, ranges, and times of target detections. When the line is getting animated, it is checking if a detection is occuring at that time and angle. The times and detections are always sequential, so I just delete them once they've been plotted.
Basically all I need is keeping the 'marker' scatter points to persist throughout the animation. As stated before, I tried appending them to a list and returning the list instead of 'marker' itself. That way, they all get plotted once they've occurred. But that didn't work. I think I am confused by what type of variable to pack them into and how to write the 'return' lines once I append them to a variable.
Or if there is another way, I'd be open to that as well.
I have a list of 2D arrays that I plot them using a slider from matplotlib.
I added some control buttons to my plot, so I want to click a play button, and the plot changes with some time interval.
Here's the function that is called by the button:
def play(mouse_event):
for x in range(len(listOfMoments)):
image.set_data(listOfMoments[x])
time.sleep(0.5)
print(x)
The print(x) shows that x is increasing normally, however, it plots only the last array of the list, after the increment finishes.
My question is: How can I make it plot the array one by one, as it expected to be?
I should mention also that I tried the while loop and got the same results.
You need to tell the backend to update the figure after changing the data each time through the loop:
fig.canvas.draw()
Here's the documentation reference.
If you don't have a reference to the Figure object, you can probably retrieve it with fig = plt.gcf().
I'm attempting to do real-time plotting of data in matplotlib, in a "production" application that uses wxPython. I had been using Chaco for this purpose, but I'm trying to avoid Chaco in the future for many reasons, one of which is that since it's not well-documented I often must spend a long time reading the Chaco source code when I want to add even the smallest feature to one of my plots. One aspect where Chaco wins out over matplotlib is in speed, so I'm exploring ways to get acceptable performance from matplotlib.
One technique I've seen widely used for fast plots in matplotlib is to set animated to True for elements of the plot which you wish to update often, then draw the background (axes, tick marks, etc.) only once, and use the canvas.copy_from_bbox() method to save the background. Then, when drawing a new foreground (the plot trace, etc.), you use canvas.restore_region() to simply copy the pre-rendered background to the screen, then draw the new foreground with axis.draw_artist() and canvas.blit() it to the screen.
I wrote up a fairly simple example that embeds a FigureCanvasWxAgg in a wxPython Frame and tries to display a single trace of random data at 45 FPS. When the program is running with the Frame at the default size (hard-coded in my source), it achieves ~13-14 frames per second on my machine. When I maximize the window, the refresh drops to around 5.5 FPS. I don't think this will be fast enough for my application, especially once I start adding additional elements to be rendered in real-time.
My code is posted here: basic_fastplot.py
I wondered if this could be made faster, so I profiled the code and found that by far the largest consumers of processing time are the calls to canvas.blit() at lines 99 and 109. I dug a little further, instrumenting the matplotlib code itself to find that most of this time is spent in a specific call to MemoryDC.SelectObject(). There are several calls to SelectObject in surrounding code, but only the one marked below takes any appreciable amount of time.
From the matplotlib source, backend_wxagg.py:
class FigureCanvasWxAgg(FigureCanvasAgg, FigureCanvasWx):
# ...
def blit(self, bbox=None):
"""
Transfer the region of the agg buffer defined by bbox to the display.
If bbox is None, the entire buffer is transferred.
"""
if bbox is None:
self.bitmap = _convert_agg_to_wx_bitmap(self.get_renderer(), None)
self.gui_repaint()
return
l, b, w, h = bbox.bounds
r = l + w
t = b + h
x = int(l)
y = int(self.bitmap.GetHeight() - t)
srcBmp = _convert_agg_to_wx_bitmap(self.get_renderer(), None)
srcDC = wx.MemoryDC()
srcDC.SelectObject(srcBmp) # <<<< Most time is spent here, 30milliseconds or more!
destDC = wx.MemoryDC()
destDC.SelectObject(self.bitmap)
destDC.BeginDrawing()
destDC.Blit(x, y, int(w), int(h), srcDC, x, y)
destDC.EndDrawing()
destDC.SelectObject(wx.NullBitmap)
srcDC.SelectObject(wx.NullBitmap)
self.gui_repaint()
My questions:
What could SelectObject() be doing that is taking so long? I had sort of assumed it would just be setting up pointers, etc., not doing much copying or computation.
Is there any way I might be able to speed this up (to get maybe 10 FPS at full-screen)?