Matplotlib多子图动画添加共享X轴描述/标题的实现问题
I’ve got a clean solution for your problem—instead of relying on subplot axes to add dynamic text, we’ll use the figure-level text object that stays anchored to the entire plot, updating its content every frame. Here’s how to implement it:
Key Idea
Instead of trying to hack subplot axes for shared text, we create a single text element directly on the figure itself. We’ll track this text by a unique label so we can update its content each frame, rather than creating new text objects every time. This ensures the text stays in a consistent, shared position (like the bottom center of the figure) and updates seamlessly with the animation.
Modified Code
Here’s the updated version of your code with this solution integrated:
import numpy as np import matplotlib.pyplot as plt from matplotlib.animation import ArtistAnimation, PillowWriter plt.rcParams['animation.html'] = 'jshtml' def make_grid(fig, X, step, description=None, labels=None, title_fmt="label: {}", cmap='gray', ncols=3, colors=None): L = len(X) nrows = -(-L // ncols) frame_plot = [] # Update or create the shared step text step_text = None # Look for existing text with our unique label for text in fig.texts: if text.get_label() == 'step_text': step_text = text break # Create text if it doesn't exist yet if not step_text: # Position at bottom center (adjust y-coordinate as needed) step_text = fig.text( 0.5, 0.02, f"step={step}", ha='center', va='center', fontsize=12, label='step_text' ) else: # Update text content for the current frame step_text.set_text(f"step={step}") frame_plot.append(step_text) # Create subplots as before for i in range(L): ax = plt.subplot(nrows, ncols, i + 1) im = ax.imshow(X[i].squeeze(), cmap=cmap, interpolation='none') if labels is not None: color = 'k' if colors is None else colors[i] ax.set_title(title_fmt.format(labels[i]), color=color) ax.set_xticks([]) ax.set_yticks([]) frame_plot.append(im) return frame_plot def animate_step(X): return X ** 2 n_splots = 6 X = np.random.random((n_splots,32,32,3)) Y = X X_t = [] for i in range(10): Y = animate_step(Y) X_t.append((Y, i)) # Initialize the figure first fig = plt.figure() frames = [] for X, step in X_t: frame = make_grid(fig, X, step, labels=range(n_splots), title_fmt="target: {}") frames.append(frame) anim = ArtistAnimation(fig, frames, interval=300, repeat_delay=8000, blit=True) plt.close() anim.save("test.gif", writer=PillowWriter()) anim
What Changed?
- Pass the figure to
make_grid: We now explicitly pass thefigobject so we can manage the global text element. - Reusable text object: We check for an existing text element with the label
step_text—if it exists, we update its content; if not, we create it once. This avoids cluttering the figure with duplicate text objects. - Add text to frame elements: The text object is added to the
frame_plotlist, soArtistAnimationknows to include it in the blitting/updating process.
Why This Is Better Than Your Temporary Fix
- Stable position: The text is anchored to the figure, not a specific subplot, so it won’t shift if you change the number of rows/columns in your grid.
- Cleaner code: No reliance on "last row middle axis" hacks—this is an intentional, maintainable approach.
- Seamless updates: Blitting works correctly because we’re updating the existing text object instead of creating new ones each frame.
内容的提问来源于stack exchange,提问作者Kurt Willis

