Related
I am trying to put text in a matplotlib animation. (Hopefully outside the plot, but I am not worrying about that yet)
I tried to follow this solution, however my code is a bit complicated in that it does not gives only one line every time.
Here is my code
import math
import argparse
import os
import json
import sys
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation ,FFMpegWriter
line_x=[ 0,1,2,3,4,5,6,7,8,9,10,11,12 ]
line1_y=[ 3,5,7,9,11,19,23,26,29,31,37,40,45 ]
line2_y=[0,2,5,7,10,10,8,5,3,2,1,3,5]
line3_y=[39,38,32,29,26,22,19,13,10,8,7,6,3]
set_lines=[line1_y,line2_y,line3_y]
n_lineas=[1,2,3,1,3,2,3,1,3,2,1,2]
show=True
thecolors=['blue','red','violet']
thelegends=['unus','duo','tres']
print(sys.argv)
if len(sys.argv)==2 and sys.argv[1]=='movie':
show=False
def get_n(thelist,c):
while(c>=len(thelist)):
c-len(thelist)
return thelist[c]
class Update:
def __init__(self,ax,limit_x):
self.ax = ax
self.lx=limit_x
if limit_x!=0:
self.ax.set_xlim(0,limit_x)
self.ax.set_ylim(0,45)
self.ax.set_aspect('equal')
self.ax.grid(True)
self.lines=()
self.counter=0
self.text=self.ax.text(0,0,'')
def __call__(self, frame):
print("Frame: ",frame)
lines=[]
self.ax.cla()
self.ax.set_xlim(0,self.lx)
self.ax.set_ylim(0,45)
self.ax.grid(True)
self.ax.set_xlabel("Y (meters)")
self.ax.set_ylabel("X (meters)")
n_lines_this_time=get_n(n_lineas,self.counter)
self.counter+=1
print(n_lines_this_time,"lines this time")
for myline in range(n_lines_this_time):
#line,=self.ax.plot([],[],'.-',color=gt_color,label=legend)
line,=self.ax.plot([],[],'.-',color=thecolors[myline],label=thelegends[myline])
x = []
y = []
for v in range(13):
x.append(line_x[v])
y.append(set_lines[myline][v])
line.set_xdata(x)
line.set_ydata(y)
lines.append(line)
plt.legend()
self.lines=tuple(lines)
self.text.set_text("Frame "+str(frame))
self.text.set_position((0,0))
#return self.lines,self.text #<---HERE this does not work!!!
return self.lines
def init(self):
print("Init")
line,=self.ax.plot([],[])
self.ax.grid(True)
self.ax.set_xlabel("Y (meters)")
self.ax.set_ylabel("X (meters)")
self.text.set_text('')
self.text.set_position((0,0))
return line,self.text,
#return line,
fig, ax = plt.subplots(1, 1,figsize=(10,10))
plt.gcf().canvas.mpl_connect(
'key_release_event',
lambda event: [exit(0) if event.key == 'escape' else None])
plt.xlabel("Y (meters)")
plt.ylabel("X (meters)")
plt.legend()
ug_i = Update(ax,13)
anim = FuncAnimation(fig, ug_i,init_func=ug_i.init, frames=10, interval=1000, blit=True,repeat=False)
if not show:
writervideo = FFMpegWriter(fps=1)
anim.save('whatever.mp4', writer=writervideo)
print('done')
plt.close()
else:
#plt.legend()
plt.show()
In the current state, the text does not show (of course) but when I try to return it (as marked above in a comment ("HERE") ) it crashes giving me the error
Traceback (most recent call last):
File "/home/kansai/miniconda3/envs/roscv/lib/python3.7/site-packages/matplotlib/backend_bases.py", line 1194, in _on_timer
ret = func(*args, **kwargs)
File "/home/kansai/miniconda3/envs/roscv/lib/python3.7/site-packages/matplotlib/animation.py", line 1442, in _step
still_going = Animation._step(self, *args)
File "/home/kansai/miniconda3/envs/roscv/lib/python3.7/site-packages/matplotlib/animation.py", line 1173, in _step
self._draw_next_frame(framedata, self._blit)
File "/home/kansai/miniconda3/envs/roscv/lib/python3.7/site-packages/matplotlib/animation.py", line 1192, in _draw_next_frame
self._draw_frame(framedata)
File "/home/kansai/miniconda3/envs/roscv/lib/python3.7/site-packages/matplotlib/animation.py", line 1748, in _draw_frame
key=lambda x: x.get_zorder())
File "/home/kansai/miniconda3/envs/roscv/lib/python3.7/site-packages/matplotlib/animation.py", line 1748, in <lambda>
key=lambda x: x.get_zorder())
AttributeError: 'tuple' object has no attribute 'get_zorder'
Aborted (core dumped)
What is failing and how can I display the text? (if outside the plot much better)
After un-commenting your line of code, I didn't get any error, however the text was not visible. So, instead of using ax.text I tried fig.text: now the text is visible outside the plotting area.
import math
import argparse
import os
import json
import sys
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation ,FFMpegWriter
line_x=[ 0,1,2,3,4,5,6,7,8,9,10,11,12 ]
line1_y=[ 3,5,7,9,11,19,23,26,29,31,37,40,45 ]
line2_y=[0,2,5,7,10,10,8,5,3,2,1,3,5]
line3_y=[39,38,32,29,26,22,19,13,10,8,7,6,3]
set_lines=[line1_y,line2_y,line3_y]
n_lineas=[1,2,3,1,3,2,3,1,3,2,1,2]
show=True
thecolors=['blue','red','violet']
thelegends=['unus','duo','tres']
# print(sys.argv)
# if len(sys.argv)==2 and sys.argv[1]=='movie':
# show=False
def get_n(thelist,c):
while(c>=len(thelist)):
c-len(thelist)
return thelist[c]
class Update:
def __init__(self,fig,ax,limit_x):
self.ax = ax
self.lx=limit_x
if limit_x!=0:
self.ax.set_xlim(0,limit_x)
self.ax.set_ylim(0,45)
self.ax.set_aspect('equal')
self.ax.grid(True)
self.lines=()
self.counter=0
self.text=fig.text(0.15,0.5,'')
def __call__(self, frame):
print("Frame: ",frame)
lines=[]
self.ax.cla()
self.ax.set_xlim(0,self.lx)
self.ax.set_ylim(0,45)
self.ax.grid(True)
self.ax.set_xlabel("Y (meters)")
self.ax.set_ylabel("X (meters)")
n_lines_this_time=get_n(n_lineas,self.counter)
self.counter+=1
print(n_lines_this_time,"lines this time")
for myline in range(n_lines_this_time):
#line,=self.ax.plot([],[],'.-',color=gt_color,label=legend)
line,=self.ax.plot([],[],'.-',color=thecolors[myline],label=thelegends[myline])
x = []
y = []
for v in range(13):
x.append(line_x[v])
y.append(set_lines[myline][v])
line.set_xdata(x)
line.set_ydata(y)
lines.append(line)
plt.legend()
self.lines=tuple(lines)
self.text.set_text("Frame "+str(frame))
return self.lines,self.text #<---HERE this does not work!!!
return self.lines
def init(self):
print("Init")
line,=self.ax.plot([],[])
self.ax.grid(True)
self.ax.set_xlabel("Y (meters)")
self.ax.set_ylabel("X (meters)")
self.text.set_text('')
return line,self.text,
#return line,
fig, ax = plt.subplots(1, 1)
plt.gcf().canvas.mpl_connect(
'key_release_event',
lambda event: [exit(0) if event.key == 'escape' else None])
plt.xlabel("Y (meters)")
plt.ylabel("X (meters)")
plt.legend()
ug_i = Update(fig,ax,13)
anim = FuncAnimation(fig, ug_i,init_func=ug_i.init, frames=10, interval=1000, blit=True,repeat=False)
if not show:
writervideo = FFMpegWriter(fps=1)
anim.save('whatever.mp4', writer=writervideo)
print('done')
plt.close()
else:
#plt.legend()
plt.show()
I want to animate a matrix plot by just changing the colormap. but i could not get it running
this is my code:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib import cm
from matplotlib.colors import ListedColormap, LinearSegmentedColormap
from matplotlib.animation import FuncAnimation
def rotate(l, n):
return l[n:] + l[:n]
def samplemat(dims):
aa = np.zeros(dims)
for i in range(min(dims)):
for j in range(min(dims)):
aa[i, j] = (i+1)*(j+1)
return aa
min_val, max_val = 0, 15
fig = plt.figure()
mat = samplemat((15, 15))
colmapV = plt.cm.Blues(list(range(0, max_val*max_val)))
colmap = ListedColormap(colmapV)
ms = plt.matshow(mat, cmap=colmap)
def animate(i):
global colmapV
colmapV = rotate(colmapV, 1)
colmap = ListedColormap(colmapV)
ms = plt.matshow(mat, cmap=colmap)
return ms,
# create animation using the animate() function
myAnimation = FuncAnimation(fig, animate, frames=100, interval=100, blit=True, repeat=True)
plt.show()
it fails with
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python3\lib\tkinter\__init__.py", line 1884, in __call__
return self.func(*args)
File "C:\Python3\lib\tkinter\__init__.py", line 805, in callit
func(*args)
File "C:\Python3\lib\site-packages\matplotlib\backends\_backend_tk.py", line 99, in _on_timer
TimerBase._on_timer(self)
File "C:\Python3\lib\site-packages\matplotlib\backend_bases.py", line 1194, in _on_timer
ret = func(*args, **kwargs)
File "C:\Python3\lib\site-packages\matplotlib\animation.py", line 1426, in _step
still_going = Animation._step(self, *args)
File "C:\Python3\lib\site-packages\matplotlib\animation.py", line 1157, in _step
self._draw_next_frame(framedata, self._blit)
File "C:\Python3\lib\site-packages\matplotlib\animation.py", line 1177, in _draw_next_frame
self._post_draw(framedata, blit)
File "C:\Python3\lib\site-packages\matplotlib\animation.py", line 1200, in _post_draw
self._blit_draw(self._drawn_artists)
File "C:\Python3\lib\site-packages\matplotlib\animation.py", line 1222, in _blit_draw
a.axes.draw_artist(a)
File "C:\Python3\lib\site-packages\matplotlib\axes\_base.py", line 2760, in draw_artist
raise AttributeError("draw_artist can only be used after an "
AttributeError: draw_artist can only be used after an initial draw which caches the renderer
Does anybody a hint?
Another small issue is that I always get 2 figure windows, but if i remove the figure call I do not know that I should pass to FuncAnimation.
This is a weird error, and the fact that you get two figures is also weird.
You can fix it by creating the axes explicitly, but then I don't think the way you are shifting your colormap is quite right. Here is the code I ended up with:
import matplotlib.pyplot as plt
import numpy as np
from matplotlib.colors import LinearSegmentedColormap
from matplotlib.animation import FuncAnimation
def samplemat(dims):
aa = np.zeros(dims)
for i in range(min(dims)):
for j in range(min(dims)):
aa[i, j] = (i + 1) * (j + 1)
return aa
min_val, max_val = 0, 15
fig = plt.figure()
ax = fig.add_subplot(111)
mat = samplemat((15, 15))
cmap = plt.get_cmap('Blues')
ms = ax.matshow(mat, cmap=cmap)
def animate(i, out):
out = np.roll(out, i)
new_cmap = LinearSegmentedColormap.from_list("", cmap(out))
ms.set_cmap(new_cmap)
return ms,
# create animation using the animate() function
myAnimation = FuncAnimation(fig, animate, frames=max_val*max_val, interval=100, blit=True, repeat=True,
fargs=(np.arange(0, max_val * max_val),))
plt.show()
(I had to reduce the resolution to fit in the filesize requirements)
I cannot add a second projection 3d axis to my figure.
If I uncomment the line of ax2, I'm getting the error:
Traceback (most recent call last): File
"/sequoia/data1/gcheron/lib/anaconda2/envs/detectron/lib/python2.7/site-packages/matplotlib/cbook/init.py",
line 388, in process
proxy(*args, **kwargs) File "/sequoia/data1/gcheron/lib/anaconda2/envs/detectron/lib/python2.7/site-packages/matplotlib/cbook/init.py",
line 228, in call
return mtd(*args, **kwargs) File "/sequoia/data1/gcheron/lib/anaconda2/envs/detectron/lib/python2.7/site-packages/matplotlib/animation.py",
line 1499, in _stop
self.event_source.remove_callback(self._loop_delay) AttributeError: 'NoneType' object has no attribute 'remove_callback'
import numpy as np
from matplotlib import pyplot as plt
import matplotlib.animation
import pandas as pd
a = np.random.rand(2000, 3)*10
t = np.array([np.ones(100)*i for i in range(20)]).flatten()
df = pd.DataFrame({"time": t ,"x" : a[:,0], "y" : a[:,1], "z" : a[:,2]})
def update_graph(num):
data=df[df['time']==num]
graph._offsets3d = (data.x, data.y, data.z)
title.set_text('3D Test, time={}'.format(num))
fig = plt.figure()
ax = fig.add_subplot(221, projection='3d')
#ax2 = fig.add_subplot(222, projection='3d')
title = ax.set_title('3D Test')
ax.scatter(data.x, data.y, data.z)
data=df[df['time']==0]
graph = ax.scatter(data.x, data.y, data.z)
ani = animation.FuncAnimation(fig, update_graph, 20,
interval=50, blit=True)
ani.save('motion.mp4', writer=writer)
If I let it comment, the code works fine and the video contains 4 subplot cells (3 are empty). Thanks!
When correcting for undefined variables, missing imports and missing return statement,
from mpl_toolkits.mplot3d import Axes3D
import numpy as np
from matplotlib import pyplot as plt
import matplotlib.animation as animation
import pandas as pd
a = np.random.rand(2000, 3)*10
t = np.array([np.ones(100)*i for i in range(20)]).flatten()
df = pd.DataFrame({"time": t ,"x" : a[:,0], "y" : a[:,1], "z" : a[:,2]})
def update_graph(num):
data=df[df['time']==num]
graph._offsets3d = (data.x, data.y, data.z)
title.set_text('3D Test, time={}'.format(num))
return graph, title
fig = plt.figure()
ax = fig.add_subplot(221, projection='3d')
ax2 = fig.add_subplot(222, projection='3d')
title = ax.set_title('3D Test')
ax.scatter(df.x, df.y, df.z)
data=df[df['time']==0]
graph = ax2.scatter(data.x, data.y, data.z)
ani = animation.FuncAnimation(fig, update_graph, 20,
interval=50, blit=True)
ani.save('motion.gif', writer="imagemagick")
plt.show()
the animation looks like this:
I am new to python so I understand that this may be a stupid question, but I am having issues animating this. I can't see what the error is. I get this error
TypeError: f() missing 1 required positional argument: '
I want to use matplotlib when animating, because I have not downloaded scitools.
Any help at all would be very much appriciated
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib import style
x = np.linspace(-6, 6)
tmax = 1
tmin = -1
t = np.linspace(-1, 1)
def f(x, t):
term = (np.exp(-1*(x-3*t)**2))*np.sin(3*np.pi*(x-t))
return term
max_f = f(x[-1], t[-1])
plt.ion()
y = f(x, tmax)
lines = plt.plot(x, y)
plt.axis([x[0], x[-1], -0.1, max_f])
plt.xlabel('x')
plt.ylabel('f')
counter = 0
for ts in t:
y = f(x, t)
lines[0].set_ydata(y)
plt.legend(['ts=%4.2f' % ts])
plt.draw()
plt.savefig('tmp_%04d.png' % counter)
counter += 1
fig = plt.figure()
anim = animation.FuncAnimation(fig, f, interval = 1000, blit=True)
fig = plt.figure()
plt.axis([x[0], x[-1], -0.1, max_f])
lines = plt.plot([], [])
plt.xlabel('x')
plt.ylabel('f')
plt.show()
EDIT, full traceback:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\me\AppData\Local\Programs\Python\Python36-32\lib\tkinter__init__.py", line 1699, in call
return self.func(*args)
File "C:\Users\me\AppData\Local\Programs\Python\Python36-32\lib\tkinter__init__.py", line 745, in callit
func(*args)
File "C:\Users\me\AppData\Local\Programs\Python\Python36-32\lib\site-packages\matplotlib\backends\backend_tkagg.py", line 370, in idle_draw
self.draw()
File "C:\Users\me\AppData\Local\Programs\Python\Python36-32\lib\site-packages\matplotlib\backends\backend_tkagg.py", line 351, in draw
FigureCanvasAgg.draw(self)
File "C:\Users\me\AppData\Local\Programs\Python\Python36-32\lib\site-packages\matplotlib\backends\backend_agg.py", line 464, in draw
self.figure.draw(self.renderer)
File "C:\Users\me\AppData\Local\Programs\Python\Python36-32\lib\site-packages\matplotlib\artist.py", line 63, in draw_wrapper
draw(artist, renderer, *args, **kwargs)
File "C:\Users\me\AppData\Local\Programs\Python\Python36-32\lib\site-packages\matplotlib\figure.py", line 1151, in draw
self.canvas.draw_event(renderer)
File "C:\Users\me\AppData\Local\Programs\Python\Python36-32\lib\site-packages\matplotlib\backend_bases.py", line 1823, in draw_event
self.callbacks.process(s, event)
File "C:\Users\me\AppData\Local\Programs\Python\Python36-32\lib\site-packages\matplotlib\cbook.py", line 554, in process
proxy(*args, **kwargs)
File "C:\Users\me\AppData\Local\Programs\Python\Python36-32\lib\site-packages\matplotlib\cbook.py", line 416, in call
return mtd(*args, **kwargs)
File "C:\Users\me\AppData\Local\Programs\Python\Python36-32\lib\site-packages\matplotlib\animation.py", line 881, in _start
self._init_draw()
File "C:\Users\me\AppData\Local\Programs\Python\Python36-32\lib\site-packages\matplotlib\animation.py", line 1540, in _init_draw
self._draw_frame(next(self.new_frame_seq()))
File "C:\Users\me\AppData\Local\Programs\Python\Python36-32\lib\site-packages\matplotlib\animation.py", line 1562, in _draw_frame
self._drawn_artists = self._func(framedata, *self._args)
TypeError: f() missing 1 required positional argument: 't'
As said, this is not really about the error, you can easily prevent that by supplying some value for t as fargs in FuncAnimation. However, this will not lead to the code producing an animation at all and hence as said, start with the exmaple add your functions and code step by step and see what happens.
This will eventually lead to something like the following:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
x = np.linspace(-6, 6)
tmax = 1
tmin = -1
t = np.linspace(-1, 1)
def f(x, t):
term = (np.exp(-1*(x-3*t)**2))*np.sin(3*np.pi*(x-t))
return term
y = f(x, tmax)
lines = plt.plot(x, y)
plt.axis([x[0], x[-1], -1, 1])
plt.xlabel('x')
plt.ylabel('f')
counter = [0]
def animate(ts):
y = f(x, ts)
lines[0].set_ydata(y)
plt.legend(['ts=%4.2f' % ts])
#plt.savefig('tmp_%04d.png' % counter)
counter[0] += 1
anim = animation.FuncAnimation(plt.gcf(), animate, frames = t, interval = 1000)
plt.show()
The code is shown below. I am attempting to animate using vectors calculated earlier a figure window is opened so i know it gets this far and the vectors are being calclated correctly. But matplotlib oututs nothing but the figure window I have no idea why. Please help.
#finally animateing
fig = plt.figure()
ax = plt.axes(xlim = (-1000,1000) ,ylim = (-1000,1000))#limits were arbitrary
#line = ax.plot([],[])
line, = ax.plot([], [], lw=2)
# initialization function: plot the background of each frame
def init():
line.set_data([], [])
return line,
def animate(i):
x = time_vec[i]
y = complex_vec[i]
#y1 = real_vec[i]
#y2 = modulus_vec[i]
line.set_data(x,y)
#line.set_data(x,y1)
#line.set_data(x,y2)
return line,
animation_object = animation.FuncAnimation(fig, animate, init_func= init, frames = num_files,interval = 30, blit = True)
#turnn this line on to save as mp4
#anim.save("give it a name.mp4", fps = 30, extra-args = ['vcodec', 'libx264'])
plt.show()
THE FULL ERROR MESSAGE IS SHOWN BELOW
Traceback (most recent call last):
File "the_animation.py", line 71, in <module>
plt.show()
File "/usr/lib/pymodules/python2.7/matplotlib/pyplot.py", line 145, in show
_show(*args, **kw)
File "/usr/lib/pymodules/python2.7/matplotlib/backend_bases.py", line 117, in __call__
self.mainloop()
File "/usr/lib/pymodules/python2.7/matplotlib/backends/backend_tkagg.py", line 69, in mainloop
Tk.mainloop()
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 366, in mainloop
_default_root.tk.mainloop(n)
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1484, in __call__
def __call__(self, *args):
MINIMAL EXAMPLE
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
complex_vec = np.arange(5,6,.001)
real_vec = np.arange(7,8,.001)
time_vec = np.arange(0,1,.001)
num_files = np.size(time_vec)
#creating the modulus vector
modulus_vec = np.zeros(np.shape(complex_vec))
for k in range (0,complex_vec.size):
a = complex_vec[k]
b = real_vec[k]
calc_modulus = np.sqrt(a**2 + b**2)
modulus_vec[k] = calc_modulus
#finally animateing
fig = plt.figure()
ax = plt.axes(xlim = (-1000,1000) ,ylim = (-1000,1000))#limits were arbitrary
#line = ax.plot([],[])
line, = ax.plot([], [], lw=2)
# initialization function: plot the background of each frame
def init():
line.set_data([], [])
return line,
def animate(i):
x = time_vec[i]
y = complex_vec[i]
y1 = real_vec[i]
y2 = modulus_vec[i]
line.set_data(x,y)
line.set_data(x,y1)
line.set_data(x,y2)
return line,
animation_object = animation.FuncAnimation(fig, animate, init_func= init, frames = num_files,interval = 30, blit = True)
#turnn this line on to save as mp4
#anim.save("give it a name.mp4", fps = 30, extra-args = ['vcodec', 'libx264'])
plt.show()
The problem here is in your animate function, you're using set_data multiple times which does not do what you think it does. You're using it like an append, when it's a set. The arguments should be two arrays, containing the respective x and y values for that line. This will animate your minimal example:
import numpy as np
from matplotlib import pyplot as plt
from matplotlib import animation
complex_vec = np.arange(5,6,.001)
real_vec = np.arange(7,8,.001)
time_vec = np.arange(0,1,.001)
num_files = np.size(time_vec)
#creating the modulus vector
modulus_vec = np.zeros(np.shape(complex_vec))
for k in range (0,complex_vec.size):
a = complex_vec[k]
b = real_vec[k]
calc_modulus = np.sqrt(a**2 + b**2)
modulus_vec[k] = calc_modulus
#finally animateing
fig = plt.figure()
ax = plt.axes(xlim = (-1,1) ,ylim = (-1,15))#limits were arbitrary
#line = ax.plot([],[])
line, = ax.plot([], [], lw=2)
# initialization function: plot the background of each frame
def init():
line.set_data([], [])
return line,
def animate(i):
x = time_vec[i]
y = complex_vec[i]
y1 = real_vec[i]
y2 = modulus_vec[i]
# notice we are only calling set_data once, and bundling the y values into an array
line.set_data(x,np.array([y, y1, y2]))
return line,
animation_object = animation.FuncAnimation(fig,
animate,
init_func= init,
frames = num_files,
interval = 30,
blit = True)
#turnn this line on to save as mp4
#anim.save("give it a name.mp4", fps = 30, extra-args = ['vcodec', 'libx264'])
plt.show()
Your previous attempt was setting the x and y values, then overwriting the previous with a new x and y, then doing that once again.