Movatterモバイル変換


[0]ホーム

URL:


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

Sign up
Appearance settings

Commit212b0ff

Browse files
committed
Merge pull request#4738 from tacaswell/fix_repl_ensure_fig_unstale
MNT: overhaul stale handling
2 parents2ff12fb +25079bf commit212b0ff

File tree

10 files changed

+127
-51
lines changed

10 files changed

+127
-51
lines changed

‎examples/pylab_examples/system_monitor.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ def get_net():
2121
defget_stats():
2222
returnget_memory(),get_cpu(),get_net()
2323

24-
25-
# turn interactive mode on for dynamic updates. If you aren't in
26-
# interactive mode, you'll need to use a GUI event handler/timer.
27-
plt.ion()
28-
2924
fig,ax=plt.subplots()
3025
ind=np.arange(1,4)
26+
27+
# show the figure, but do not block
28+
plt.show(block=False)
29+
30+
3131
pm,pc,pn=plt.bar(ind,get_stats())
3232
centers=ind+0.5*pm.get_width()
3333
pm.set_facecolor('r')
@@ -44,10 +44,21 @@ def get_stats():
4444
foriinrange(200):# run for a little while
4545
m,c,n=get_stats()
4646

47+
# update the animated artists
4748
pm.set_height(m)
4849
pc.set_height(c)
4950
pn.set_height(n)
51+
52+
# ask the canvas to re-draw itself the next time it
53+
# has a chance.
54+
# For most of the GUI backends this adds an event to the queue
55+
# of the GUI frameworks event loop.
56+
fig.canvas.draw_idle()
5057
try:
58+
# make sure that the GUI framework has a chance to run its event loop
59+
# and clear any GUI events. This needs to be in a try/except block
60+
# because the default implemenation of this method is to raise
61+
# NotImplementedError
5162
fig.canvas.flush_events()
5263
exceptNotImplementedError:
5364
pass

‎lib/matplotlib/animation.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1030,17 +1030,18 @@ def __init__(self, fig, artists, *args, **kwargs):
10301030

10311031
def_init_draw(self):
10321032
# Make all the artists involved in *any* frame invisible
1033-
axes=[]
1033+
figs=set()
10341034
forfinself.new_frame_seq():
10351035
forartistinf:
10361036
artist.set_visible(False)
1037+
artist.set_animated(self._blit)
10371038
# Assemble a list of unique axes that need flushing
1038-
ifartist.axesnotinaxes:
1039-
axes.append(artist.axes)
1039+
ifartist.axes.figurenotinfigs:
1040+
figs.add(artist.axes.figure)
10401041

10411042
# Flush the needed axes
1042-
foraxinaxes:
1043-
ax.figure.canvas.draw()
1043+
forfiginfigs:
1044+
fig.canvas.draw()
10441045

10451046
def_pre_draw(self,framedata,blit):
10461047
'''
@@ -1155,6 +1156,8 @@ def _init_draw(self):
11551156
self._draw_frame(next(self.new_frame_seq()))
11561157
else:
11571158
self._drawn_artists=self._init_func()
1159+
forainself._drawn_artists:
1160+
a.set_animated(self._blit)
11581161

11591162
def_draw_frame(self,framedata):
11601163
# Save the data for potential saving of movies.
@@ -1167,3 +1170,5 @@ def _draw_frame(self, framedata):
11671170
# Call the func with framedata and args. If blitting is desired,
11681171
# func needs to return a sequence of any artists that were modified.
11691172
self._drawn_artists=self._func(framedata,*self._args)
1173+
forainself._drawn_artists:
1174+
a.set_animated(self._blit)

‎lib/matplotlib/artist.py

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,9 @@ def draw_wrapper(artist, renderer, *args, **kwargs):
6868
returndraw_wrapper
6969

7070

71-
def_stale_figure_callback(self):
72-
self.figure.stale=True
73-
74-
75-
def_stale_axes_callback(self):
76-
self.axes.stale=True
71+
def_stale_axes_callback(self,val):
72+
ifself.axes:
73+
self.axes.stale=val
7774

7875

7976
classArtist(object):
@@ -87,6 +84,7 @@ class Artist(object):
8784

8885
def__init__(self):
8986
self._stale=True
87+
self.stale_callback=None
9088
self._axes=None
9189
self.figure=None
9290

@@ -124,6 +122,7 @@ def __getstate__(self):
124122
# remove the unpicklable remove method, this will get re-added on load
125123
# (by the axes) if the artist lives on an axes.
126124
d['_remove_method']=None
125+
d['stale_callback']=None
127126
returnd
128127

129128
defremove(self):
@@ -222,7 +221,7 @@ def axes(self, new_axes):
222221

223222
self._axes=new_axes
224223
ifnew_axesisnotNoneandnew_axesisnotself:
225-
self.add_callback(_stale_axes_callback)
224+
self.stale_callback=_stale_axes_callback
226225

227226
returnnew_axes
228227

@@ -236,15 +235,16 @@ def stale(self):
236235

237236
@stale.setter
238237
defstale(self,val):
239-
# only trigger call-back stack on being marked as 'stale'
240-
# when not already stale
241-
# the draw process will take care of propagating the cleaning
242-
# process
243-
ifnot (self._stale==val):
244-
self._stale=val
245-
# only trigger propagation if marking as stale
246-
ifself._stale:
247-
self.pchanged()
238+
self._stale=val
239+
240+
# if the artist is animated it does not take normal part in the
241+
# draw stack and is not expected to be drawn as part of the normal
242+
# draw loop (when not saving) so do not propagate this change
243+
ifself.get_animated():
244+
return
245+
246+
ifvalandself.stale_callbackisnotNone:
247+
self.stale_callback(self,val)
248248

249249
defget_window_extent(self,renderer):
250250
"""
@@ -608,9 +608,19 @@ def set_figure(self, fig):
608608
609609
ACCEPTS: a :class:`matplotlib.figure.Figure` instance
610610
"""
611+
# if this is a no-op just return
612+
ifself.figureisfig:
613+
return
614+
# if we currently have a figure (the case of both `self.figure`
615+
# and `fig` being none is taken care of above) we then user is
616+
# trying to change the figure an artist is associated with which
617+
# is not allowed for the same reason as adding the same instance
618+
# to more than one Axes
619+
ifself.figureisnotNone:
620+
raiseRuntimeError("Can not put single artist in "
621+
"more than one figure")
611622
self.figure=fig
612623
ifself.figureandself.figureisnotself:
613-
self.add_callback(_stale_figure_callback)
614624
self.pchanged()
615625
self.stale=True
616626

@@ -804,9 +814,9 @@ def set_animated(self, b):
804814
805815
ACCEPTS: [True | False]
806816
"""
807-
self._animated=b
808-
self.pchanged()
809-
self.stale=True
817+
ifself._animated!=b:
818+
self._animated=b
819+
self.pchanged()
810820

811821
defupdate(self,props):
812822
"""

‎lib/matplotlib/axes/_base.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -475,7 +475,7 @@ def __setstate__(self, state):
475475
container=getattr(self,container_name)
476476
forartistincontainer:
477477
artist._remove_method=container.remove
478-
self.stale=True
478+
self._stale=True
479479

480480
defget_window_extent(self,*args,**kwargs):
481481
"""
@@ -2059,7 +2059,8 @@ def draw(self, renderer=None, inframe=False):
20592059
ifnotself.get_visible():
20602060
return
20612061
renderer.open_group('axes')
2062-
2062+
# prevent triggering call backs during the draw process
2063+
self._stale=True
20632064
locator=self.get_axes_locator()
20642065
iflocator:
20652066
pos=locator(self,renderer)

‎lib/matplotlib/backend_bases.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434

3535
from __future__import (absolute_import,division,print_function,
3636
unicode_literals)
37+
fromcontextlibimportcontextmanager
3738

3839
frommatplotlib.externalsimportsix
3940
frommatplotlib.externals.six.movesimportxrange
@@ -1690,6 +1691,13 @@ def __init__(self, figure):
16901691
self.mouse_grabber=None# the axes currently grabbing mouse
16911692
self.toolbar=None# NavigationToolbar2 will set me
16921693
self._is_saving=False
1694+
self._is_idle_drawing=False
1695+
1696+
@contextmanager
1697+
def_idle_draw_cntx(self):
1698+
self._is_idle_drawing=True
1699+
yield
1700+
self._is_idle_drawing=False
16931701

16941702
defis_saving(self):
16951703
"""
@@ -2012,7 +2020,9 @@ def draw_idle(self, *args, **kwargs):
20122020
"""
20132021
:meth:`draw` only if idle; defaults to draw but backends can overrride
20142022
"""
2015-
self.draw(*args,**kwargs)
2023+
ifnotself._is_idle_drawing:
2024+
withself._idle_draw_cntx():
2025+
self.draw(*args,**kwargs)
20162026

20172027
defdraw_cursor(self,event):
20182028
"""

‎lib/matplotlib/figure.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@
5151
docstring.interpd.update(projection_names=get_projection_names())
5252

5353

54+
def_stale_figure_callback(self,val):
55+
ifself.figure:
56+
self.figure.stale=val
57+
58+
5459
classAxesStack(Stack):
5560
"""
5661
Specialization of the Stack to handle all tracking of Axes in a Figure.
@@ -330,6 +335,7 @@ def __init__(self,
330335
xy=(0,0),width=1,height=1,
331336
facecolor=facecolor,edgecolor=edgecolor,
332337
linewidth=linewidth)
338+
333339
self._set_artist_props(self.patch)
334340
self.patch.set_aa(False)
335341

@@ -543,6 +549,7 @@ def suptitle(self, t, **kwargs):
543549
sup.remove()
544550
else:
545551
self._suptitle=sup
552+
546553
self.stale=True
547554
returnself._suptitle
548555

@@ -650,6 +657,8 @@ def figimage(self, X,
650657
self.set_size_inches(figsize,forward=True)
651658

652659
im=FigureImage(self,cmap,norm,xo,yo,origin,**kwargs)
660+
im.stale_callback=_stale_figure_callback
661+
653662
im.set_array(X)
654663
im.set_alpha(alpha)
655664
ifnormisNone:
@@ -910,6 +919,7 @@ def add_axes(self, *args, **kwargs):
910919
self.sca(a)
911920
a._remove_method=lambdaax:self.delaxes(ax)
912921
self.stale=True
922+
a.stale_callback=_stale_figure_callback
913923
returna
914924

915925
@docstring.dedent_interpd
@@ -999,6 +1009,7 @@ def add_subplot(self, *args, **kwargs):
9991009
self.sca(a)
10001010
a._remove_method=lambdaax:self.delaxes(ax)
10011011
self.stale=True
1012+
a.stale_callback=_stale_figure_callback
10021013
returna
10031014

10041015
defclf(self,keep_observers=False):
@@ -1046,8 +1057,10 @@ def draw(self, renderer):
10461057
# draw the figure bounding box, perhaps none for white figure
10471058
ifnotself.get_visible():
10481059
return
1049-
renderer.open_group('figure')
10501060

1061+
renderer.open_group('figure')
1062+
# prevent triggering call backs during the draw process
1063+
self._stale=True
10511064
ifself.get_tight_layout()andself.axes:
10521065
try:
10531066
self.tight_layout(renderer,**self._tight_parameters)
@@ -1119,12 +1132,11 @@ def draw_composite():
11191132
dsu.sort(key=itemgetter(0))
11201133
forzorder,a,func,argsindsu:
11211134
func(*args)
1122-
a.stale=False
11231135

11241136
renderer.close_group('figure')
1137+
self.stale=False
11251138

11261139
self._cachedRenderer=renderer
1127-
self.stale=False
11281140
self.canvas.draw_event(renderer)
11291141

11301142
defdraw_artist(self,a):
@@ -1274,6 +1286,7 @@ def text(self, x, y, s, *args, **kwargs):
12741286
def_set_artist_props(self,a):
12751287
ifa!=self:
12761288
a.set_figure(self)
1289+
a.stale_callback=_stale_figure_callback
12771290
a.set_transform(self.transFigure)
12781291

12791292
@docstring.dedent_interpd
@@ -1350,7 +1363,7 @@ def _gci(self):
13501363
returnNone
13511364

13521365
def__getstate__(self):
1353-
state=self.__dict__.copy()
1366+
state=super(Figure,self).__getstate__()
13541367
# the axobservers cannot currently be pickled.
13551368
# Additionally, the canvas cannot currently be pickled, but this has
13561369
# the benefit of meaning that a figure can be detached from one canvas,

‎lib/matplotlib/image.py

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,14 @@ class _AxesImageBase(martist.Artist, cm.ScalarMappable):
7171
iterpnames=interpolations_names
7272
# <end unused keys>
7373

74+
defset_cmap(self,cmap):
75+
super(_AxesImageBase,self).set_cmap(cmap)
76+
self.stale=True
77+
78+
defset_norm(self,norm):
79+
super(_AxesImageBase,self).set_norm(norm)
80+
self.stale=True
81+
7482
def__str__(self):
7583
return"AxesImage(%g,%g;%gx%g)"%tuple(self.axes.bbox.bounds)
7684

@@ -835,12 +843,12 @@ def set_filterrad(self, s):
835843
defset_norm(self,norm):
836844
ifself._AisnotNone:
837845
raiseRuntimeError('Cannot change colors after loading data')
838-
cm.ScalarMappable.set_norm(self,norm)
846+
super(NonUniformImage,self).set_norm(self,norm)
839847

840848
defset_cmap(self,cmap):
841849
ifself._AisnotNone:
842850
raiseRuntimeError('Cannot change colors after loading data')
843-
cm.ScalarMappable.set_cmap(self,cmap)
851+
super(NonUniformImage,self).set_cmap(self,cmap)
844852

845853

846854
classPcolorImage(martist.Artist,cm.ScalarMappable):
@@ -1033,6 +1041,7 @@ def get_extent(self):
10331041
defset_data(self,A):
10341042
"""Set the image array."""
10351043
cm.ScalarMappable.set_array(self,cbook.safe_masked_invalid(A))
1044+
self.stale=True
10361045

10371046
defset_array(self,A):
10381047
"""Deprecated; use set_data for consistency with other image types."""

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp