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

Skip to content

Commit 9cc4e30

Browse files
working PlayerAnimation with example
1 parent df926b7 commit 9cc4e30

File tree

3 files changed

+255
-15
lines changed

3 files changed

+255
-15
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 & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@
4040
DISPLAY_TEMPLATE, INCLUDED_FRAMES, JS_INCLUDE, STYLE_INCLUDE)
4141
from matplotlib import _api, cbook
4242

43+
from matplotlib.widgets import Button,Slider
44+
import mpl_toolkits.axes_grid1
45+
from functools import partial
4346

4447
_log = logging.getLogger(__name__)
4548

@@ -1764,3 +1767,106 @@ def _draw_frame(self, framedata):
17641767

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

0 commit comments

Comments
 (0)