Matplotlib animation - tuple object is not callable - python

having issues with the FuncAnimantion function in matplotlib. Code is as follows:
import time
from matplotlib import pyplot as plt
from matplotlib import animation
from ppbase import *
plt.ion()
#This is just essentially a stream of data
Paramater = PbaProObject("address of object")
fig = plt.figure()
ax = plt.axes(xlim=(0,2), ylim=(-90, 90))
line, = ax.plot([], [], lw=2)
def init():
line.set_data([], [])
return line,
def animate(Parameter):
x = time.time()
y = Parameter.ValueStr
line.set_data(x, y)
return line,
anim = animation.FuncAnimation(fig, animate(Parameter), init_func=init,
frames=200, interval=2, blit=True)
plt.show()
And the error is:
Traceback (most recent call last):
File "C:\Anaconda\lib\site-packages\matplotlib\backend_bases.py", line 1203, in _on_timer
ret = func(*args, **kwargs)
File "C:\Anaconda\lib\site-packages\matplotlib\animation.py", line 876, in _step
still_going = Animation._step(self, *args)
File "C:\Anaconda\lib\site-packages\matplotlib\animation.py", line 735, in _step self._draw_frame(framedata)
File "C:\Anaconda\lib\site-packages\matplotlib\animation.py", line 754, in _draw_next_frame self._draw_frame(framedata, self._blit)
File "C:\Anaconda\lib\site-packages\matplotlib\animation.py", line 1049, in _draw_frame self._drawn_artists = self._func(framedata, *self._args)
TypeError: 'tuple' object is not callable
Been reading around all morning and it seems commonly plt.plot is overwritten by a tuple, so I checked for that but don't think I have done it anywhere. I've also turned blit to false but that didn't help either. I do also want to interactively update the x-axis, I had line:
ax = plt.axes(xlim((x-10), (x+10)), ylim=(-90, 90))
in the animate function but took that out to see if it made any difference.
I think mostly the problems stem from not really understanding tuples too well. Also my understanding of the FuncAnimation function is that it calls the animate() each time it updates the plot - hence why I though that I could use it to update the axis' also. But this may not be the case.
Any help appreciated.

You need to pass in the function object not the result of the calling the function and you can pass a generator to frames (this might only work on 1.4.0+).
# turn your Parameter object into a generator
def param_gen(p):
yield p.ValueStr
def animate(p):
# get the data the is currently on the graph
old_x = line.get_xdata()
old_y = line.get_ydata()
# add the new data to the end of the old data
x = np.r_[old_x, time.time()]
y = np.r_[old_y, p]
# update the data in the line
line.set_data(x, y)
# return the line2D object so the blitting code knows what to redraw
return line,
anim = animation.FuncAnimation(fig, animate, init_func=init,
frames=param_gen(Parameter), interval=2, blit=True)
I also fixed a problem with your animation function and you should use 4 space indents, not 2.

Related

Why isn't FuncAnimation repeating when I'm put repeat = True as a parameter?

import matplotlib.image as mpimg
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib.widgets import Button
from matplotlib.widgets import Slider
fig = plt.figure()
image_list = ['downloads/20120831_194836_aia.lev1_euv_12s_4k.jpg', 'downloads/20120831_194936_aia.lev1_euv_12s_4k.jpg', 'downloads/20120831_195036_aia.lev1_euv_12s_4k.jpg']
list = []
for raw_image in image_list:
image1 = mpimg.imread(raw_image)
real_image1 = plt.imshow(image1)
list.append([real_image1])
def update_plot(t):
print(t)
return list[t]
anim = animation.FuncAnimation(fig, update_plot, repeat = True, interval=1, blit=False,
repeat_delay=200)
plt.show()
I am trying to create a func animation with the 3 jpg images in the list. After the program runs the 3 images 1 time, it gives me an error. When I print 't', it never resets to 0.
Error:
Traceback (most recent call last):
File "/Users/jamisenma/opt/anaconda3/lib/python3.7/site-packages/matplotlib/backend_bases.py", line 1194, in _on_timer
ret = func(*args, **kwargs)
File "/Users/jamisenma/opt/anaconda3/lib/python3.7/site-packages/matplotlib/animation.py", line 1447, in _step
still_going = Animation._step(self, *args)
File "/Users/jamisenma/opt/anaconda3/lib/python3.7/site-packages/matplotlib/animation.py", line 1173, in _step
self._draw_next_frame(framedata, self._blit)
File "/Users/jamisenma/opt/anaconda3/lib/python3.7/site-packages/matplotlib/animation.py", line 1192, in _draw_next_frame
self._draw_frame(framedata)
File "/Users/jamisenma/opt/anaconda3/lib/python3.7/site-packages/matplotlib/animation.py", line 1755, in _draw_frame
self._drawn_artists = self._func(framedata, *self._args)
File "/Users/jamisenma/Library/Application Support/JetBrains/PyCharmCE2020.1/scratches/scratch_59.py", line 19, in update_plot
return list[t]
IndexError: list index out of range
Does anyone know what the issue is?
Solved it. I had to add frames = len(list) as a parameter of FuncAnimation
You failed to provide into the expected MRE, and didn't do the expected initial debugging work. Therefore, I can't be sure.
However, my greatest suspicion is at the return from update_plot, which is using an argument you failed to show us -- and using that as subscript into a global sequence that shadows a pre-defined type.
Try debugging with this simple technique:
def update_plot(t):
print("ENTER update_plot; t =", t, "\n list =", list)
print(t)
return list[t]
I expect that, just before your point of failure, you will see that t >= len(list).
General hint: do not give a variable the same name as a built-in or predefined name. In particular, change list.

Matplotlib basemap multiple shapes animation exception: 'list' object has no attribute 'set_animated'

I'm using basemap to plot some polygons on a map and make an animation. When I animate one polygon and change its shape it works.
If I add second one I got exception:
Traceback (most recent call last):
File "...\Programs\Python\Python37\lib\site-packages\matplotlib\cbook\__init__.py", line 216, in process
func(*args, **kwargs)
File "...\Local\Programs\Python\Python37\lib\site-packages\matplotlib\animation.py", line 953, in _start
self._init_draw()
File "...\Local\Programs\Python\Python37\lib\site-packages\matplotlib\animation.py", line 1741, in _init_draw
a.set_animated(self._blit)
AttributeError: 'list' object has no attribute 'set_animated'
Traceback (most recent call last):
File "...\Local\Programs\Python\Python37\lib\site-packages\matplotlib\cbook\__init__.py", line 216, in process
func(*args, **kwargs)
File "...\Local\Programs\Python\Python37\lib\site-packages\matplotlib\animation.py", line 1269, in _handle_resize
self._init_draw()
File "..\Local\Programs\Python\Python37\lib\site-packages\matplotlib\animation.py", line 1741, in _init_draw
a.set_animated(self._blit)
AttributeError: 'list' object has no attribute 'set_animated'
My code:
input = pd.read_csv(filename)
data = pd.read_csv(datafilename)
m = Basemap(projection='spstere',boundinglat=-50,lon_0=0,resolution='l', area_thresh = 1000.0)
m.fillcontinents()
m.drawmapboundary()
lon =[]
lat = []
lon1=[]
lat1=[]
for j in range(0,100):
latlist = list()
latlist1 = list()
for i in range(0,361):
latlist.append(float(input.iloc[j][str(i)]))
latlist1.append(float(data.iloc[j][str(i)]))
lat.append(latlist)
lon.append(list(range(0,361)))
lat1.append(latlist1)
lon1.append(list(range(0,361)))
polys = []
x,y = m(lon[0],lat[0])
xy = list(zip(x,y))
poly=Polygon(xy,facecolor='None', alpha=1, edgecolor='green', linewidth=1)
x1,y1 = m(lon1[0],lat1[0])
xy1 = list(zip(x1,y1))
poly1=Polygon(xy1,facecolor='None', alpha=1, edgecolor='red', linewidth=1)
polys.append(poly)
polys.append(poly1)
def init():
plt.gca().add_patch(polys[0])
plt.gca().add_patch(polys[1])
return polys,
def animate(i):
print(i)
x,y = m(lon[i], lat[i])
xy=list(zip(x,y))
polys[0].set_xy(xy)
x1,y1 = m(lon1[i], lat1[i])
xy1=list(zip(x1,y1))
polys[1].set_xy(xy1)
return polys,
anim = animation.FuncAnimation(plt.gcf(), animate, init_func=init, frames=100, interval=500, blit=True)
plt.show()
If I return only one poly in functions and set blit=False it works.
If I return only one poly in functions and blit=True - only one polygon changes. How to animate two shapes in one animation with blitting?
You need to return an iterable of artists that you want to be updated. That is usually a tuple or list.
polys is a list of artists
poly, poly1 is a tuple of artists
[poly, poly1] is a list of artists
(poly, poly1) is a tuple of artists
etc.
But polys, is a tuple of one list. That one list is not an artist, which is what the error tells you.

Animating 3D scatter plot using Python mplotlib via serial data

I am trying to animate a 3D scatter plot using mplotlib in Python. I am able to graph the data and redraw every time, but this results in a frame rate of less than 1 FPS, and I need to scale to upwards of 30 FPS. When I run my code:
import serial
import numpy
import matplotlib.pyplot as plt #import matplotlib library
from mpl_toolkits.mplot3d import Axes3D
from drawnow import *
import matplotlib.animation
import time
ser = serial.Serial('COM7',9600,timeout=5)
ser.flushInput()
time.sleep(5)
ser.write(bytes(b's1000'))
x=list()
y=list()
z=list()
#plt.ion()
fig = plt.figure(figsize=(16,12))
ax = fig.add_subplot(111, projection="3d")
graph = ax.scatter(x,y,z, c='r',marker='o')
ax.set_xlim3d(-255, 255)
ax.set_ylim3d(-255, 255)
ax.set_zlim3d(-255, 255)
def generate():
while True:
try:
ser_bytes = ser.readline()
data = str(ser_bytes[0:len(ser_bytes)-2].decode("utf-8"))
xyz = data.split(", ")
dx = float(xyz[0])
dy = float(xyz[1])
dz = float(xyz[2].replace(";",""))
x.append(dx);
y.append(dy);
z.append(dz);
graph._offset3d(x,y,z, c='r',marker='o')
except:
print("Keyboard Interrupt")
ser.close()
break
return graph,
ani = matplotlib.animation.FuncAnimation(fig, generate(), interval=1, blit=True)
plt.show()
I get the following error:
Traceback (most recent call last):
File "C:\Users\bunti\AppData\Local\Programs\Python\Python36-32\lib\site-packages\matplotlib\cbook\__init__.py", line 388, in process
proxy(*args, **kwargs)
File "C:\Users\bunti\AppData\Local\Programs\Python\Python36-32\lib\site-packages\matplotlib\cbook\__init__.py", line 228, in __call__
return mtd(*args, **kwargs)
File "C:\Users\bunti\AppData\Local\Programs\Python\Python36-32\lib\site-packages\matplotlib\animation.py", line 1026, in _start
self._init_draw()
File "C:\Users\bunti\AppData\Local\Programs\Python\Python36-32\lib\site-packages\matplotlib\animation.py", line 1750, in _init_draw
self._draw_frame(next(self.new_frame_seq()))
File "C:\Users\bunti\AppData\Local\Programs\Python\Python36-32\lib\site-packages\matplotlib\animation.py", line 1772, in _draw_frame
self._drawn_artists = self._func(framedata, *self._args)
TypeError: 'tuple' object is not callable
Traceback (most recent call last):
File "C:\Users\bunti\AppData\Local\Programs\Python\Python36-32\lib\site-packages\matplotlib\cbook\__init__.py", line 388, in process
proxy(*args, **kwargs)
File "C:\Users\bunti\AppData\Local\Programs\Python\Python36-32\lib\site-packages\matplotlib\cbook\__init__.py", line 228, in __call__
return mtd(*args, **kwargs)
File "C:\Users\bunti\AppData\Local\Programs\Python\Python36-32\lib\site-packages\matplotlib\animation.py", line 1308, in _handle_resize
self._init_draw()
File "C:\Users\bunti\AppData\Local\Programs\Python\Python36-32\lib\site-packages\matplotlib\animation.py", line 1750, in _init_draw
self._draw_frame(next(self.new_frame_seq()))
File "C:\Users\bunti\AppData\Local\Programs\Python\Python36-32\lib\site-packages\matplotlib\animation.py", line 1772, in _draw_frame
self._drawn_artists = self._func(framedata, *self._args)
TypeError: 'tuple' object is not callable
I am receiving x, y, z coordinates from a lidar module connected to an Arduino, which sends the coordinates over serial to the Python script.
A possible problem with your code is that the animation function (generate) is not supposed to run in an infinite loop. Furthermore, you are supposed to pass a reference to that function to FuncAnimate, but instead you are calling the function (i.e. you need to omit the parentheses in FuncAnimation(..., generate, ...)
The second problem is that you are treating graph._offset3d as if it was a function, when it is merely a tuple of lists. You should assign a new tuple to it, instead of trying to call the function (which I believe is what the error message alludes to).
I simplified your code and the following works fine:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.animation as animation
def update_lines(num):
dx, dy, dz = np.random.random((3,)) * 255 * 2 - 255 # replace this line with code to get data from serial line
text.set_text("{:d}: [{:.0f},{:.0f},{:.0f}]".format(num, dx, dy, dz)) # for debugging
x.append(dx)
y.append(dy)
z.append(dz)
graph._offsets3d = (x, y, z)
return graph,
x = [0]
y = [0]
z = [0]
fig = plt.figure(figsize=(5, 5))
ax = fig.add_subplot(111, projection="3d")
graph = ax.scatter(x, y, z, color='orange')
text = fig.text(0, 1, "TEXT", va='top') # for debugging
ax.set_xlim3d(-255, 255)
ax.set_ylim3d(-255, 255)
ax.set_zlim3d(-255, 255)
# Creating the Animation object
ani = animation.FuncAnimation(fig, update_lines, frames=200, interval=50, blit=False)
plt.show()

Unable to save Matplotlib animations using ffmpeg

I have installed ffmpeg and added it to path, and checked it works in command prompt, but I am still unable to save animations. I have tried creating a sin wave that will animate when I don't try to save it, but throws an error when I do to demonstrate;
from __future__ import division
import numpy as numpy
from matplotlib import pyplot as pyplot
from matplotlib import animation
fig = pyplot.figure()
ax = pyplot.axes(xlim=(0, 2), ylim=(-2, 2))
line, = ax.plot([], [], lw=2)
def init():
line.set_data([], [])
return line,
def animate(i):
x = numpy.linspace(0, 2, 1000)
y = numpy.sin(2 * numpy.pi * (x - 0.01 * i))
line.set_data(x, y)
return line,
anim = animation.FuncAnimation(fig, animate, init_func=init, frames=200,
interval=20, blit=True, repeat=False)
FFwriter = animation.FFMpegWriter()
anim.save('animation_testing.mp4', writer = FFwriter)
pyplot.show()
When I try to run this it throws the same errors over and over again, I assume as it iterates through each frame;
Traceback (most recent call last):
File "c:\users\james\appdata\local\enthought\canopy\user\lib\site-
packages\matplotlib\backends\backend_wx.py", line 212, in _on_timer
TimerBase._on_timer(self)
File "c:\users\james\appdata\local\enthought\canopy\user\lib\site-
packages\matplotlib\backend_bases.py", line 1273, in _on_timer
ret = func(*args, **kwargs)
File "c:\users\james\appdata\local\enthought\canopy\user\lib\site-
packages\matplotlib\animation.py", line 910, in _step
still_going = Animation._step(self, *args)
File "c:\users\james\appdata\local\enthought\canopy\user\lib\site-
packages\matplotlib\animation.py", line 769, in _step
self._draw_next_frame(framedata, self._blit)
File "c:\users\james\appdata\local\enthought\canopy\user\lib\site-
packages\matplotlib\animation.py", line 787, in _draw_next_frame
self._pre_draw(framedata, blit)
File "c:\users\james\appdata\local\enthought\canopy\user\lib\site-
packages\matplotlib\animation.py", line 800, in _pre_draw
self._blit_clear(self._drawn_artists, self._blit_cache)
File "c:\users\james\appdata\local\enthought\canopy\user\lib\site-
packages\matplotlib\animation.py", line 840, in _blit_clear
a.figure.canvas.restore_region(bg_cache[a])
KeyError: <matplotlib.axes._subplots.AxesSubplot object at 0x0000000009C04DD8>
Since it mentioned an error in _blit_clear I tried changing blit to False in FuncAnimation, but then it wouldn't animate in the pyplot.show() when I didn't try to save.
I'm unsure as to where the error could be and so can't work out how to fix this.
I'm using Windows 10, python 2.7.6 and matplotlib version 1.4.2
Many Thanks!
Adding;
pyplot.switch_backend('backend')
where backend is either TkAgg or Qt4Agg solved my problem.
Thank you to ImportanceOfBeingErnest for solving this for me!

Gracefully stopping (not pausing) an animated plot in python (matplotlib)

I am running an animated scatter in a process. Everything is working fine, except that an exception is throw when I want to exit everything.
import multiprocessing as mp
import time
from collections import deque
def start_colored_scores(nb_channels):
q = mp.Queue()
process = mp.Process(target=colored_scores,args=(q,nb_channels,4000))
process.start()
return process,q
def colored_scores(q,nb_channels,size):
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, axes = plt.subplots(nrows=nb_channels,ncols=1,sharex=True,sharey=True)
plt.axis([-1.0,1.0,-1.0,1.0])
scats = [axe.scatter([0], [0], c="white", s=size) for axe in axes]
def animate(i):
scores = q.get()
if scores is None : # this is the external signal saying things should stop
plt.close()
return [axe.scatter([0], [0], c="white", s=size) for axe in axes]
scats = []
for score,axe in zip(scores,axes):
score = max(min(1,1-score),0)
scats.append(axe.scatter([0], [0], c=(1-score,0,score), s=size))
return scats
ani = animation.FuncAnimation(fig, animate, interval=1, blit=True)
plt.show()
For example, this is working fine:
_,q = start_colored_scores(2)
x = 0
right = 1
time_start = time.time()
while time.time()-time_start < 5:
if right==1 and x>1.0:
x = 1.0
right = -1
if right==-1 and x<0.0:
x = 0.0
right = 1
x+=right*0.02
q.put([x,1-x])
time.sleep(0.02)
q.put(None) # indicating I do not need plotting anymore
print "this is printed ... exception in the process ?"
The behavior is as I expect : scatters are displayed and animated for 5 seconds, then the program continues. The only issue is that an exception is thrown (I guess in the process) saying :
AttributeError: 'NoneType' object has no attribute 'tk'
Is there a way to do the exact same thing but avoiding the exception ? Or to catch this exception somewhere ?
You can catch that exception pretty easily:
def colored_scores(q,nb_channels,size):
import matplotlib.pyplot as plt
import matplotlib.animation as animation
fig, axes = plt.subplots(nrows=nb_channels,ncols=1,sharex=True,sharey=True)
plt.axis([-1.0,1.0,-1.0,1.0])
scats = [axe.scatter([0], [0], c="white", s=size) for axe in axes]
def animate(i):
scores = q.get()
if scores is None : # this is the external signal saying things should stop
plt.close()
return [axe.scatter([0], [0], c="white", s=size) for axe in axes]
scats = []
for score,axe in zip(scores,axes):
score = max(min(1,1-score),0)
scats.append(axe.scatter([0], [0], c=(1-score,0,score), s=size))
return scats
ani = animation.FuncAnimation(fig, animate, interval=1, blit=True)
try:
plt.show()
except AttributeError: # This will supress the exception
pass
However, once you get catch that one, you get a new one (at least on my system):
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 1489, in __call__
return self.func(*args)
File "/usr/lib/python2.7/lib-tk/Tkinter.py", line 536, in callit
func(*args)
File "/usr/lib/pymodules/python2.7/matplotlib/backends/backend_tkagg.py", line 141, in _on_timer
TimerBase._on_timer(self)
File "/usr/lib/pymodules/python2.7/matplotlib/backend_bases.py", line 1203, in _on_timer
ret = func(*args, **kwargs)
File "/usr/lib/pymodules/python2.7/matplotlib/animation.py", line 876, in _step
still_going = Animation._step(self, *args)
File "/usr/lib/pymodules/python2.7/matplotlib/animation.py", line 735, in _step
self._draw_next_frame(framedata, self._blit)
File "/usr/lib/pymodules/python2.7/matplotlib/animation.py", line 755, in _draw_next_frame
self._post_draw(framedata, blit)
File "/usr/lib/pymodules/python2.7/matplotlib/animation.py", line 778, in _post_draw
self._blit_draw(self._drawn_artists, self._blit_cache)
File "/usr/lib/pymodules/python2.7/matplotlib/animation.py", line 798, in _blit_draw
ax.figure.canvas.blit(ax.bbox)
File "/usr/lib/pymodules/python2.7/matplotlib/backends/backend_tkagg.py", line 353, in blit
tkagg.blit(self._tkphoto, self.renderer._renderer, bbox=bbox, colormode=2)
File "/usr/lib/pymodules/python2.7/matplotlib/backends/tkagg.py", line 20, in blit
tk.call("PyAggImagePhoto", photoimage, id(aggimage), colormode, id(bbox_array))
TclError: this isn't a Tk application
I can't find any way to supress that one. What you could do, is just terminate the subprocess, rather than try to send it a signal to shutdown:
proc,q = start_colored_scores(2)
x = 0
right = 1
time_start = time.time()
while time.time()-time_start < 5:
if right==1 and x>1.0:
x = 1.0
right = -1
if right==-1 and x<0.0:
x = 0.0
right = 1
x+=right*0.02
q.put([x,1-x])
time.sleep(0.02)
#q.put(None) # indicating I do not need plotting anymore
proc.terminate()
This is not as graceful as sending something through the queue (and doesn't allow for any additional clean-up in the sub-process, assuming you want it), but doesn't throw any exceptions.

Categories

Resources