|
40 | 40 | DISPLAY_TEMPLATE,INCLUDED_FRAMES,JS_INCLUDE,STYLE_INCLUDE)
|
41 | 41 | frommatplotlibimport_api,cbook
|
42 | 42 |
|
| 43 | +frommatplotlib.widgetsimportButton,Slider |
| 44 | +importmpl_toolkits.axes_grid1 |
| 45 | +fromfunctoolsimportpartial |
43 | 46 |
|
44 | 47 | _log=logging.getLogger(__name__)
|
45 | 48 |
|
@@ -1764,3 +1767,106 @@ def _draw_frame(self, framedata):
|
1764 | 1767 |
|
1765 | 1768 | forainself._drawn_artists:
|
1766 | 1769 | a.set_animated(self._blit)
|
| 1770 | + |
| 1771 | +classPlayerAnimation(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 | +definit_func_wrapper(): |
| 1791 | +returninit_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 | +ifnotself._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 | +whileTrue: |
| 1817 | +next=self.val+self.direction*self.valstep |
| 1818 | +ifnext>=self.minandnext<=self.max: |
| 1819 | +self.val=next |
| 1820 | +print(f"yield:{self.val}") |
| 1821 | +yieldself.val |
| 1822 | +else: |
| 1823 | +self.pause() |
| 1824 | +print(f"pause, yield:{self.val}") |
| 1825 | +yieldself.val |
| 1826 | + |
| 1827 | +defpause(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 | +defresume(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 | +defplay_pause(self,event=None): |
| 1840 | +ifself.direction==0: |
| 1841 | +self.resume() |
| 1842 | +else: |
| 1843 | +self.pause() |
| 1844 | + |
| 1845 | +defoneforward(self,event=None): |
| 1846 | +self.direction=1 |
| 1847 | +self.trigger_step() |
| 1848 | + |
| 1849 | +defonebackward(self,event=None): |
| 1850 | +self.direction=-1 |
| 1851 | +self.trigger_step() |
| 1852 | + |
| 1853 | +defset_pos(self,val): |
| 1854 | +ifisinstance(self.valstep,int): |
| 1855 | +val=int(val)# slider gives float event if valstep is int |
| 1856 | +ifself.val!=val: |
| 1857 | +print(f"slider set_pos:{val}") |
| 1858 | +self.val=val |
| 1859 | +self.direction=0 |
| 1860 | +self.trigger_step() |
| 1861 | + |
| 1862 | +deftrigger_step(self): |
| 1863 | +forainself._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 | +returnself.caller_func(self.val) |
| 1872 | + |