Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f3e8394

Browse files
committed
* Fix cycler advance bug (and added test)
* Normalize aliases * defaults cycling will ignore properties that are not relevant to the particular artist.
1 parent 8ce4797 commit f3e8394

4 files changed

Lines changed: 66 additions & 19 deletions

File tree

lib/matplotlib/axes/_base.py

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -225,21 +225,32 @@ def _xy_from_xy(self, x, y):
225225
y = y[:, np.newaxis]
226226
return x, y
227227

228-
def _getdefaults(self, *kwargs):
228+
def _getdefaults(self, ignore, *kwargs):
229229
"""
230230
Only advance the cycler if the cycler has information that
231-
is not specified in any of the supplied tuple of dicts
231+
is not specified in any of the supplied tuple of dicts.
232+
Ignore any keys specified in the `ignore` set.
232233
233-
Returns a dictionary of defaults if there are any
234+
Returns a copy of defaults dictionary if there are any
234235
keys that are not found in any of the supplied dictionaries.
235236
If the supplied dictionaries have non-None values for
236237
everything the property cycler has, then just return
237-
an empty dictionary.
238-
239-
"""
240-
if any([all([kw.get(k, None) is None for kw in kwargs])
241-
for k in self._prop_keys]):
242-
default_dict = six.next(self.prop_cycler)
238+
an empty dictionary. Ignored keys are excluded from the
239+
returned dictionary.
240+
241+
"""
242+
prop_keys = self._prop_keys
243+
if ignore is None:
244+
ignore = set([])
245+
prop_keys = prop_keys - ignore
246+
247+
if any(all(kw.get(k, None) is None for kw in kwargs)
248+
for k in prop_keys):
249+
# Need to copy this dictionary or else the next time around
250+
# in the cycle, the dictionary could be missing entries.
251+
default_dict = six.next(self.prop_cycler).copy()
252+
for p in ignore:
253+
default_dict.pop(p, None)
243254
else:
244255
default_dict = {}
245256
return default_dict
@@ -252,14 +263,14 @@ def _setdefaults(self, defaults, *kwargs):
252263
253264
"""
254265
for k in defaults:
255-
if all([kw.get(k, None) is None for kw in kwargs]):
266+
if all(kw.get(k, None) is None for kw in kwargs):
256267
for kw in kwargs:
257268
kw[k] = defaults[k]
258269

259270
def _makeline(self, x, y, kw, kwargs):
260271
kw = kw.copy() # Don't modify the original kw.
261272
kwargs = kwargs.copy()
262-
default_dict = self._getdefaults(kw, kwargs)
273+
default_dict = self._getdefaults(None, kw, kwargs)
263274
self._setdefaults(default_dict, kw, kwargs)
264275
seg = mlines.Line2D(x, y, **kw)
265276
self.set_lineprops(seg, **kwargs)
@@ -269,11 +280,26 @@ def _makefill(self, x, y, kw, kwargs):
269280
kw = kw.copy() # Don't modify the original kw.
270281
kwargs = kwargs.copy()
271282

283+
# Ignore 'marker'-related properties as they aren't Polygon
284+
# properties, but they are Line2D properties, and so they are
285+
# likely to appear in the default cycler construction.
286+
# This is done here to the defaults dictionary as opposed to the
287+
# other two dictionaries because we do want to capture when a
288+
# *user* explicitly specifies a marker which should be an error.
289+
# We also want to prevent advancing the cycler if there are no
290+
# defaults needed after ignoring the given properties.
291+
ignores = set(['marker', 'markersize', 'markeredgecolor',
292+
'markerfacecolor', 'markeredgewidth'])
293+
# Also ignore anything provided by *kwargs*.
294+
for k, v in six.iteritems(kwargs):
295+
if v is not None:
296+
ignores.add(k)
297+
272298
# Only using the first dictionary to use as basis
273299
# for getting defaults for back-compat reasons.
274300
# Doing it with both seems to mess things up in
275301
# various places (probably due to logic bugs elsewhere).
276-
default_dict = self._getdefaults(kw)
302+
default_dict = self._getdefaults(ignores, kw)
277303
self._setdefaults(default_dict, kw)
278304

279305
# Looks like we don't want "color" to be interpreted to
@@ -285,12 +311,9 @@ def _makefill(self, x, y, kw, kwargs):
285311
facecolor = kw.get('color', None)
286312

287313
# Throw out 'color' as it is now handled as a facecolor
288-
# Need to copy this dictionary or else the next time around
289-
# in the cycle, the dictionary will be missing this entry
290-
# completely!
291-
default_dict = default_dict.copy()
292314
default_dict.pop('color', None)
293315

316+
294317
# To get other properties set from the cycler
295318
# modify the kwargs dictionary.
296319
self._setdefaults(default_dict, kwargs)

lib/matplotlib/rcsetup.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -593,8 +593,6 @@ def __call__(self, s):
593593
'markeredgewidth': validate_floatlist,
594594
'markeredgecolor': validate_colorlist,
595595
'alpha': validate_floatlist,
596-
# No, this isn't a "property", but how long do you think it
597-
# will be before someone requests support for cycling markers?
598596
'marker': validate_stringlist,
599597
}
600598
_prop_aliases = {
@@ -621,7 +619,9 @@ def _cycler_wrap(prop, vals):
621619
if validator is None:
622620
raise TypeError("Unknown artist property: %s" % prop)
623621
vals = validator(vals)
624-
return cycler(prop, vals)
622+
# We will normalize the property names as well to reduce
623+
# the amount of alias handling code elsewhere.
624+
return cycler(norm_prop, vals)
625625

626626
def validate_cycler(s):
627627
'return a cycler object from a string repr or the object itself'
55.7 KB
Loading

lib/matplotlib/tests/test_cycles.py

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,30 @@ def test_fillcycle_basic():
8080
ax.legend(loc='upper left')
8181

8282

83+
@image_comparison(baseline_images=['fill_cycle_ignore'], remove_text=True,
84+
extensions=['png'])
85+
def test_fillcycle_ignore():
86+
fig = plt.figure()
87+
ax = fig.add_subplot(111)
88+
ax.set_prop_cycle(cycler('color', ['r', 'g', 'y']) +
89+
cycler('hatch', ['xx', 'O', '|-']) +
90+
cycler('marker', ['.', '*', 'D']))
91+
xs = np.arange(10)
92+
ys = 0.25 * xs**.5 + 2
93+
# Should not advance the cycler, even though there is an
94+
# unspecified property in the cycler "marker".
95+
# "marker" is not a Polygon property, and should be ignored.
96+
ax.fill(xs, ys, 'r', hatch='xx', label='red, xx')
97+
ys = 0.45 * xs**.5 + 3
98+
# Allow the cycler to advance, but specify some properties
99+
ax.fill(xs, ys, hatch='O', label='red, circle')
100+
ys = 0.65 * xs**.5 + 4
101+
ax.fill(xs, ys, label='green, circle')
102+
ys = 0.85 * xs**.5 + 5
103+
ax.fill(xs, ys, label='yellow, cross')
104+
ax.legend(loc='upper left')
105+
106+
83107
if __name__ == '__main__':
84108
import nose
85109
nose.runmodule(argv=['-s', '--with-doctest'], exit=False)

0 commit comments

Comments
 (0)