I'm a little hesitant about asking this, since there seem to be many "Exception in Tkinter callback" questions, but I cannot find one that fits the problem I have here.
I am trying to save an MP4 animation (of a percolation simulation) using matplotlib with ffmpeg. The code works fine on my home laptop, but not on my work PC. It also works fine if I replace the anim.save
line with plt.show()
, but I do want to save the animation. I'm using Python 3.5.2 on Ubuntu 17.04 (and I have ffmpeg installed).
Here is the error:
>>> Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.5/tkinter/__init__.py", line 1558, in __call__
return self.func(*args)
File "/usr/lib/python3.5/tkinter/__init__.py", line 604, in callit
func(*args)
File "/usr/lib/python3/dist-packages/matplotlib/backends/backend_tkagg.py", line 373, in idle_draw
self.draw()
File "/usr/lib/python3/dist-packages/matplotlib/backends/backend_tkagg.py", line 354, in draw
FigureCanvasAgg.draw(self)
File "/usr/lib/python3/dist-packages/matplotlib/backends/backend_agg.py", line 474, in draw
self.figure.draw(self.renderer)
File "/usr/lib/python3/dist-packages/matplotlib/artist.py", line 62, in draw_wrapper
draw(artist, renderer, *args, **kwargs)
File "/usr/lib/python3/dist-packages/matplotlib/figure.py", line 1165, in draw
self.canvas.draw_event(renderer)
File "/usr/lib/python3/dist-packages/matplotlib/backend_bases.py", line 1809, in draw_event
self.callbacks.process(s, event)
File "/usr/lib/python3/dist-packages/matplotlib/cbook.py", line 563, in process
proxy(*args, **kwargs)
File "/usr/lib/python3/dist-packages/matplotlib/cbook.py", line 430, in __call__
return mtd(*args, **kwargs)
File "/usr/lib/python3/dist-packages/matplotlib/animation.py", line 661, in _start
self._init_draw()
File "/usr/lib/python3/dist-packages/matplotlib/animation.py", line 1221, in _init_draw
self._draw_frame(next(self.new_frame_seq()))
StopIteration
The code producing the error is:
def percolate():
# initialize an instance of the Percolator class
perc = Percolator(1, 100, 0.1)
# initialize the image
fig, ax = plt.subplots()
im = plt.imshow(perc.states)
anim = animation.FuncAnimation(fig, perc.update, perc.update_iter, repeat=False, fargs=(im, ), save_count=perc.n**2)
anim.save("perc.mp4")
I can reproduce the code for the Percolator
class if necessary, but that part is working fine. It has two functions: update_iter
, a generator function that yields True
as long as the animation should continue, and update
, which takes (the result of the iterator and) im
as inputs, and its last two lines are
im.set_array(self.states)
return im,
UPDATE:
Here's an MWE.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation
class Percolator:
def __init__(self):
self.i = 0
self.states = np.zeros((10, 10))
self.end = False
def update(self, garbage=None, im=None):
self.i += 1
if self.i == 10:
self.end = True
im.set_array(self.states)
return im,
def update_iter(self):
while self.end == False:
yield True
def percolate():
perc = Percolator()
fig, ax = plt.subplots()
im = plt.imshow(perc.states)
anim = animation.FuncAnimation(fig, perc.update, perc.update_iter, repeat=False, \
fargs=(im, ), save_count=100)
anim.save("perc.gif", writer="imagemagick")
In this example, the Percolator
class doesn't do anything interesting - it sets up a 10x10 grid of 0s, and each call to its update
function sets the image to the same 10x10 grid.
If the frames
attribute of FuncAnimation is set to 50 (say), rather than to perc.update_iter
, then there is no error and the image is saved correctly. So the problem seems to be with my generator function. I want to use the generator function because I want to keep creating new frames until some condition on perc.states
is met - here, boringly, I've just asked it to keep going for 10 iterations.
System details: Python 3.5.3, matplotlib 2.0.0, Ubuntu 17.04.
UPDATE 2:
Same problem after upgrading to matplotlib 2.0.2. Also, printing some output along the way reveals that the error occurs at the end of the iterations. In fact, if update_iter
is changed to:
def update_iter(self):
print(self.end)
while self.end == False:
yield True
... then the output is:
False
False
False
False
False
False
False
False
False
False
False
True
>>> True
Exception in Tkinter callback
Traceback (most recent call last):
etc.