8000 Added PlayerAnimation with example · matplotlib/matplotlib@642167d · GitHub
[go: up one dir, main page]

Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 642167d

Browse files
Added PlayerAnimation with example
1 parent eaa72c8 commit 642167d

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

43+
from matplotlib.widgets import Button,Slider
44+
import mpl_toolkits.axes_grid1
4345

4446
_log = logging.getLogger(__name__)
4547

@@ -1210,7 +1212,7 @@ def _on_resize(self, event):
12101212
def _end_redraw(self, event):
12111213
# Now that the redraw has happened, do the post draw flushing and
12121214
# blit handling. Then re-enable all of the original events.
1213-
self._post_draw(None, False)
1215+
self._post_draw(None, self._blit)
12141216
if not self._was_stopped:
12151217
self.event_source.start()
12161218
self._fig.canvas.mpl_disconnect(self._resize_id)
@@ -1764,3 +1766,106 @@ def _draw_frame(self, framedata):
17641766

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

0 commit comments

Comments
 (0)
0