Matplotlib animate over an image - python

I'm trying to animate a bunch of constantly updating points over an image (imagine making a plotted dot move diagonally across some image). I've looked at the animate examples here: http://matplotlib.org/examples/animation/dynamic_image.html, but I'm not sure how to keep the same image while clearing out all previous dots, then redrawing them. Any ideas?

You do not need to clear the figure between every frame
#initial data
ln, = ax.plot(x,y)
#...some loop code
ln.set_xdata(new_x)
ln.set_ydata(new_y)
Can you show some code of what you have tried, it will make it easier to give a more concrete answer.
Also see: using matplotlib's quiver in a loop efficiently, Visualization of 3D-numpy-array frame by frame

Related

Rotate pie chart in interactive window without replotting

I am drawing a pie chart out of provided data, and this can potentially get out of hand as the length of the labels can be pretty long, and there can be a lot of them overlapping each other. Because of this, it is crucial to find a good startangle parameter to my pie chart drawing.
Conceptually, I want to use a mouse scroll event to rotate the whole pie chart by 5 degrees every time the user uses the scroll wheel. Rotating the wedges isn't too much trouble with their theta1 and theta2 properties, but repositioning the labels and autotexts is serious trouble because of alignment properties and the lining up with wedges. I also want to retain an interactive frame rate, so clearing the figure and redrawing is not an option.
Here is one such situation where this is useful. The labels are too big and rotating the whole pie chart would help reposition them in sight. Of course in this case it would be enough to resize the chart instead of rotating but you get my point.
Is there a way to achieve this that does not imply rewriting the entire label and autotext positioning code for my own use?
In particular, I'm wondering if it wouldn't be possible to do something that is conceptually equivalent to making the same pyplot.pie call as before, only with a different startangle. Alternatively, maybe Text objects have methods I can use for positioning them around the newly rotated wedges that spare me working with just positions and sizes.

Matplotlib Glitches After Window Resize using Blitting

I am trying to use blitting with matplotlib to improve the performance of my plot, but am running into glitches after resizing the matplotlib window.
I have a matplotlib window showing 8 different lines plotted, along with a table of the values from the plot. The table and plot are updated every ~0.7 seconds.
Here is my main loop:
canvas.draw()
background = canvas.copy_from_bbox(ax.bbox)
while True
canvas.restore_region(background)
[update data for lines, table]
for line in lines:
ax.draw_artist(line)
canvas.blit(ax.bbox)
for line in lines:
line.set_animated(False)
canvas.draw_idle()
canvas.flush_events()
for line in lines:
line.set_animated(True)
time.sleep(0.7)
This updates as I expect it to, and everything looks OK.
But then when I resize the plot window (by clicking and dragging the window edge) I start to see artifacts left over from apparently as the resize was occurring (it appears for maybe 0.1 second, so I can't attach a screenshot). Basically I see what appears to be an old plot and the border of my table for a very short time on each iteration through my main loop, then the plot and table display fine.
What I've Tried:
I registered a callback for 'resize_event' and in that case do:
canvas.restore_region(firstBackground)
canvas.draw_idle()
canvas.flush_events()
Then on the next iteration through the main loop behave like normal. My callback is running, but this doesn't seem to help the problem at all.
So I think I need to do a transform on the background I saved before any data was plotted and call restore_region with the transformed background, but I am not sure if I am missing something simple.
EDIT:
Some more information. The plot I am trying to blit has two axes in the same area(one was created by calling twinx() on the other axis)
So far I have tried:
Calling set_visible(False) on all lines, then drawing and getting a new background image. This shows the same glitch on resize.
Calling cla() on the two axes that overlap. This also shows the same glitch after resizing.

How to animate a bar plot

I want to make an animated barchart in Python and save this animation in mp4 format. My problem is that the frames in the saved video overlay, although I use "blit=True" to tell the animation that only the things that change from frame to frame are drawn. Surprisingly, this problem does not occur in the built-in preview of Python.
Here is a minimal that reflects my situation:
import matplotlib.pyplot as plt
from matplotlib import animation
def barlist(n): #That's the list of bars I want to display
C=[]
for k in range(1,6):
C.append(1/float(n*k))
return C
fig=plt.figure()
n=100 #Number of frames
def animate(i):
x=range(1,6)
y=barlist(i+1)
return plt.bar(x,y)
anim=animation.FuncAnimation(fig,animate,repeat=False,blit=True,frames=n,
interval=50)
anim.save('barchart_animated_'+str(n)+'.mp4')
plt.show()
I must admit that I'm not pretty sure what I should do to remove this flaw. The only example I know of where the bars do not overlay in the frames is here (more exactly, I'm referring to the code of the first answer of the following link):
Dynamically updating a bar plot in matplotlib
It seems that I somehow have to tell the animation how it should set the height of each bar at each frame with the set_height-method. But as I said, I don't really know what's wrong in the above example. Thanks for any help!
Martin
The problem you have here is that you create a new barplot in every iteration of the animation. They will one by one be added to the plot, but since their height is shrinking over time, it may look as though only the first bar is present.
There are two ways to overcome this. First option is to clear the axes before plotting a new bar plot. This however will rescale the axis limits, which should then be constantly set to the same value.
The other option is to manipulate the one and only bar plot in the axes and adapt it's height for every frame. This is shown in the code below.
import matplotlib.pyplot as plt
from matplotlib import animation
def barlist(n):
return [1/float(n*k) for k in range(1,6)]
fig=plt.figure()
n=100 #Number of frames
x=range(1,6)
barcollection = plt.bar(x,barlist(1))
def animate(i):
y=barlist(i+1)
for i, b in enumerate(barcollection):
b.set_height(y[i])
anim=animation.FuncAnimation(fig,animate,repeat=False,blit=False,frames=n,
interval=100)
anim.save('mymovie.mp4',writer=animation.FFMpegWriter(fps=10))
plt.show()
Answers to the questions from the comments:
Blotting is a technique where all the parts of the figure which do not change are stored as a background. Then for each animated frame, only the changing parts are redrawn. This avoids the background to be redrawn from scratch and thus allows for much faster animations. Blitting will only affect the on-screen animation, because saving the animation to a file is not performed in real-time (and doesn't need to anyways).
Using blit=False here allows to make the code more simple because we do not need to care about the differences between the animation on screen and the one saved - they are just the same.
The enumerate function yields both the index as well as the object from the enumerated sequence. I did use it here, because it is a convenient way to obtain both in the same loop. It is not at all important here, you could alternatively do something like
for i in range(len(barcollection)):
barcollection[i].set_height(y[i])

python matplotlib blit to axes or sides of the figure?

I'm trying to refresh some plots that I have within a gui everytime I go once through a fitting procedure. Also, these plots are within a framw which can be resized, so the axes and labels etc need to be redrawn after the resizing. So was wondering if anyone knew how to update the sides of a figure using something like plot.figure.canvas.copy_from_bbox and blit. This appears to only copy and blit the background of the graphing area (where the lines are being drawn) and not to the sides of the graph or figure (where the labels and ticks are). I have been trying to get my graphs to update by trial and error and reading mpl documentation, but so far my code has jst become horrendously complex with things like self.this_plot.canvas_of_plot..etc.etc.. .plot.figure.canvas.copy_from_bbox... which is probably far too convoluted.
I know that my language might be a little off but I've been trying to read through the matplotlb documentation and the differences between Figure, canvas, graph, plot, figure.Figure, etc. are starting to evade me. So my first and foremost question would be:
1 - How do you update the ticks and labels around a matplotlib plot.
and secondly, since I would like to have a better grasp on what the answer to this question,
2 - What is the difference between a plot, figure, canvas, etc. in regards to the area which they cover in the GUI.
Thank you very much for the help.
All this can certainly be rather confusing at first!
To begin with, if you're chaining the ticks, etc, there isn't much point in using blitting. Blitting is just a way to avoid re-drawing everything if only some things are changing. If everything is changing, there's no point in using blitting. Just re-draw the plot.
Basically, you just want fig.canvas.draw() or plt.draw()
At any rate, to answer your first question, in most cases you won't need to update them manually. If you change the axis limits, they'll update themselves. You're running into problems because you're blitting just the inside of the axes instead of redrawing the plot.
As for your second question, a good, detailed overview is the Artist Tutorial of the Matplotlib User's Guide.
In a nutshell, there are two separate layers. One deals with grouping things into the parts that you'll worry about when plotting (e.g. the figure, axes, axis, lines, etc) and another that deals with rendering and drawing in general (the canvas and renderer).
Anything you can see in a matplotlib plot is an Artist. (E.g. text, a line, the axes, and even the figure itself.) An artist a) knows how to draw itself, and b) can contain other artists.
For an artist to draw itself, it uses the renderer (a backend-specific module that you'll almost never touch directly) to draw on a FigureCanvas a.k.a. "canvas" (an abstraction around either a vector-based page or a pixel buffer). To draw everything in a figure, you call canvas.draw().
Because artists can be groups of other artists, there's a hierarchy to things. Basically, something like this (obviously, this varies):
Figure
Axes (0-many) (An axes is basically a plot)
Axis (usually two) (x-axis and y-axis)
ticks
ticklabels
axis label
background patch
title, if present
anything you've plotted, e.g. Line2D's
Hopefully that makes things a touch clearer, anyway.
If you really do want to use blitting to update the tick labels, etc, you'll need to grab and restore the full region including them. This region is a bit tricky to get, because it isn't exactly known until after draw-time (rendering text in matplotlib is more complicated than rendering other things due to latex support, etc). You can do it, and I'll be glad to give an example if it's really what you want, but it's typically not going to yield a speed advantage over just drawing everything. (The exception is if you're only updating one subplot in a figure with lots of subplots.)

Python: copy basemap or remove data from figure

I want to use a basemap figure as a background and plot things ontop. As the generation of the basemap figure (a polar stereographic plot) is quite time intensive I only want to do this once.
The second step would be to draw some wind barbs on this plot and some other wind barbs on the same plot, but without the first set of barbs.
Now either I copy the background image and then draw two different images or I can remove the first set of barbs and then draw the second, I don't mind which way to go.
Unfortunately my brain is not helping me today and I am getting nowhere. I would be grateful for any tips.
If the new barbs will be in the same place you can use set_UVC to update the existing object with the new wind data.
Assuming m is your basemap object, something like:
n_barb,s_barb = m.barbs(...)
#some code
n_barb.set_UVC(newU,newV,newC)
I don't have wind data to test this on though.

Categories

Resources