| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent eaa72c8 commit 642167d
2 files changed
| Original file line number | Diff line number | Diff 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() | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -40,6 +40,8 @@ | |||
| 40 | 40 | DISPLAY_TEMPLATE, INCLUDED_FRAMES, JS_INCLUDE, STYLE_INCLUDE) | |
| 41 | 41 | from matplotlib import _api, cbook | |
| 42 | 42 | ||
| 43 | + from matplotlib.widgets import Button,Slider | ||
| 44 | + import mpl_toolkits.axes_grid1 | ||
| 43 | 45 | ||
| 44 | 46 | _log = logging.getLogger(__name__) | |
| 45 | 47 | ||
@@ -1210,7 +1212,7 @@ def _on_resize(self, event): | |||
| 1210 | 1212 | def _end_redraw(self, event): | |
| 1211 | 1213 | # Now that the redraw has happened, do the post draw flushing and | |
| 1212 | 1214 | # blit handling. Then re-enable all of the original events. | |
| 1213 | - self._post_draw(None, False) | ||
| 1215 | + self._post_draw(None, self._blit) | ||
| 1214 | 1216 | if not self._was_stopped: | |
| 1215 | 1217 | self.event_source.start() | |
| 1216 | 1218 | self._fig.canvas.mpl_disconnect(self._resize_id) | |
@@ -1764,3 +1766,106 @@ def _draw_frame(self, framedata): | |||
| 1764 | 1766 | ||
| 1765 | 1767 | for a in self._drawn_artists: | |
| 1766 | 1768 | 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 | ||
| 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 | + | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments