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

Skip to content

Commit e3c0951

Browse files
committed
Implemented style cycling using cycler.
* New rcparam 'axes.style_cycle' * Deprecate rcparam 'axes.color_cycle' * 'axes.style_cycle' can be a cycler object or string repr * Enables setting of just about any property
1 parent d08c627 commit e3c0951

2 files changed

Lines changed: 69 additions & 18 deletions

File tree

lib/matplotlib/axes/_base.py

Lines changed: 37 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import math
1010
from operator import itemgetter
1111

12+
from cycler import cycler
1213
import numpy as np
1314
from numpy import ma
1415

@@ -138,20 +139,25 @@ class _process_plot_var_args(object):
138139
def __init__(self, axes, command='plot'):
139140
self.axes = axes
140141
self.command = command
141-
self.set_color_cycle()
142+
self.set_style_cycle()
142143

143144
def __getstate__(self):
144145
# note: it is not possible to pickle a itertools.cycle instance
145146
return {'axes': self.axes, 'command': self.command}
146147

147148
def __setstate__(self, state):
148149
self.__dict__ = state.copy()
149-
self.set_color_cycle()
150-
151-
def set_color_cycle(self, clist=None):
152-
if clist is None:
153-
clist = rcParams['axes.color_cycle']
154-
self.color_cycle = itertools.cycle(clist)
150+
self.set_style_cycle()
151+
152+
def set_style_cycle(self, style_cycler=None):
153+
if style_cycler is None:
154+
style_cycler = rcParams['axes.style_cycle']
155+
if style_cycler is None and 'axes.color_cycle' in rcParams:
156+
clist = rcParams['axes.color_cycle']
157+
style_cycler = cycler('color', clist)
158+
self.style_cycler = itertools.cycle(style_cycler)
159+
# Make a copy
160+
self._style_keys = list(style_cycler.keys)
155161

156162
def __call__(self, *args, **kwargs):
157163

@@ -229,24 +235,37 @@ def _xy_from_xy(self, x, y):
229235
y = y[:, np.newaxis]
230236
return x, y
231237

238+
def _setdefaults(self, kw, kwargs):
239+
# Only advance the cycler if the cycler
240+
# has information that is not specified
241+
# in the supplied kw and kwargs dicts
242+
if any([kw.get(k, None) is None and kwargs.get(k, None) is None
243+
for k in self._style_keys]):
244+
default_dict = six.next(self.style_cycler)
245+
else:
246+
default_dict = None
247+
248+
for k in self._style_keys:
249+
if (default_dict is not None and
250+
kw.get(k, None) is None and
251+
kwargs.get(k, None) is None):
252+
kwargs[k] = kw[k] = default_dict[k]
253+
232254
def _makeline(self, x, y, kw, kwargs):
233255
kw = kw.copy() # Don't modify the original kw.
234256
kwargs = kwargs.copy()
235-
if kw.get('color', None) is None and kwargs.get('color', None) is None:
236-
kwargs['color'] = kw['color'] = six.next(self.color_cycle)
237-
# (can't use setdefault because it always evaluates
238-
# its second argument)
239-
seg = mlines.Line2D(x, y,
240-
**kw
241-
)
257+
self._setdefaults(kw, kwargs)
258+
seg = mlines.Line2D(x, y, **kw)
242259
self.set_lineprops(seg, **kwargs)
243260
return seg
244261

245262
def _makefill(self, x, y, kw, kwargs):
246-
try:
247-
facecolor = kw['color']
248-
except KeyError:
249-
facecolor = six.next(self.color_cycle)
263+
kw = kw.copy() # Don't modify the original kw.
264+
kwargs = kwargs.copy()
265+
# Might be problematic with fallback names such as
266+
# 'facecolor' and such. Possibly fixed by traitlets?
267+
self._setdefaults(kw, kwargs)
268+
facecolor = kw['color']
250269
seg = mpatches.Polygon(np.hstack((x[:, np.newaxis],
251270
y[:, np.newaxis])),
252271
facecolor=facecolor,

lib/matplotlib/rcsetup.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
from matplotlib.fontconfig_pattern import parse_fontconfig_pattern
2424
from matplotlib.colors import is_color_like
2525

26+
from cycler import cycler, Cycler
27+
2628
#interactive_bk = ['gtk', 'gtkagg', 'gtkcairo', 'qt4agg',
2729
# 'tkagg', 'wx', 'wxagg', 'cocoaagg', 'webagg']
2830
# The capitalized forms are needed for ipython at present; this may
@@ -299,6 +301,12 @@ def validate_color(s):
299301

300302
raise ValueError('%s does not look like a color arg%s' % (s, msg))
301303

304+
def deprecate_axes_colorcycle(value):
305+
warnings.warn("axes.color_cycle is deprecated. Use axes.style_cycle "
306+
"instead. Will be removed in 2.1.0")
307+
return validate_colorlist(value)
308+
309+
302310

303311
def validate_colorlist(s):
304312
'return a list of colorspecs'
@@ -310,6 +318,25 @@ def validate_colorlist(s):
310318
msg = "'s' must be of type [ string | list | tuple ]"
311319
raise ValueError(msg)
312320

321+
def validate_cycler(s):
322+
'return a cycler object from a string repr or the object itself'
323+
if isinstance(s, six.string_types):
324+
try:
325+
# TODO: We might want to rethink this...
326+
cycler_inst = eval(s, {'cycler': cycler, 'Cycler': Cycler})
327+
except BaseException as e:
328+
raise ValueError("'%s' is not a valid cycler construction: %s" %
329+
(s, e))
330+
elif isinstance(s, Cycler):
331+
cycler_inst = s
332+
else:
333+
raise ValueError("object was not a string or Cycler instance: %s" % s)
334+
335+
# TODO: apply validation to the cycled elements
336+
# probably doable via traitlets
337+
338+
return cycler_inst
339+
313340
def validate_stringlist(s):
314341
'return a list'
315342
if isinstance(s, six.string_types):
@@ -750,6 +777,11 @@ def __call__(self, s):
750777
'axes.color_cycle': [['b', 'g', 'r', 'c', 'm', 'y', 'k'],
751778
validate_colorlist], # cycle of plot
752779
# line colors
780+
# This entry can be either a cycler object or a
781+
# string repr of a cycler-object, which gets eval()'ed
782+
# to create the object.
783+
'axes.style_cycle': [cycler('color', 'bgrcmyk'),
784+
validate_cycler],
753785
'axes.xmargin': [0, ValidateInterval(0, 1,
754786
closedmin=True,
755787
closedmax=True)], # margin added to xaxis

0 commit comments

Comments
 (0)