I'm trying to animate a plot in mpl and I've decided the smartest way to do it is to add a number of axes dynamically using Gridspec. I provide a number of columns and rows that I want Gridspec to make Axes for on my canvas and then I can fill those axes with artists.
Since the number of axes is arbitrary and thus the number of artists is arbitrary, I thought it would work best to access the artists like so:
import matplotlib.pyplot as plt
fig = plt.figure(constrained_layout=False, figsize=(8,8), facecolor=bg_colour)
gs = fig.add_gridspec(nrows=nrows, ncols=ncols, hspace=0.1, wspace=0.1)
for i in range(nrows):
for j in range(ncols):
ax = plt.subplot(gs[i,j])
print(fig.axes[0].artists)
So the idea is that I can create all my axes dynamically and then I can also dynamically add artists to those axes by slicing fig.axes
using the appropriate index. Once I have my axes selected I can just do
fig.axes[0].plot([],[])
to add an empty artist to the right axes, which I can then animate with the update function in my code.
The problem is that no matter how I set up the creation of the figure, the axes and the artists, the fig.axes[index].artists
list is always empty. I don't understand how I can give the command to plot for my axes and then not have the artist show up in the list of all artists. Is .artists
not actually the container for all artists that I need to use? Is the thing I'm looking for stored somewhere else? Does mpl not actually allow for this kind of thing in the first place?
Full code
import numpy as np
import itertools
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib.ticker import AutoMinorLocator, MaxNLocator
def circle_points_x(period):
t = np.linspace(0,2*np.pi,500)
x = np.cos(period*t np.pi/2)
return x
def circle_points_y(period):
t = np.linspace(0,2*np.pi,500)
y = np.sin(period*t np.pi/2)
return y
# true distribution
ncols,nrows = (5,5)
bg_colour = np.array((84, 153, 215))/256
bg_colour = np.append(bg_colour,0.5)
fig = plt.figure(constrained_layout=False, figsize=(8,8), facecolor=bg_colour)
gs = fig.add_gridspec(nrows=nrows, ncols=ncols, hspace=0.1, wspace=0.1)
for i in range(nrows):
for j in range(ncols):
ax = plt.subplot(gs[i,j])
ax.axis('off')
def init():
for i in range(nrows):
for j in range(ncols):
x = circle_points_x(j 1)
y = circle_points_y(i 1)
fig.axes[i*nrows j].plot(x,y,lw=0.5,aa=True)
fig.axes[i*nrows j].scatter([x[0]],[y[0]])
print(fig.axes[0].artists)
return list(itertools.chain(*[i.artists for i in fig.axes]))
def update(frame):
for i in range(nrows):
for j in range(ncols):
# fig.axes[i*nrows j].artists[0] # TURN THIS ON TO SEE THE ERROR
pass
return list(itertools.chain(*[i.artists for i in fig.axes]))
ani = FuncAnimation(fig, update, frames=range(1,8),repeat=False,init_func=init, blit=True,interval=40)
# ani.save('histogram.gif', dpi=300 ,writer="ffmpeg")
plt.show()
CodePudding user response:
Maybe what you are looking for is ax.get_children()
, however it returns everything (lines, line collections, spines, legends maybe...), so you would have to filter the results somehow.
If you know in advance what kind of data you are plotting, here are a few location where Matplotlib separates the artists.
ax.lines
stores the lines created withax.plot
.ax.collections
stores the collections (line collections, poly collections) created withax.add_collection
,ax.plot_surface
,ax.contour
,ax.contourf
, ...ax.images
store the artists created withax.imshow
.ax.patches
contains artists created withax.bar
,ax.fill
, ....ax.tables
contains artists created withax.table
.