I am an absolute novice in Python and Jupyter, and also new to this forum! So it’s likely that I am missing something obvious.
I have produced an animation within JupyterLab which works fine. It produces an animation along with control buttons (play/stop etc.) as expected. At the same time, it also produces a second, static graphic which is useless to me. I don’t know where second graphic is coming from and how to get rid of it. I would appreciate a remedy, and also comments for imporving the code in general.
Here is my code:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
from IPython.display import HTML
fig, ax = plt.subplots()
x = np.arange(0, 2*np.pi, 0.1)
line, = ax.plot(x, np.sin(x))
z = x.size
def animate(i):
line.set_ydata(np.sin(x - 2*np.pi*i / z))
return line,
ani = animation.FuncAnimation(
fig, animate,
frames = z,
blit=True)
HTML(ani.to_jshtml())
This is done in Firefox in Linux. Here is the output of “jupyter --verion”:
I’ve seen this before. It stems, I think from the way modern Jupyter tries to evaluate a cell for an active plot and display it automatically. I cannot recall if I found a way way around out. I may be able to investigate more on Monday over at my animated_matplotlib-binder where I like to keep good examples. I also had come across a similar thing with a more complicated use of ipywidgets and wrapping it an an output widget helped. I don’t have time to explore if that may help here to absolutely control.
For now…
I can offer for now this work around. Your code will keep producing the issue. My code will at least only produce it once. When you run the code the second time, it just produces the plot with the controller widget.
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
from IPython.display import HTML
plt.rcParams["animation.html"] = "jshtml"
plt.ioff() #needed so the second time you run it you get only single plot
fig, ax = plt.subplots()
x = np.arange(0, 2*np.pi, 0.1)
line, = ax.plot(x, np.sin(x))
z = x.size
def animate(i):
line.set_ydata(np.sin(x - 2*np.pi*i / z))
return line,
ani = animation.FuncAnimation(
fig, animate,
frames = z,
blit=True)
ani
Sorry, I didn’t have time the other day to explore this better…
That static graphic corresponds to the last frame generated.
The more permanent solution is to close the current active plot view so that modern Jupyter doesn’t detect that active plot object and display it.
In practical terms for your example code, that means adding plt.close() in the plot generating code cell just before you call the display of the playback widget that you made for stepping through the calculated frames.
Here is your code adapted to do that by adding that one line:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import numpy as np
from IPython.display import HTML
fig, ax = plt.subplots()
x = np.arange(0, 2*np.pi, 0.1)
line, = ax.plot(x, np.sin(x))
z = x.size
def animate(i):
line.set_ydata(np.sin(x - 2*np.pi*i / z))
return line,
ani = animation.FuncAnimation(
fig, animate,
frames = z,
blit=True)
plt.close()
HTML(ani.to_jshtml())
Plus, note that this is related in a way to the fact you no longer need plt.show() in modern Jupyter, even though most examples still include it.
Glad you asked because it prompted me to update my animated_matplotlib_binder for where I show the examples generating the playback controller widget that lets you step through the animation frames.