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()
Related
I have a matplotlib plot with a slider and am interested in setting the colors of the points plotted as the slider gets dragged one way or the other. I know you can set the colors of points with set_array() but so far have had luck passing set_array() only 1D arrays of floats. When I pass set_array() an array of strings corresponding to matplotlib colors, I receive the error and traceback below.
Here are some relevant code snippets, with dummy info. for ease of assessment. With the two commented-out lines in update(val), the error below appears. Un-commenting them, so that set_array() gets a float, fixes the issue, but then I lose ability to control the colors with any precision. (For instance, changing 'g' to 'c' makes no perceptible difference in the colors.)
import numpy as np
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
import matplotlib.colors as c
x = np.linspace(1, 100, 100)
y = x**2
fig, ax = plt.subplots()
plt.subplots_adjust(left=0.1, bottom=0.20)
plt.ylim([y.min(), y.max()])
plt.xlim([0, 100])
init = 4
scat = ax.scatter(x[:init], y[:init], s=5, c=['k']*init)
ax_slider = plt.axes([0.15, 0.05, 0.65, 0.03])
slider = Slider(ax_slider, 'Day', 0, 100, valinit=init, valfmt="%i")
# Update function, called upon slider movement
def update(val):
val = int(val)
colors_new = np.array(['k']*val, dtype=object)
colors_new[::2] = 'g'
colors_new[1::2] = 'm'
# colors_new = map(lambda x: c.to_rgb(x), colors_new)
# colors_new = np.dot(colors_new, [0.2989, 0.5870, 0.1140])
scat.set_array(colors_new)
xx = np.vstack((x[:val], y[:val]))
scat.set_offsets(xx.T)
# Call update function on slider value change
slider.on_changed(update)
plt.show()
Traceback:
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/local/Cellar/python/2.7.6_1/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 1470, in __call__
return self.func(*args)
File "/usr/local/Cellar/python/2.7.6_1/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-tk/Tkinter.py", line 531, in callit
func(*args)
File "/Users/johnsmith/PycharmProjects/LawSchoolData/venv/lib/python2.7/site-packages/matplotlib/backends/_backend_tk.py", line 310, in idle_draw
self.draw()
File "/Users/johnsmith/PycharmProjects/LawSchoolData/venv/lib/python2.7/site-packages/matplotlib/backends/backend_tkagg.py", line 12, in draw
super(FigureCanvasTkAgg, self).draw()
File "/Users/johnsmith/PycharmProjects/LawSchoolData/venv/lib/python2.7/site-packages/matplotlib/backends/backend_agg.py", line 437, in draw
self.figure.draw(self.renderer)
File "/Users/johnsmith/PycharmProjects/LawSchoolData/venv/lib/python2.7/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
return draw(artist, renderer, *args, **kwargs)
File "/Users/johnsmith/PycharmProjects/LawSchoolData/venv/lib/python2.7/site-packages/matplotlib/figure.py", line 1493, in draw
renderer, self, artists, self.suppressComposite)
File "/Users/johnsmith/PycharmProjects/LawSchoolData/venv/lib/python2.7/site-packages/matplotlib/image.py", line 141, in _draw_list_compositing_images
a.draw(renderer)
File "/Users/johnsmith/PycharmProjects/LawSchoolData/venv/lib/python2.7/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
return draw(artist, renderer, *args, **kwargs)
File "/Users/johnsmith/PycharmProjects/LawSchoolData/venv/lib/python2.7/site-packages/matplotlib/axes/_base.py", line 2635, in draw
mimage._draw_list_compositing_images(renderer, self, artists)
File "/Users/johnsmith/PycharmProjects/LawSchoolData/venv/lib/python2.7/site-packages/matplotlib/image.py", line 141, in _draw_list_compositing_images
a.draw(renderer)
File "/Users/johnsmith/PycharmProjects/LawSchoolData/venv/lib/python2.7/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
return draw(artist, renderer, *args, **kwargs)
File "/Users/johnsmith/PycharmProjects/LawSchoolData/venv/lib/python2.7/site-packages/matplotlib/collections.py", line 911, in draw
Collection.draw(self, renderer)
File "/Users/johnsmith/PycharmProjects/LawSchoolData/venv/lib/python2.7/site-packages/matplotlib/artist.py", line 55, in draw_wrapper
return draw(artist, renderer, *args, **kwargs)
File "/Users/johnsmith/PycharmProjects/LawSchoolData/venv/lib/python2.7/site-packages/matplotlib/collections.py", line 264, in draw
self.update_scalarmappable()
File "/Users/johnsmith/PycharmProjects/LawSchoolData/venv/lib/python2.7/site-packages/matplotlib/collections.py", line 808, in update_scalarmappable
self._facecolors = self.to_rgba(self._A, self._alpha)
File "/Users/johnsmith/PycharmProjects/LawSchoolData/venv/lib/python2.7/site-packages/matplotlib/cm.py", line 274, in to_rgba
x = self.norm(x)
File "/Users/johnsmith/PycharmProjects/LawSchoolData/venv/lib/python2.7/site-packages/matplotlib/colors.py", line 943, in __call__
self.autoscale_None(result)
File "/Users/johnsmith/PycharmProjects/LawSchoolData/venv/lib/python2.7/site-packages/matplotlib/colors.py", line 994, in autoscale_None
self.vmin = A.min()
File "/Users/johnsmith/PycharmProjects/LawSchoolData/venv/lib/python2.7/site-packages/numpy/ma/core.py", line 5602, in min
axis=axis, out=out, **kwargs).view(type(self))
AttributeError: 'str' object has no attribute 'view'
set_array() only works to set the color values when the color is defined by a colormap. You could create a custom ListedColormap and set the values numerically.
If you want to set the colors via color values, you can use set_facecolors():
import numpy as np
import matplotlib
matplotlib.use('TkAgg')
import matplotlib.pyplot as plt
from matplotlib.widgets import Slider
x = np.linspace(1, 100, 100)
y = x**2
fig, ax = plt.subplots()
plt.subplots_adjust(left=0.1, bottom=0.20)
plt.ylim([y.min(), y.max()])
plt.xlim([0, 100])
init = 4
scat = ax.scatter(x[:init], y[:init], s=5, c=['k']*init)
ax_slider = plt.axes([0.15, 0.05, 0.65, 0.03])
slider = Slider(ax_slider, 'Day', 0, 100, valinit=init, valfmt="%i")
# Update function, called upon slider movement
def update(val):
val = int(val)
colors_new = np.array(['k']*val, dtype=object)
colors_new[::2] = 'g'
colors_new[1::2] = 'm'
scat.set_facecolors(colors_new)
xx = np.vstack((x[:val], y[:val]))
scat.set_offsets(xx.T)
# Call update function on slider value change
slider.on_changed(update)
plt.show()
Here is an example working with a colormap:
colors = ['k', 'g', 'm']
cmap = ListedColormap(colors)
scat = ax.scatter(x[:init], y[:init], s=5, c=np.zeros(init), cmap=cmap, vmin=0, vmax=len(colors)-1)
#....
def update(val):
val = int(val)
colors_new = np.zeros(val)
colors_new[::2] = 1
colors_new[1::2] = 2
scat.set_array(colors_new)
#....
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 want to Animate a 3D Polar Plot and export it, because my Computer has not enough CPU Power for displaying it with plt.show() (If you have a good solution how to calculate it before showing, I would appreciate it, too)
Unfortunatly the export does not work and I do not know why. To my mind this should work.
Is it possible that my Python IDE(PyCharm) oder my Python Version(2.7) is responsible?
import numpy as np
import matplotlib.pyplot as plt
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.animation as animation
from IPython import display
psi_vec = np.arange(0, 2 * np.pi, 0.1)
radius = np.arange(0,1,0.05)
Z_ones = np.ones(63)
def animate(time):
X = np.zeros((np.size(psi_vec), np.size(radius)))
Y = np.zeros((np.size(psi_vec), np.size(radius)))
Z = np.zeros((np.size(psi_vec), np.size(radius)))
for i in range(np.size(radius)):
X[:,i] = np.cos(psi_vec) * radius[i]
Y[:,i] = np.sin(psi_vec) * radius[i]
Z[:,i] = np.sin(time) * i * Z_ones
line = ax.plot_surface(X, Y, Z,color= 'b')
return line
fig = plt.figure()
ax = p3.Axes3D(fig)
anim = animation.FuncAnimation(fig, animate, frames = 15, interval = 500)
video = anim.to_html5_video()
html = display(video)
display.display(html)
The Error Message is:
Traceback (most recent call last):
File "/home/alexander/Dokumente/PetersHe (vectors)/animation.py", line 48, in <module>
video = anim.to_html5_video()
File "/home/alexander/anaconda2/envs/PyEnv/lib/python2.7/site-packages/matplotlib/animation.py", line 1353, in to_html5_video
self.save(f.name, writer=writer)
File "/home/alexander/anaconda2/envs/PyEnv/lib/python2.7/site-packages/matplotlib/animation.py", line 1200, in save
writer.grab_frame(**savefig_kwargs)
File "/home/alexander/anaconda2/envs/PyEnv/lib/python2.7/contextlib.py", line 35, in __exit__
self.gen.throw(type, value, traceback)
File "/home/alexander/anaconda2/envs/PyEnv/lib/python2.7/site-packages/matplotlib/animation.py", line 241, in saving
self.finish()
File "/home/alexander/anaconda2/envs/PyEnv/lib/python2.7/site-packages/matplotlib/animation.py", line 367, in finish
self.cleanup()
File "/home/alexander/anaconda2/envs/PyEnv/lib/python2.7/site-packages/matplotlib/animation.py", line 405, in cleanup
out, err = self._proc.communicate()
File "/home/alexander/anaconda2/envs/PyEnv/lib/python2.7/site-packages/subprocess32.py", line 748, in communicate
stdout, stderr = self._communicate(input, endtime, timeout)
File "/home/alexander/anaconda2/envs/PyEnv/lib/python2.7/site-packages/subprocess32.py", line 1557, in _communicate
orig_timeout)
File "/home/alexander/anaconda2/envs/PyEnv/lib/python2.7/site-packages/subprocess32.py", line 1613, in _communicate_with_poll
register_and_append(self.stdout, select_POLLIN_POLLPRI)
File "/home/alexander/anaconda2/envs/PyEnv/lib/python2.7/site-packages/subprocess32.py", line 1592, in register_and_append
poller.register(file_obj.fileno(), eventmask)
ValueError: I/O operation on closed file
Process finished with exit code 1
Thank You!
I am new with matplotlib and I am trying to make an animation of a list of grib files.
I wrote this code:
import pygrib
from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
import matplotlib.animation as animation
import os
m = Basemap(projection='robin', resolution = 'l', area_thresh = 1000.0,
lat_0=0, lon_0=-130)
m.drawcoastlines()
m.drawcountries()
m.fillcontinents(color = 'gray')
m.drawmapboundary()
m.drawmeridians(np.arange(0, 360, 30))
m.drawparallels(np.arange(-90, 90, 30))
for grib in os.listdir("path"):
grbs=pygrib.open(grib)
for grb in grbs:
print grb
lats, lons = grb.latlons()
data=grb.values
x, y = m(lons,lats)
norm=colors.LogNorm())
m.drawcoastlines()
m.drawcountries()
m.fillcontinents(color = 'gray')
m.drawmapboundary()
m.drawmeridians(np.arange(0, 360, 30))
m.drawparallels(np.arange(-90, 90, 30))
cmap = plt.get_cmap('YlOrBr')
cs = m.pcolormesh(x,y,data,shading='flat',cmap=cmap)
plt.colorbar(cs,orientation='vertical', shrink=0.3)
plt.show()
and it works opening a windows that I have to close and after this opening a new one.
I want to use a nice animation with matplotlib.animation : i would change the grib over a map and showing it as a time series.
I try this:
m = Basemap(projection='robin', resolution = 'l', area_thresh = 1000.0,
lat_0=0, lon_0=-130)
m.drawcoastlines()
m.drawcountries()
m.fillcontinents(color = 'gray')
m.drawmapboundary()
m.drawmeridians(np.arange(0, 360, 30))
m.drawparallels(np.arange(-90, 90, 30))
#reading the gribs files
griblist = os.listdir("path")
def animate (i):
global griblist
for grib in griblist:
grbs=pygrib.open(grib)
for grb in grbs:
grb
lats, lons = grb.latlons()
x, y = m(lons, lats)
data = grb.values
cmap = plt.get_cmap('YlOrBr')
cs = m.pcolormesh(x,y,data,shading='flat',cmap=cmap)
plt.colorbar(cs,orientation='vertical', shrink=0.5)
plt.title('UV biological effective dose')
anim = animation.FuncAnimation(plt.gcf(), animate,
frames=len(os.listdir("/home/gloria/UV_CAMS")), interval=500, blit=True)
plt.show()
But I got this error:
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1540, in __call__
return self.func(*args)
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 590, in callit
func(*args)
File "/usr/lib/python2.7/dist-packages/matplotlib/backends/backend_tkagg.py", line 373, in idle_draw
self.draw()
File "/usr/lib/python2.7/dist-packages/matplotlib/backends/backend_tkagg.py", line 354, in draw
FigureCanvasAgg.draw(self)
File "/usr/lib/python2.7/dist-packages/matplotlib/backends/backend_agg.py", line 474, in draw
self.figure.draw(self.renderer)
File "/usr/lib/python2.7/dist-packages/matplotlib/artist.py", line 61, in draw_wrapper
draw(artist, renderer, *args, **kwargs)
File "/usr/lib/python2.7/dist-packages/matplotlib/figure.py", line 1165, in draw
self.canvas.draw_event(renderer)
File "/usr/lib/python2.7/dist-packages/matplotlib/backend_bases.py", line 1809, in draw_event
self.callbacks.process(s, event)
File "/usr/lib/python2.7/dist-packages/matplotlib/cbook.py", line 563, in process
proxy(*args, **kwargs)
File "/usr/lib/python2.7/dist-packages/matplotlib/cbook.py", line 430, in __call__
return mtd(*args, **kwargs)
File "/usr/lib/python2.7/dist-packages/matplotlib/animation.py", line 648, in _start
self._init_draw()
File "/usr/lib/python2.7/dist-packages/matplotlib/animation.py", line 1193, in _init_draw
self._draw_frame(next(self.new_frame_seq()))
File "/usr/lib/python2.7/dist-packages/matplotlib/animation.py", line 1214, in _draw_frame
for a in self._drawn_artists:
TypeError: 'NoneType' object is not iterable
What I got if that I need to tell Python to iterate thought some variable using the function animate().
I am not fluent with defining functions and I am stuck in this part of the code: I cannot understand where my code is wrong...
Some help about this?
Many thanks!!
To get rid of the error, set blit=False.
Apart the animation would currently not animate anything because for each frame you loop over all files. Instead you want to show one file per frame.
griblist = os.listdir("path")
def animate (i):
grib = griblist[i]
grbs=pygrib.open(grib)
lats, lons = grb.latlons()
x, y = m(lons, lats)
data = grb.values
cmap = plt.get_cmap('YlOrBr')
cs = m.pcolormesh(x,y,data,shading='flat',cmap=cmap)
plt.colorbar(cs,orientation='vertical', shrink=0.5)
plt.title('UV biological effective dose')
anim = animation.FuncAnimation(plt.gcf(), animate,
frames=len(griblist), interval=500)
plt.show()
I am writing a program in Python, using matplotlib to (among other things) run an animation showing a numerical solution to the time-dependent Schrodinger Equation.
Everything is working fine, but once an animation has finished running, I would like the window it was in to close itself. My way of doing this (shown below) works, but exceptions are thrown up which I cant seem to catch. It works fine for what I need it to do, but the error looks very messy.
I have an alternative method which works without throwing an error, but requires the user to manually close the window (unacceptable for my purposes). Can someone please point out what I am doing wrong, or suggest a better option?
A simplified version of the relevant parts of my code follows:
from matplotlib import animation as ani
from matplotlib import pyplot as plt
multiplier = 0
def get_data(): # some dummy data to animate
x = range(-10, 11)
global multiplier
y = [multiplier * i for i in x]
multiplier += 0.005
return x, y
class Schrodinger_Solver(object):
def __init__(self, xlim = (-10, 10), ylim = (-10, 10), num_frames = 200):
self.num_frames = num_frames
self.fig = plt.figure()
self.ax = self.fig.add_subplot(111, xlim = xlim, ylim = ylim)
self.p_line, = self.ax.plot([], [])
self.ani = ani.FuncAnimation(self.fig, self.animate_frame,
init_func = self.init_func,
interval = 1, frames = self.num_frames,
repeat = False, blit = True)
plt.show()
def animate_frame(self, framenum):
data = get_data()
self.p_line.set_data(data[0], data[1])
if framenum == self.num_frames - 1:
plt.close()
# closes the window when the last frame is reached,
# but exception is thrown. Comment out to avoid the error,
# but then the window needs manual closing
return self.p_line,
def init_func(self):
self.p_line.set_data([], [])
return self.p_line,
Schrodinger_Solver()
I am running Python 2.7.2 on windows 7, with matplotlib 1.1.0
Thanks in advance
EDIT:
exception and traceback as follows:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Python27\lib\lib-tk\Tkinter.py", line 1410, in __call__
return self.func(*args)
File "C:\Python27\lib\lib-tk\Tkinter.py", line 495, in callit
func(*args)
File "C:\Python27\lib\site-packages\matplotlib\backends\backend_tkagg.py", line 116, in _on_timer
TimerBase._on_timer(self)
File "C:\Python27\lib\site-packages\matplotlib\backend_bases.py", line 1092, in _on_timer
ret = func(*args, **kwargs)
File "C:\Python27\lib\site-packages\matplotlib\animation.py", line 315, in _step
still_going = Animation._step(self, *args)
File "C:\Python27\lib\site-packages\matplotlib\animation.py", line 177, in _step
self._draw_next_frame(framedata, self._blit)
File "C:\Python27\lib\site-packages\matplotlib\animation.py", line 197, in _draw_next_frame
self._post_draw(framedata, blit)
File "C:\Python27\lib\site-packages\matplotlib\animation.py", line 220, in _post_draw
self._blit_draw(self._drawn_artists, self._blit_cache)
File "C:\Python27\lib\site-packages\matplotlib\animation.py", line 240, in _blit_draw
ax.figure.canvas.blit(ax.bbox)
File "C:\Python27\lib\site-packages\matplotlib\backends\backend_tkagg.py", line 244, in blit
tkagg.blit(self._tkphoto, self.renderer._renderer, bbox=bbox, colormode=2)
File "C:\Python27\lib\site-packages\matplotlib\backends\tkagg.py", line 19, in blit
tk.call("PyAggImagePhoto", photoimage, id(aggimage), colormode, id(bbox_array))
TclError: this isn't a Tk application
Traceback (most recent call last):
File "C:\Python27\quicktest.py", line 44, in <module>
Schrodinger_Solver()
File "C:\Python27\quicktest.py", line 26, in __init__
plt.show()
File "C:\Python27\lib\site-packages\matplotlib\pyplot.py", line 139, in show
_show(*args, **kw)
File "C:\Python27\lib\site-packages\matplotlib\backend_bases.py", line 109, in __call__
self.mainloop()
File "C:\Python27\lib\site-packages\matplotlib\backends\backend_tkagg.py", line 69, in mainloop
Tk.mainloop()
File "C:\Python27\lib\lib-tk\Tkinter.py", line 325, in mainloop
_default_root.tk.mainloop(n)
AttributeError: 'NoneType' object has no attribute 'tk'
I can catch the second exception, the AttributeError, by a small change:
try: plt.show()
except AttributeError: pass
but the first part, the TclError, remains no matter what I try
I had the same problem a few minutes before... The reason was a very low interval-value in FuncAnimation. Your code tries to update the graphics evere 1 millisecond - quite fast! (1000 fps might not be needed). I tried interval=200 and the error was gone...
HTH
try with:
if framenum == self.num_frames - 1:
exit()
it works for me...
I am facing the exact same problem, and I managed to solve the issue by creating another Animation class. Essentially, I made two changes:
Write a custom class that overwrites the _stop and _step methods.
Riase an StopIteration error in the update function, instead of using plt.close. The exception will be caught and won't break the script.
Here is the code.
from matplotlib import animation as ani
from matplotlib import pyplot as plt
class FuncAnimationDisposable(ani.FuncAnimation):
def __init__(self, fig, func, **kwargs):
super().__init__(fig, func, **kwargs)
def _step(self, *args):
still_going = ani.Animation._step(self, *args)
if not still_going and self.repeat:
super()._init_draw()
self.frame_seq = self.new_frame_seq()
self.event_source.interval = self._repeat_delay
return True
elif (not still_going) and (not self.repeat):
plt.close() # this code stopped the window
return False
else:
self.event_source.interval = self._interval
return still_going
def _stop(self, *args):
# On stop we disconnect all of our events.
if self._blit:
self._fig.canvas.mpl_disconnect(self._resize_id)
self._fig.canvas.mpl_disconnect(self._close_id)
multiplier = 0
def get_data(): # some dummy data to animate
x = range(-10, 11)
global multiplier
y = [multiplier * i for i in x]
multiplier += 0.005
return x, y
class Schrodinger_Solver(object):
def __init__(self, xlim = (-10, 10), ylim = (-10, 10), num_frames = 200):
self.num_frames = num_frames
self.fig = plt.figure()
self.ax = self.fig.add_subplot(111, xlim = xlim, ylim = ylim)
self.p_line, = self.ax.plot([], [])
self.ani = FuncAnimationDisposable(self.fig, self.animate_frame,
init_func = self.init_func,
interval = 1, frames = self.num_frames,
repeat = False, blit = True)
plt.show()
def animate_frame(self, framenum):
data = get_data()
self.p_line.set_data(data[0], data[1])
if framenum == self.num_frames - 1:
raise StopIteration # instead of plt.close()
return self.p_line,
def init_func(self):
self.p_line.set_data([], [])
return self.p_line,
Schrodinger_Solver()
Schrodinger_Solver()
print(multiplier)
(The code snippet was tested with Python 3.7 and matplotlib 3.4.2.)