Skip to content

Commit e91c5a1

Browse files
committed
pyplot tracks current image at the figure and axes level;
fixes ticket 1656374 svn path=/trunk/matplotlib/; revision=7494
1 parent 47254fc commit e91c5a1

File tree

7 files changed

+85
-35
lines changed

7 files changed

+85
-35
lines changed

CHANGELOG

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
2009-08-15 Pyplot interface: the current image is now tracked at the
2+
figure and axes level, addressing tracker item 1656374. - EF
3+
14
2009-08-15 Docstrings are now manipulated with decorators defined
25
in a new module, docstring.py, thanks to Jason Coombs. - EF
36

boilerplate.py

+13-12
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Wrap the plot commands defined in axes. The code generated by this
2-
# file is pasted into pylab.py. We did try to do this the smart way,
2+
# file is pasted into pyplot.py. We did try to do this the smart way,
33
# with callable functions and new.function, but could never get the
44
# docstrings right for python2.2. See
55
# http://groups.google.com/group/comp.lang.python/browse_frm/thread/dcd63ec13096a0f6/1b14640f3a4ad3dc?#1b14640f3a4ad3dc
@@ -13,7 +13,7 @@
1313
import types
1414

1515
# import the local copy of matplotlib, not the installed one
16-
sys.path.insert(0, './lib')
16+
#sys.path.insert(0, './lib')
1717
from matplotlib.axes import Axes
1818
from matplotlib.cbook import dedent
1919

@@ -104,16 +104,16 @@ def %(func)s(%(argspec)s):
104104
)
105105

106106
cmappable = {
107-
'contour' : 'if %(ret)s._A is not None: gci._current = %(ret)s',
108-
'contourf': 'if %(ret)s._A is not None: gci._current = %(ret)s',
109-
'hexbin' : 'gci._current = %(ret)s',
110-
'scatter' : 'gci._current = %(ret)s',
111-
'pcolor' : 'gci._current = %(ret)s',
112-
'pcolormesh' : 'gci._current = %(ret)s',
113-
'imshow' : 'gci._current = %(ret)s',
114-
'spy' : 'gci._current = %(ret)s',
115-
'quiver' : 'gci._current = %(ret)s',
116-
'specgram' : 'gci._current = %(ret)s[-1]',
107+
'contour' : 'if %(ret)s._A is not None: sci(%(ret)s)',
108+
'contourf': 'if %(ret)s._A is not None: sci(%(ret)s)',
109+
'hexbin' : 'sci(%(ret)s)',
110+
'scatter' : 'sci(%(ret)s)',
111+
'pcolor' : 'sci(%(ret)s)',
112+
'pcolormesh': 'sci(%(ret)s)',
113+
'imshow' : 'sci(%(ret)s)',
114+
'spy' : 'sci(%(ret)s)',
115+
'quiver' : 'sci(%(ret)s)',
116+
'specgram' : 'sci(%(ret)s[-1])',
117117

118118
}
119119

@@ -233,3 +233,4 @@ def %(name)s():
233233
# add all the colormaps (autumn, hsv, ....)
234234
for name in cmaps:
235235
print _fmtcmap%locals()
236+

examples/pylab_examples/multi_image.py

+5-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
It also illustrates colorbar tick labelling with a multiplier.
66
'''
77

8-
from matplotlib.pyplot import figure, show, sci
8+
from matplotlib.pyplot import figure, show, axes, sci
99
from matplotlib import cm, colors
1010
from matplotlib.font_manager import FontProperties
1111
from numpy import amin, amax, ravel
@@ -68,9 +68,11 @@ def __call__(self, leader):
6868
# The colorbar is also based on this master image.
6969
fig.colorbar(images[0], cax, orientation='horizontal')
7070

71-
# We need the following only if we want to run this
71+
# We need the following only if we want to run this interactively and
72+
# modify the colormap:
7273

73-
sci(images[0])
74+
axes(ax[0]) # Return the current axes to the first one,
75+
sci(images[0]) # because the current image must be in current axes.
7476

7577
show()
7678

lib/matplotlib/axes.py

+22
Original file line numberDiff line numberDiff line change
@@ -838,6 +838,7 @@ def cla(self):
838838
self.tables = []
839839
self.artists = []
840840
self.images = []
841+
self._current_image = None # strictly for pyplot via _sci, _gci
841842
self.legend_ = None
842843
self.collections = [] # collection.Collection instances
843844

@@ -1309,6 +1310,27 @@ def get_yticklines(self):
13091310

13101311
#### Adding and tracking artists
13111312

1313+
def _sci(self, im):
1314+
"""
1315+
helper for :func:`~matplotlib.pyplot.sci`;
1316+
do not use elsewhere.
1317+
"""
1318+
if isinstance(im, matplotlib.contour.ContourSet):
1319+
if im.collections[0] not in self.collections:
1320+
raise ValueError(
1321+
"ContourSet must be in current Axes")
1322+
elif im not in self.images and im not in self.collections:
1323+
raise ValueError(
1324+
"Argument must be an image, collection, or ContourSet in this Axes")
1325+
self._current_image = im
1326+
1327+
def _gci(self):
1328+
"""
1329+
helper for :func:`~matplotlib.pyplot.gci`;
1330+
do not use elsewhere.
1331+
"""
1332+
return self._current_image
1333+
13121334
def has_data(self):
13131335
'''Return *True* if any artists have been added to axes.
13141336

lib/matplotlib/cbook.py

+7-1
Original file line numberDiff line numberDiff line change
@@ -1007,7 +1007,7 @@ def __setitem__(self, k, v):
10071007

10081008

10091009

1010-
class Stack:
1010+
class Stack(object):
10111011
"""
10121012
Implement a stack where elements can be pushed on and you can move
10131013
back and forth. But no pop. Should mimic home / back / forward
@@ -1023,6 +1023,12 @@ def __call__(self):
10231023
if not len(self._elements): return self._default
10241024
else: return self._elements[self._pos]
10251025

1026+
def __len__(self):
1027+
return self._elements.__len__()
1028+
1029+
def __getitem__(self, ind):
1030+
return self._elements.__getitem__(ind)
1031+
10261032
def forward(self):
10271033
'move the position forward and return the current element'
10281034
N = len(self._elements)

lib/matplotlib/figure.py

+12-1
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,17 @@ def sca(self, a):
950950
for func in self._axobservers: func(self)
951951
return a
952952

953+
def _gci(self):
954+
"""
955+
helper for :func:`~matplotlib.pyplot.gci`;
956+
do not use elsewhere.
957+
"""
958+
for ax in reversed(self._axstack):
959+
im = ax._gci()
960+
if im is not None:
961+
return im
962+
return None
963+
953964
def add_axobserver(self, func):
954965
'whenever the axes state change, func(self) will be called'
955966
self._axobservers.append(func)
@@ -1039,7 +1050,7 @@ def savefig(self, *args, **kwargs):
10391050
def colorbar(self, mappable, cax=None, ax=None, **kw):
10401051
"""
10411052
Create a colorbar for a ScalarMappable instance.
1042-
1053+
10431054
Documentation for the pylab thin wrapper:
10441055
%(colorbar_doc)s
10451056
"""

lib/matplotlib/pyplot.py

+23-18
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,10 @@ def rcdefaults():
129129
matplotlib.rcdefaults()
130130
draw_if_interactive()
131131

132-
# The current "image" (ScalarMappable) is tracked here on a
133-
# per-pylab-session basis:
132+
# The current "image" (ScalarMappable) is retrieved or set
133+
# only via the pyplot interface using the following two
134+
# functions:
135+
134136
def gci():
135137
"""
136138
Get the current :class:`~matplotlib.cm.ScalarMappable` instance
@@ -142,18 +144,20 @@ def gci():
142144
:func:`~matplotlib.pyplot.pcolor` and
143145
:func:`~matplotlib.pyplot.scatter` create
144146
:class:`~matplotlib.collections.Collection` instances.
147+
The current image is an attribute of the current axes, or the
148+
nearest earlier axes in the current figure that contains an
149+
image.
145150
"""
146-
return gci._current
147-
gci._current = None
148-
151+
return gcf()._gci()
149152

150153
def sci(im):
151154
"""
152155
Set the current image (target of colormap commands like
153156
:func:`~matplotlib.pyplot.jet`, :func:`~matplotlib.pyplot.hot` or
154-
:func:`~matplotlib.pyplot.clim`).
157+
:func:`~matplotlib.pyplot.clim`). The current image is an
158+
attribute of the current axes.
155159
"""
156-
gci._current = im
160+
gca()._sci(im)
157161

158162

159163
## Any Artist ##
@@ -1598,7 +1602,6 @@ def autogen_docstring(base):
15981602

15991603
## Plotting part 2: autogenerated wrappers for axes methods ##
16001604

1601-
16021605
# This function was autogenerated by boilerplate.py. Do not edit as
16031606
# changes will be lost
16041607
@autogen_docstring(Axes.acorr)
@@ -1830,7 +1833,7 @@ def contour(*args, **kwargs):
18301833
draw_if_interactive()
18311834
finally:
18321835
ax.hold(washold)
1833-
if ret._A is not None: gci._current = ret
1836+
if ret._A is not None: sci(ret)
18341837
return ret
18351838

18361839
# This function was autogenerated by boilerplate.py. Do not edit as
@@ -1848,7 +1851,7 @@ def contourf(*args, **kwargs):
18481851
draw_if_interactive()
18491852
finally:
18501853
ax.hold(washold)
1851-
if ret._A is not None: gci._current = ret
1854+
if ret._A is not None: sci(ret)
18521855
return ret
18531856

18541857
# This function was autogenerated by boilerplate.py. Do not edit as
@@ -1956,7 +1959,7 @@ def hexbin(x, y, C=None, gridsize=100, bins=None, xscale='linear', yscale='linea
19561959
draw_if_interactive()
19571960
finally:
19581961
ax.hold(washold)
1959-
gci._current = ret
1962+
sci(ret)
19601963
return ret
19611964

19621965
# This function was autogenerated by boilerplate.py. Do not edit as
@@ -2010,7 +2013,7 @@ def imshow(X, cmap=None, norm=None, aspect=None, interpolation=None, alpha=1.0,
20102013
draw_if_interactive()
20112014
finally:
20122015
ax.hold(washold)
2013-
gci._current = ret
2016+
sci(ret)
20142017
return ret
20152018

20162019
# This function was autogenerated by boilerplate.py. Do not edit as
@@ -2046,7 +2049,7 @@ def pcolor(*args, **kwargs):
20462049
draw_if_interactive()
20472050
finally:
20482051
ax.hold(washold)
2049-
gci._current = ret
2052+
sci(ret)
20502053
return ret
20512054

20522055
# This function was autogenerated by boilerplate.py. Do not edit as
@@ -2064,7 +2067,7 @@ def pcolormesh(*args, **kwargs):
20642067
draw_if_interactive()
20652068
finally:
20662069
ax.hold(washold)
2067-
gci._current = ret
2070+
sci(ret)
20682071
return ret
20692072

20702073
# This function was autogenerated by boilerplate.py. Do not edit as
@@ -2154,7 +2157,7 @@ def quiver(*args, **kw):
21542157
draw_if_interactive()
21552158
finally:
21562159
ax.hold(washold)
2157-
gci._current = ret
2160+
sci(ret)
21582161
return ret
21592162

21602163
# This function was autogenerated by boilerplate.py. Do not edit as
@@ -2190,7 +2193,7 @@ def scatter(x, y, s=20, c='b', marker='o', cmap=None, norm=None, vmin=None, vmax
21902193
draw_if_interactive()
21912194
finally:
21922195
ax.hold(washold)
2193-
gci._current = ret
2196+
sci(ret)
21942197
return ret
21952198

21962199
# This function was autogenerated by boilerplate.py. Do not edit as
@@ -2244,7 +2247,7 @@ def specgram(x, NFFT=256, Fs=2, Fc=0, detrend=mlab.detrend_none, window=mlab.win
22442247
draw_if_interactive()
22452248
finally:
22462249
ax.hold(washold)
2247-
gci._current = ret[-1]
2250+
sci(ret[-1])
22482251
return ret
22492252

22502253
# This function was autogenerated by boilerplate.py. Do not edit as
@@ -2262,7 +2265,7 @@ def spy(Z, precision=0, marker=None, markersize=None, aspect='equal', hold=None,
22622265
draw_if_interactive()
22632266
finally:
22642267
ax.hold(washold)
2265-
gci._current = ret
2268+
sci(ret)
22662269
return ret
22672270

22682271
# This function was autogenerated by boilerplate.py. Do not edit as
@@ -2628,3 +2631,5 @@ def spectral():
26282631
draw_if_interactive()
26292632

26302633

2634+
2635+

0 commit comments

Comments
 (0)