@@ -247,7 +247,7 @@ class MovieWriter(AbstractMovieWriter):
247
247
The format used in writing frame data, defaults to 'rgba'.
248
248
fig : `~matplotlib.figure.Figure`
249
249
The figure to capture data from.
250
- This must be provided by the sub-classes .
250
+ This must be provided by the subclasses .
251
251
"""
252
252
253
253
# Builtin writer subclasses additionally define the _exec_key and _args_key
@@ -1083,7 +1083,7 @@ def _pre_composite_to_white(color):
1083
1083
frame_number = 0
1084
1084
# TODO: Currently only FuncAnimation has a save_count
1085
1085
# attribute. Can we generalize this to all Animations?
1086
- save_count_list = [getattr (a , 'save_count ' , None )
1086
+ save_count_list = [getattr (a , '_save_count ' , None )
1087
1087
for a in all_anim ]
1088
1088
if None in save_count_list :
1089
1089
total_frames = None
@@ -1236,7 +1236,7 @@ def to_html5_video(self, embed_limit=None):
1236
1236
This saves the animation as an h264 video, encoded in base64
1237
1237
directly into the HTML5 video tag. This respects :rc:`animation.writer`
1238
1238
and :rc:`animation.bitrate`. This also makes use of the
1239
- `` interval`` to control the speed, and uses the `` repeat``
1239
+ * interval* to control the speed, and uses the * repeat*
1240
1240
parameter to decide whether to loop.
1241
1241
1242
1242
Parameters
@@ -1299,7 +1299,7 @@ def to_html5_video(self, embed_limit=None):
1299
1299
options = ['controls' , 'autoplay' ]
1300
1300
1301
1301
# If we're set to repeat, make it loop
1302
- if hasattr (self , 'repeat' ) and self . repeat :
1302
+ if getattr (self , '_repeat' , False ) :
1303
1303
options .append ('loop' )
1304
1304
1305
1305
return VIDEO_TAG .format (video = self ._base64_video ,
@@ -1320,17 +1320,18 @@ def to_jshtml(self, fps=None, embed_frames=True, default_mode=None):
1320
1320
embed_frames : bool, optional
1321
1321
default_mode : str, optional
1322
1322
What to do when the animation ends. Must be one of ``{'loop',
1323
- 'once', 'reflect'}``. Defaults to ``'loop'`` if ``self. repeat``
1324
- is True, otherwise ``'once'``.
1323
+ 'once', 'reflect'}``. Defaults to ``'loop'`` if the * repeat*
1324
+ parameter is True, otherwise ``'once'``.
1325
1325
"""
1326
1326
if fps is None and hasattr (self , '_interval' ):
1327
1327
# Convert interval in ms to frames per second
1328
1328
fps = 1000 / self ._interval
1329
1329
1330
1330
# If we're not given a default mode, choose one base on the value of
1331
- # the repeat attribute
1331
+ # the _repeat attribute
1332
1332
if default_mode is None :
1333
- default_mode = 'loop' if self .repeat else 'once'
1333
+ default_mode = 'loop' if getattr (self , '_repeat' ,
1334
+ False ) else 'once'
1334
1335
1335
1336
if not hasattr (self , "_html_representation" ):
1336
1337
# Can't open a NamedTemporaryFile twice on Windows, so use a
@@ -1394,13 +1395,12 @@ class TimedAnimation(Animation):
1394
1395
blit : bool, default: False
1395
1396
Whether blitting is used to optimize drawing.
1396
1397
"""
1397
-
1398
1398
def __init__ (self , fig , interval = 200 , repeat_delay = 0 , repeat = True ,
1399
1399
event_source = None , * args , ** kwargs ):
1400
1400
self ._interval = interval
1401
1401
# Undocumented support for repeat_delay = None as backcompat.
1402
1402
self ._repeat_delay = repeat_delay if repeat_delay is not None else 0
1403
- self .repeat = repeat
1403
+ self ._repeat = repeat
1404
1404
# If we're not given an event source, create a new timer. This permits
1405
1405
# sharing timers between animation objects for syncing animations.
1406
1406
if event_source is None :
@@ -1417,7 +1417,7 @@ def _step(self, *args):
1417
1417
# back.
1418
1418
still_going = super ()._step (* args )
1419
1419
if not still_going :
1420
- if self .repeat :
1420
+ if self ._repeat :
1421
1421
# Restart the draw loop
1422
1422
self ._init_draw ()
1423
1423
self .frame_seq = self .new_frame_seq ()
@@ -1437,6 +1437,8 @@ def _step(self, *args):
1437
1437
self .event_source .interval = self ._interval
1438
1438
return True
1439
1439
1440
+ repeat = _api .deprecate_privatize_attribute ("3.7" )
1441
+
1440
1442
1441
1443
class ArtistAnimation (TimedAnimation ):
1442
1444
"""
@@ -1593,7 +1595,7 @@ def init_func() -> iterable_of_artists
1593
1595
Additional arguments to pass to each call to *func*. Note: the use of
1594
1596
`functools.partial` is preferred over *fargs*. See *func* for details.
1595
1597
1596
- save_count : int, default: 100
1598
+ save_count : int, optional
1597
1599
Fallback for the number of values from *frames* to cache. This is
1598
1600
only used if the number of frames cannot be inferred from *frames*,
1599
1601
i.e. when it's an iterator without length or a generator.
@@ -1618,7 +1620,6 @@ def init_func() -> iterable_of_artists
1618
1620
Whether frame data is cached. Disabling cache might be helpful when
1619
1621
frames contain large objects.
1620
1622
"""
1621
-
1622
1623
def __init__ (self , fig , func , frames = None , init_func = None , fargs = None ,
1623
1624
save_count = None , * , cache_frame_data = True , ** kwargs ):
1624
1625
if fargs :
@@ -1631,7 +1632,7 @@ def __init__(self, fig, func, frames=None, init_func=None, fargs=None,
1631
1632
# Amount of framedata to keep around for saving movies. This is only
1632
1633
# used if we don't know how many frames there will be: in the case
1633
1634
# of no generator or in the case of a callable.
1634
- self .save_count = save_count
1635
+ self ._save_count = save_count
1635
1636
# Set up a function that creates a new iterable when needed. If nothing
1636
1637
# is passed in for frames, just use itertools.count, which will just
1637
1638
# keep counting from 0. A callable passed in for frames is assumed to
@@ -1651,19 +1652,31 @@ def iter_frames(frames=frames):
1651
1652
else :
1652
1653
self ._iter_gen = lambda : iter (frames )
1653
1654
if hasattr (frames , '__len__' ):
1654
- self .save_count = len (frames )
1655
+ self ._save_count = len (frames )
1656
+ if save_count is not None :
1657
+ _api .warn_external (
1658
+ f"You passed in an explicit { save_count = } "
1659
+ "which is being ignored in favor of "
1660
+ f"{ len (frames )= } ."
1661
+ )
1655
1662
else :
1656
1663
self ._iter_gen = lambda : iter (range (frames ))
1657
- self .save_count = frames
1658
-
1659
- if self .save_count is None :
1660
- # If we're passed in and using the default, set save_count to 100.
1661
- self .save_count = 100
1662
- else :
1663
- # itertools.islice returns an error when passed a numpy int instead
1664
- # of a native python int (https://bugs.python.org/issue30537).
1665
- # As a workaround, convert save_count to a native python int.
1666
- self .save_count = int (self .save_count )
1664
+ self ._save_count = frames
1665
+ if save_count is not None :
1666
+ _api .warn_external (
1667
+ f"You passed in an explicit { save_count = } which is being "
1668
+ f"ignored in favor of { frames = } ."
1669
+ )
1670
+ if self ._save_count is None and cache_frame_data :
1671
+ _api .warn_external (
1672
+ f"{ frames = !r} which we can infer the length of, "
1673
+ "did not pass an explicit *save_count* "
1674
+ f"and passed { cache_frame_data = } . To avoid a possibly "
1675
+ "unbounded cache, frame data caching has been disabled. "
1676
+ "To suppress this warning either pass "
1677
+ "`cache_frame_data=False` or `save_count=MAX_FRAMES`."
1678
+ )
1679
+ cache_frame_data = False
1667
1680
1668
1681
self ._cache_frame_data = cache_frame_data
1669
1682
@@ -1690,26 +1703,18 @@ def new_saved_frame_seq(self):
1690
1703
self ._old_saved_seq = list (self ._save_seq )
1691
1704
return iter (self ._old_saved_seq )
1692
1705
else :
1693
- if self .save_count is not None :
1694
- return itertools .islice (self .new_frame_seq (), self .save_count )
1695
-
1696
- else :
1706
+ if self ._save_count is None :
1697
1707
frame_seq = self .new_frame_seq ()
1698
1708
1699
1709
def gen ():
1700
1710
try :
1701
- for _ in range ( 100 ) :
1711
+ while True :
1702
1712
yield next (frame_seq )
1703
1713
except StopIteration :
1704
1714
pass
1705
- else :
1706
- _api .warn_deprecated (
1707
- "2.2" , message = "FuncAnimation.save has truncated "
1708
- "your animation to 100 frames. In the future, no "
1709
- "such truncation will occur; please pass "
1710
- "'save_count' accordingly." )
1711
-
1712
1715
return gen ()
1716
+ else :
1717
+ return itertools .islice (self .new_frame_seq (), self ._save_count )
1713
1718
1714
1719
def _init_draw (self ):
1715
1720
super ()._init_draw ()
@@ -1747,10 +1752,7 @@ def _draw_frame(self, framedata):
1747
1752
if self ._cache_frame_data :
1748
1753
# Save the data for potential saving of movies.
1749
1754
self ._save_seq .append (framedata )
1750
-
1751
- # Make sure to respect save_count (keep only the last save_count
1752
- # around)
1753
- self ._save_seq = self ._save_seq [- self .save_count :]
1755
+ self ._save_seq = self ._save_seq [- self ._save_count :]
1754
1756
1755
1757
# Call the func with framedata and args. If blitting is desired,
1756
1758
# func needs to return a sequence of any artists that were modified.
@@ -1776,3 +1778,5 @@ def _draw_frame(self, framedata):
1776
1778
1777
1779
for a in self ._drawn_artists :
1778
1780
a .set_animated (self ._blit )
1781
+
1782
+ save_count = _api .deprecate_privatize_attribute ("3.7" )
0 commit comments