Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 6e5456a

Browse files
Added PlayerAnimation with example
1 parent 3df1db3 commit 6e5456a

File tree

2 files changed

+157
-1
lines changed

2 files changed

+157
-1
lines changed
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
"""
2+
=====
3+
Decay
4+
=====
5+
6+
This example showcases:
7+
- using PlayerAnimation
8+
- using blitting
9+
- changing axes limits during an animation.
10+
"""
11+
12+
import itertools
13+
14+
import numpy as np
15+
import matplotlib.pyplot as plt
16+
17+
from matplotlib.animation import PlayerAnimation, FuncAnimation
18+
import math
19+
20+
def data_gen():
21+
for cnt in itertools.count():
22+
yield cnt
23+
24+
def init(val):
25+
global line
26+
print(f"init with val: {val}")
27+
if not "line" in globals():
28+
line, = ax.plot([], [], lw=2)
29+
ax.grid()
30+
ax.set_ylim(-1.1, 1.1)
31+
ax.set_xlim(0, 1 + math.floor(val / 10))
32+
line.set_data([], [])
33+
return [line]
34+
35+
fig, ax = plt.subplots()
36+
37+
def update_plot(i):
38+
# update the data
39+
xdata = np.linspace(-10, 10, 1000)
40+
ydata = np.sin(xdata + i*0.1)
41+
_, xmax = ax.get_xlim()
42+
new_xmax = 1 + math.floor(i / 10)
43+
if xmax != new_xmax:
44+
ax.set_xlim(0, new_xmax)
45+
ax.figure.canvas.draw_idle()
46+
line.set_data(xdata, ydata)
47+
48+
return [line]
49+
50+
animation = PlayerAnimation(fig=fig, func=update_plot, init_func=init, interval=100, blit=True, valstep=0.5)
51+
plt.show()

lib/matplotlib/animation.py

Lines changed: 106 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
DISPLAY_TEMPLATE, INCLUDED_FRAMES, JS_INCLUDE, STYLE_INCLUDE)
4141
from matplotlib import _api, cbook
4242
import matplotlib.colors as mcolors
43+
from matplotlib.widgets import Button,Slider
44+
import mpl_toolkits.axes_grid1
4345

4446
_log = logging.getLogger(__name__)
4547

@@ -1225,7 +1227,7 @@ def _on_resize(self, event):
12251227
def _end_redraw(self, event):
12261228
# Now that the redraw has happened, do the post draw flushing and
12271229
# blit handling. Then re-enable all of the original events.
1228-
self._post_draw(None, False)
1230+
self._post_draw(None, self._blit)
12291231
if not self._was_stopped:
12301232
self.event_source.start()
12311233
self._fig.canvas.mpl_disconnect(self._resize_id)
@@ -1779,3 +1781,106 @@ def _draw_frame(self, framedata):
17791781

17801782
for a in self._drawn_artists:
17811783
a.set_animated(self._blit)
1784+
1785+
class PlayerAnimation(FuncAnimation):
1786+
# inspired from https://stackoverflow.com/a/46327978/3949028
1787+
PLAY_SYMBOL = "$\u25B6$"
1788+
STOP_SYMBOL = "$\u25A0$"
1789+
PAUSE_SYMBOL = "$\u23F8$" #TODO use instead of STOP_SYMBOL, but doesn't work in Button.label
1790+
ONE_BACK_SYMBOL = "$\u29CF$"
1791+
ONE_FORWARD_SYMBOL = "$\u29D0$"
1792+
1793+
def __init__(self, func, init_func, min_value=0, max_value=100,
1794+
pos=(0.125, 0.92), valstep=1, **kwargs):
1795+
self.val = min_value
1796+
self.min = min_value
1797+
self.max = max_value
1798+
self.direction = 1
1799+
self.caller_func = func
1800+
self.valstep = valstep
1801+
self._player_initiated = False
1802+
#https://github.com/matplotlib/matplotlib/issues/17685
1803+
1804+
def init_func_wrapper():
1805+
return init_func(self.val)
1806+
1807+
super().__init__(func=self._func_wrapper, frames=self._frame_generator,
1808+
init_func=init_func_wrapper, **kwargs)
1809+
1810+
self._setup_player(pos)
1811+
1812+
def _setup_player(self, pos):
1813+
if not self._player_initiated:
1814+
self._player_initiated = True
1815+
playerax = self._fig.add_axes([pos[0], pos[1], 0.64, 0.04])
1816+
divider = mpl_toolkits.axes_grid1.make_axes_locatable(playerax)
1817+
sax = divider.append_axes("right", size="80%", pad=0.05)
1818+
ofax = divider.append_axes("right", size="100%", pad=0.05)
1819+
sliderax = divider.append_axes("right", size="500%", pad=0.07)
1820+
self.button_oneback = Button(playerax, label=self.ONE_BACK_SYMBOL, useblit=self._blit)
1821+
self.play_pause_button = Button(sax, label=self.STOP_SYMBOL, useblit=self._blit)
1822+
self.button_oneforward = Button(ofax, label=self.ONE_FORWARD_SYMBOL, useblit=self._blit)
1823+
self.button_oneback.on_clicked(self.onebackward)
1824+
self.play_pause_button.on_clicked(self.play_pause)
1825+
self.button_oneforward.on_clicked(self.oneforward)
1826+
self.slider = Slider(sliderax, '', self.min, self.max, valinit=self.min, valstep=self.valstep, useblit=self._blit)
1827+
self.slider.on_changed(self.set_pos)
1828+
1829+
def _frame_generator(self):
1830+
while True:
1831+
next = self.val + self.direction*self.valstep
1832+
if next >= self.min and next <= self.max:
1833+
self.val = next
1834+
print(f"yield: {self.val}")
1835+
yield self.val
1836+
else:
1837+
self.pause()
1838+
print(f"pause, yield: {self.val}")
1839+
yield self.val
1840+
1841+
def pause(self, event=None):
1842+
super().pause()
1843+
self.direction = 0
1844+
self.play_pause_button.label.set_text(self.PLAY_SYMBOL)
1845+
self.play_pause_button._draw()
1846+
1847+
def resume(self, event=None):
1848+
self.direction = 1
1849+
self.play_pause_button.label.set_text(self.STOP_SYMBOL)
1850+
self.play_pause_button._draw()
1851+
super().resume()
1852+
1853+
def play_pause(self, event=None):
1854+
if self.direction == 0:
1855+
self.resume()
1856+
else:
1857+
self.pause()
1858+
1859+
def oneforward(self, event=None):
1860+
self.direction = 1
1861+
self.trigger_step()
1862+
1863+
def onebackward(self, event=None):
1864+
self.direction = -1
1865+
self.trigger_step()
1866+
1867+
def set_pos(self, val):
1868+
if isinstance(self.valstep, int):
1869+
val = int(val) # slider gives float event if valstep is int
1870+
if self.val != val:
1871+
print(f"slider set_pos: {val}")
1872+
self.val = val
1873+
self.direction = 0
1874+
self.trigger_step()
1875+
1876+
def trigger_step(self):
1877+
for a in self._drawn_artists:
1878+
a.set_animated(True)
1879+
self._step()
1880+
self.pause()
1881+
1882+
def _func_wrapper(self, val):
1883+
print(f"player _func_wrapper: {val}")
1884+
self.slider.set_val(val)
1885+
return self.caller_func(self.val)
1886+

0 commit comments

Comments
 (0)