Python animation figure window cannot be closed automatically - python

When creating an animation using matplotlib.animation and saving it, an error appears when trying to close the figure window via plt.close:
Python version:
Python 2.7.12 |Anaconda custom (64-bit)| (default, Jul 2 2016, 17:42:40)
IPython 4.1.2 -- An enhanced Interactive Python
Currently I switched to using PyCharm 2017.1 Community Edition. The error message can be reproduced both directly in IPython and within PyCharm when running from %cpaste or %paste in IPython or running in PyCharm's interactive console using Shift+Alt+E. The movie encoder used is mencoder as integrated in mplayer, since this is the default one installed at my work place.
Note:
in IPython use plt.ion() first to turn on interactive mode (already switched on in PyCharm by default)
code exits without error in IPython when pasted using the middle mouse button directly into the IPython screen
code exits without error in IPython or PyCharm when typing all commands separately and also when pasting (%cpaste, %paste) all commands except for plt.close() and then typing plt.close() manually
code exits without error when replacing plt.close() with plt.clf(), but I need plt.close(), e.g. for creating animations in a loop with different parameters where the graph needs to be recreated from scratch
code exits without error when running the code in a script from start to end (non-interactive mode)
code exits without error when closing the figure window clicking with the mouse on the window button
code exits with error when inserting time.sleep(1) before plt.close(), so the problem likely does not relate to time conflicts in the code
A minimal, complete and (hopefully) verifiable example is given below:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
# animation function for random image data
def animate_random_data(i):
new_data = np.random.rand(10, 10)
# update the data
im.set_data(new_data)
# initialize the graph
first_data = np.random.rand(10,10)
im = plt.imshow(first_data,interpolation='none')
myfig = plt.gcf()
# create the animation and save it
ani = animation.FuncAnimation(myfig, animate_random_data, range(10),
interval=100)
ani.save('animation_random_data.mpg', writer='mencoder')
plt.close()
Error traceback (from PyCharm):
Traceback (most recent call last):
File "/opt/local/anaconda/anaconda-2.2.0/lib/python2.7/site-packages/matplotlib/backends/backend_qt5agg.py", line 176, in __draw_idle_agg
FigureCanvasAgg.draw(self)
File "/opt/local/anaconda/anaconda-2.2.0/lib/python2.7/site-packages/matplotlib/backends/backend_agg.py", line 474, in draw
self.figure.draw(self.renderer)
File "/opt/local/anaconda/anaconda-2.2.0/lib/python2.7/site-packages/matplotlib/artist.py", line 61, in draw_wrapper
draw(artist, renderer, *args, **kwargs)
File "/opt/local/anaconda/anaconda-2.2.0/lib/python2.7/site-packages/matplotlib/figure.py", line 1165, in draw
self.canvas.draw_event(renderer)
File "/opt/local/anaconda/anaconda-2.2.0/lib/python2.7/site-packages/matplotlib/backend_bases.py", line 1809, in draw_event
self.callbacks.process(s, event)
File "/opt/local/anaconda/anaconda-2.2.0/lib/python2.7/site-packages/matplotlib/cbook.py", line 563, in process
proxy(*args, **kwargs)
File "/opt/local/anaconda/anaconda-2.2.0/lib/python2.7/site-packages/matplotlib/cbook.py", line 430, in __call__
return mtd(*args, **kwargs)
File "/opt/local/anaconda/anaconda-2.2.0/lib/python2.7/site-packages/matplotlib/animation.py", line 652, in _start
self.event_source.add_callback(self._step)
AttributeError: 'NoneType' object has no attribute 'add_callback'
Although the program continues without error when closing the windows manually as written in the list above, it is an annoying bug (think of multiple animations in a loop). The error appears also for e.g. 1D line plots. Thanks for any help (and clarification on what this error message exactly means)!

The error comes from the animation still running while closing the figure. While in most cases it is automatically taken care of to stop the animation when closing the figure, it seems not to be the case in interactive mode.
A solution can be to explicitely stop the animation and delete it before closing the figure.
ani = animation.FuncAnimation(...)
ani.save(...)
ani.event_source.stop()
del ani
plt.close()

in this case you could just exit the program with exit()

Related

How do I get rid of attribute error in matplotlib animation

I want to turn a series of matplotlib figures into an animation. However, whatever I do, I always receive error. I use Enthought Canopy 1.6.2 and Python 2.7.13 on Windows 10.
I have tried using videofig package. While it was good, I could not manage to save the mp4 file. Also, I believe using the source directly, i.e., matplotlib animation package would be more versatile for future uses. I checked a few answers, including 1, 2 and 3, yet none of them solved my problem.
The function I call is structured as follows.
def some_plotter(self, path, start_value, image_array)
some_unrelated_fig_functions()
im=plt.savefig(path, animated=True)
image_array.append([im])
plt.close("all")
The main code is as follows:
import matplotlib.animation as animation
image_array=[]
while(something):
some_obj.some_plotter(path, start_value, image_array)
fig = plt.figure()
ani = animation.ArtistAnimation(fig, image_array, interval=50, blit=True, repeat_delay=1000)
I receive the following error:
Traceback (most recent call last):
File "C:\Users\kocac\AppData\Local\Enthought\Canopy\edm\envs\User\lib\site-packages\matplotlib\cbook__init__.py", line 387, in process
proxy(*args, **kwargs)
File "C:\Users\kocac\AppData\Local\Enthought\Canopy\edm\envs\User\lib\site-packages\matplotlib\cbook__init__.py", line 227, in call
return mtd(*args, **kwargs)
File "C:\Users\kocac\AppData\Local\Enthought\Canopy\edm\envs\User\lib\site-packages\matplotlib\animation.py", line 1026, in _start
self._init_draw()
File "C:\Users\kocac\AppData\Local\Enthought\Canopy\edm\envs\User\lib\site-packages\matplotlib\animation.py", line 1557, in _init_draw
artist.set_visible(False)
AttributeError: 'NoneType' object has no attribute 'set_visible'
I had more similar lines to this, yet updating Matplotlib, following the suggestion at 3 reduced the error lines to 4. Yet I cannot proceed any longer. Note that saved images are perfectly fine so I am probably not doing anything wrong in the image creation.
How can I get rid of these errors? Where am I going wrong?

I cannot call show() after close() with matplotlib in python

I'm new to Python (I used MATLAB before), and I find that I cannot call show() after close some figures by close(). My goal is closing figures freely and then show the rest plots at last. Could anyone help me? Thank you.
My system: Python 3.6 on Windows 10. The matplotlib version is 2.2.2. I run my code through Eclipse.
Here is the code:
# Original code
import matplotlib.pyplot as plt
figA = plt.figure('aa')
figB = plt.figure('bb')
plt.close('aa')
plt.plot([2,3],[1,1],color='green')
plt.show()
When I run it, I get the following error in the Eclipse console.
Traceback (most recent call last):
File "D:\a project for testing dionysus\test_pythonPractice.py", line 26, in
plt.show()
File "C:\Users\hanlin\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\pyplot.py", line 253, in show
return _show(*args, **kw)
File "C:\Users\hanlin\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\backend_bases.py", line 208, in show
cls.mainloop()
File "C:\Users\hanlin\AppData\Local\Programs\Python\Python36\lib\site-packages\matplotlib\backends_backend_tk.py", line 1073, in mainloop
Tk.mainloop()
File "C:\Users\hanlin\AppData\Local\Programs\Python\Python36\lib\tkinter__init__.py", line 557, in mainloop
_default_root.tk.mainloop(n)
AttributeError: 'NoneType' object has no attribute 'tk'
However, if I change the code to either of the following two versions, there is no error.
# Revised ver.1
import matplotlib.pyplot as plt
figA = plt.figure('aa')
figB = plt.figure('bb')
plt.close('bb')
plt.plot([2,3],[1,1],color='green')
plt.show()
or
# Revised ver.2
import matplotlib.pyplot as plt
figA = plt.figure('aa')
figB = plt.figure('bb')
plt.close('aa')
plt.plot([2,3],[1,1],color='green')
plt.show(block=False)
plt.pause(3)
From revised ver.1, my guess is that close() only works on the last added figure. If we remove the previous figure, there will be an "empty" element in the list recording those figures. But this assumption violates the revised ver.2... Does anyone know why and how to solve this problem? Thank you.
Thanks to #Mr.T and #DavidG, I figure it out. Now the code becomes
# Revised original code
import matplotlib
matplotlib.use("Qt5Agg")
import matplotlib.pyplot as plt
figA = plt.figure('aa')
figB = plt.figure('bb')
plt.close('aa')
plt.plot([2,3],[1,1],color='green')
plt.show()
The culprit is the backend (default: "TkAgg"), and I set it as "Qt5Agg" now. I install the package pyqt5 by
pip install pyqt5==5.10.1
Note that the pyqt5 version needs to be 5.10.1. The latest one (5.11.2) would cause another error. For details, please read the webpage.

How to get useful information from pycallgraph tracing?

In my question about tracking the python execution I was recommended to use pycallgraph so I decided to give it a try on following code:
#!/bin/env python
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 2, 100)
# The first call to plt.plot will automatically create
# the necessary figure and axes to achieve the desired plot.
plt.plot(x, x, label='linear')
# Subsequent calls to plt.plot re-use
# the current axes and each add another line.
plt.plot(x, x**2, label='quadratic')
plt.plot(x, x**3, label='cubic')
# Setting the title, legend, and axis labels also automatically
# use the current axes and set the title,
# create the legend, and label the axis respectively.
plt.xlabel('x label')
plt.ylabel('y label')
plt.title("Simple Plot")
plt.legend()
plt.show()
This code is from my another question where I've been asking about hierarchy in matplotlib, and I get the following answer:
If you're really keen to see how the auto creation is done - it's all
open source. You can see the call to plot() creates an Axes instance
by a call to gca() in the code here. This in turn calls gcf(), which
looks for a FigureManager (which is what actually maintains the
state). If one exists, it returns the figure it's managing, otherwise
it creates a new one using plt.figure(). Again, this process to some
degree inherits from matlab, where the initial call is usually figure
before any plotting operation.
First, I was trying pycallgraph with graphviz option, but it gives me following error:
$ pycallgraph graphviz -- matplotlib.py
libpath/shortest.c:324: triangulation failed
libpath/shortest.c:192: source point not in any triangle
Error: in routesplines, Pshortestpath failed
Segmentation fault
Traceback (most recent call last):
File "/usr/local/bin/pycallgraph", line 26, in <module>
exec(__file_content)
File "/usr/local/lib/python2.7/dist-packages/pycallgraph/pycallgraph.py", line 38, in __exit__
self.done()
File "/usr/local/lib/python2.7/dist-packages/pycallgraph/pycallgraph.py", line 81, in done
self.stop()
File "/usr/local/lib/python2.7/dist-packages/pycallgraph/pycallgraph.py", line 90, in generate
output.done()
File "/usr/local/lib/python2.7/dist-packages/pycallgraph/output/graphviz.py", line 112, in done
'code %(ret)i.' % locals())
pycallgraph.exceptions.PyCallGraphException: The command "dot -Tpng -opycallgraph.png /tmp/tmpObsZGK" failed with error code 35584.
Second I've tried to generate gephi format and it worked:
$ pycallgraph gephi -- matplotlib.py
When I've opened this in gephi I get huge graph (Nodes: 1062, Edges: 1362) which was almost useless and I cannot saw anything useful here. So I've tried limit the output:
$ pycallgraph --max-depth 5 gephi -- traceme.py
This gives me graph with 254 nodes and 251 edges which was basically also useless because I still cannot saw nothing useful here. So, I've decided to try following:
egrep -i 'gcf|gca|plot' pycallgraph.gdf
But it returns me nothing. Then I've started wondering what exactly is pycallgraph tracing and I've made this hello world:
#!/bin/env python
print "This line will be printed."
And I've run it with (graphviz):
pycallgraph graphviz -- hello_world.py
The output (read from png file that was generated) was:
__main__ ---> <module>
What tool can I use to get similar answer as I've referred? In another words, how can I know that this is the order of calling the functions: plot() -> gca() -> gcf()?
What is pycallgraph actually tracing?
Why hello world example is working with graphviz option but more complex example is working only with gephi option?
How to preserve the DOT format which is pycallgraph generating and passing to graphviz?

Matplotlib: ion(): Why draw() for GTK3Agg but pause(s) for Qt4Agg?

I was trying to update a figure in matplotlib after each time step of a simulation.
I have managed to do this with:
import time, math
import matplotlib
matplotlib.use(u'Qt4Agg') # use some backend
# (c.f. http://stackoverflow.com/a/21836184 for .use())
import matplotlib.pyplot as plt
plt.ion()
fig = plt.figure()
sub = fig.add_subplot(1, 1, 1)
x = [i/100. for i in range(-500, 500)]
sin = lambda x : [math.sin(i) for i in x]
axis, = sub.plot(x, sin(x), 'r')
shift = 0.1
while shift < 5:
shift += 0.01
x = [i+shift for i in x]
axis.set_ydata(sin(x))
plt.pause(0.0001) # was plt.draw()
During the course of coming up with this solution I was almost getting mad because the Qt4Agg backend did now work properly with the draw() command where now there is a plt.pause(0.0001) command.
Now it works, but I get the following warning:
``/usr/local/lib/python2.7/dist-packages/matplotlib/backend_bases.py:2399: MatplotlibDeprecationWarning: Using default event loop until function specific to this GUI is implemented
warnings.warn(str, mplDeprecation)``
I also tried the GTK3Agg backend, but I am not sure if the computers where we want to run the project support it. Most surprisingly this backend worked with plt.draw() (except for the fact that the window freezes) but throws the following warnings and errors when running it with plt.pause(0.0001):
/usr/local/lib/python2.7/dist-packages/matplotlib/backend_bases.py:2399: MatplotlibDeprecationWarning: Using default event loop until function specific to this GUI is implemented
warnings.warn(str, mplDeprecation)
Traceback (most recent call last):
File "subplot_demo.py", line 24, in <module>
plt.pause(0.0001) # was plt.draw()
File "/usr/local/lib/python2.7/dist-packages/matplotlib/pyplot.py", line 199, in pause
canvas.start_event_loop(interval)
File "/usr/local/lib/python2.7/dist-packages/matplotlib/backends/backend_gtk3.py", line 370, in start_event_loop
FigureCanvasBase.start_event_loop_default(self,timeout)
File "/usr/local/lib/python2.7/dist-packages/matplotlib/backend_bases.py", line 2407, in start_event_loop_default
self.flush_events()
File "/usr/local/lib/python2.7/dist-packages/matplotlib/backends/backend_gtk3.py", line 365, in flush_events
Gtk.main_iteration(True)
File "/usr/lib/python2.7/dist-packages/gi/types.py", line 43, in function
return info.invoke(*args, **kwargs)
TypeError: main_iteration() takes exactly 0 arguments (1 given)
I assume now that pause() is deprecated (as it proved also difficult to find some useful documentation about it) but still stick to it with the Qt4Agg backend as I am sure that PyQt4 will be installed on the target systems.
I now wonder: How do I correctly draw() with the Qt4Agg? The draw() call is not working, pause() deprecated - what can I do? (On a side note: What exactly went wrong in the pause()-call of the GTK3Agg backend?)
Some notes on versions:
Ubuntu: 12.04, Matplotlib: 1.4.3, Python: 2.7.3, GCC 4.6.3, PyQt: 4.8.1

Renderer problems using Matplotlib from within a script

I've narrowed down to this call:
fig.canvas.tostring_argb() #fig=matplotlib.pyplot.figure()
this function raises an AttributeError when I run the code as a python script.
AttributeError: 'FigureCanvasGTKAgg' object has no attribute 'renderer'
However, this code works properly if run in the ipython --pylab command line.
As far as I can tell from the documentation, the Agg renderer should work OK.
The context is that I'm trying to make a movie from figures, without saving the frames
to disk; as per this question. I'm using the approach that streams the pixel arrays
to ffmpeg (running as a separate process) to do this, I need the argb array of values from the frame.
Is there some configuration setting I can make to get matplotlib to work correctly from within a script?
Edit
Tried use('Agg') as per a comment; still fails; this is a minimal working example.
[dave#dave tools]$ python -c "import matplotlib; matplotlib.use('Agg'); import matplotlib.pyplot; fig=matplotlib.pyplot.figure(); fig.canvas.tostring_argb()"
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "/usr/lib64/python2.7/site-packages/matplotlib/backends/backend_agg.py", line 416, in tostring_argb
return self.renderer.tostring_argb()
AttributeError: FigureCanvasAgg instance has no attribute 'renderer'
I suspect that you have missed out the call to:
fig.canvas.draw()
before
fig.canvas.tostring_argb()
as
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot
fig=matplotlib.pyplot.figure()
fig.canvas.tostring_argb()
fails for me, but
import matplotlib
matplotlib.use('Agg')
import matplotlib.pyplot
fig=matplotlib.pyplot.figure()
fig.canvas.draw()
fig.canvas.tostring_argb()
works.
I ended up installing and using the WXAgg backend; the Agg,and default GTKAgg, didn't work for me.

Categories