Skip to content

Commit c353bc9

Browse files
committed
Add axes for controls by modifying gridspec of figure
Makes control placement compatible with tight_layout() method or constrained_layout option. Falls back on previous absolute placement method if the gridspec version fails for any reason.
1 parent 632d988 commit c353bc9

File tree

1 file changed

+63
-10
lines changed

1 file changed

+63
-10
lines changed

animatplot/animation.py

Lines changed: 63 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from matplotlib.animation import FuncAnimation, PillowWriter
2+
from matplotlib.gridspec import GridSpec
23
from matplotlib.widgets import Button, Slider
34
import matplotlib.pyplot as plt
45
import numpy as np
@@ -42,6 +43,7 @@ def __init__(self, blocks, timeline=None, fig=None):
4243
self.fig = plt.gcf() if fig is None else fig
4344
self._has_slider = False
4445
self._pause = False
46+
self._controls_gridspec_object = None
4547

4648
def animate(i):
4749
updates = []
@@ -58,6 +60,40 @@ def animate(i):
5860
interval=1000/self.timeline.fps
5961
)
6062

63+
@property
64+
def _controls_gridspec(self):
65+
if self._controls_gridspec_object is None:
66+
# make the bottom of the subplots grid lower to fit the controls in
67+
adjust_plot = {'bottom': 0.03}
68+
plt.subplots_adjust(**adjust_plot)
69+
70+
controls_height = 0.2
71+
72+
# get gridspec to adjust
73+
gs = (plt.gca().get_gridspec().get_topmost_subplotspec()
74+
.get_gridspec())
75+
nrows, ncols = gs.get_geometry()
76+
height_ratios = gs.get_height_ratios()
77+
78+
# update parameters with a new row
79+
if height_ratios is None:
80+
# if height_ratios is None, all rows on the original gridspec
81+
# are the same height
82+
height_ratios = [(1.-controls_height)/nrows
83+
for i in range(nrows)]
84+
else:
85+
height_ratios = [r*(1.-controls_height) for r in height_ratios]
86+
height_ratios.append(controls_height)
87+
gs._nrows += 1
88+
gs.set_height_ratios(height_ratios)
89+
90+
# make a sub-grid in the bottom row
91+
self._controls_gridspec_object = gs[-1, :].subgridspec(
92+
1,3, width_ratios=[.07, .65, .28], wspace=0., hspace=0.)
93+
gs.update()
94+
95+
return self._controls_gridspec_object
96+
6197
def toggle(self, ax=None):
6298
"""Creates a play/pause button to start/stop the animation
6399
@@ -67,11 +103,20 @@ def toggle(self, ax=None):
67103
The matplotlib axes to attach the button to.
68104
"""
69105
if ax is None:
70-
adjust_plot = {'bottom': .2}
71-
rect = [.78, .03, .1, .07]
72-
73-
plt.subplots_adjust(**adjust_plot)
74-
self.button_ax = plt.axes(rect)
106+
try:
107+
button_subplotspec = self._controls_gridspec[0, 2]
108+
button_gridspec = button_subplotspec.subgridspec(
109+
3, 3, width_ratios=[0.45, 0.45, 0.1],
110+
height_ratios=[.05, .5, .45], wspace=0., hspace=0.)
111+
self.button_ax = self.fig.add_subplot(button_gridspec[1,1])
112+
except:
113+
# editing the gridspec did not work for some reason, fall back to
114+
# subplots_adjust
115+
adjust_plot = {'bottom': .2}
116+
rect = [.78, .03, .1, .07]
117+
118+
plt.subplots_adjust(**adjust_plot)
119+
self.button_ax = plt.axes(rect)
75120
else:
76121
self.button_ax = ax
77122

@@ -113,11 +158,19 @@ def timeline_slider(self, text='Time', ax=None, valfmt=None, color=None):
113158
The color of the slider.
114159
"""
115160
if ax is None:
116-
adjust_plot = {'bottom': .2}
117-
rect = [.18, .05, .5, .03]
118-
119-
plt.subplots_adjust(**adjust_plot)
120-
self.slider_ax = plt.axes(rect)
161+
try:
162+
slider_subplotspec = self._controls_gridspec[0, 1]
163+
slider_gridspec = slider_subplotspec.subgridspec(
164+
3, 1, height_ratios=[.2, .2, .6], wspace=0.,
165+
hspace=0.)
166+
self.slider_ax = self.fig.add_subplot(slider_gridspec[1, 0])
167+
except:
168+
# editing the gridspec did not work for some reason, fall back to
169+
# subplots_adjust
170+
adjust_plot = {'bottom': .2}
171+
rect = [.18, .05, .5, .03]
172+
plt.subplots_adjust(**adjust_plot)
173+
self.slider_ax = plt.axes(rect)
121174
else:
122175
self.slider_ax = ax
123176

0 commit comments

Comments
 (0)