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

Appearance settings

Commit e524354

Browse filesBrowse files
committed
API/ENH: Color cycle handling
API/ENH: Color cycle handling
1 parent 9f83d53 commit e524354
Copy full SHA for e524354

File tree

Expand file treeCollapse file tree

11 files changed

+152
-52
lines changed
Filter options
Expand file treeCollapse file tree

11 files changed

+152
-52
lines changed

‎examples/pylab_examples/color_demo.py

Copy file name to clipboardExpand all lines: examples/pylab_examples/color_demo.py
+6-3Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#!/usr/bin/env python
22
"""
3-
matplotlib gives you 4 ways to specify colors,
3+
matplotlib gives you 5 ways to specify colors,
44
55
1) as a single letter string, ala MATLAB
66
@@ -11,6 +11,9 @@
1111
4) as a string representing a floating point number
1212
from 0 to 1, corresponding to shades of gray.
1313
14+
5) as a special color "Cn", where n is a number 0-9 specifying the
15+
nth color in the currently active color cycle.
16+
1417
See help(colors) for more info.
1518
"""
1619
import matplotlib.pyplot as plt
@@ -20,8 +23,8 @@
2023
#subplot(111, facecolor='#ababab')
2124
t = np.arange(0.0, 2.0, 0.01)
2225
s = np.sin(2*np.pi*t)
23-
plt.plot(t, s, 'y')
24-
plt.xlabel('time (s)', color='r')
26+
plt.plot(t, s, 'C1')
27+
plt.xlabel('time (s)', color='C1')
2528
plt.ylabel('voltage (mV)', color='0.5') # grayscale color
2629
plt.title('About as silly as it gets, folks', color='#afeeee')
2730
plt.show()

‎lib/matplotlib/axes/_axes.py

Copy file name to clipboardExpand all lines: lib/matplotlib/axes/_axes.py
+35-16Lines changed: 35 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from matplotlib.externals import six
55
from matplotlib.externals.six.moves import reduce, xrange, zip, zip_longest
66

7+
import itertools
78
import math
89
import warnings
910

@@ -2457,7 +2458,7 @@ def pie(self, x, explode=None, labels=None, colors=None,
24572458
Call signature::
24582459
24592460
pie(x, explode=None, labels=None,
2460-
colors=('b', 'g', 'r', 'c', 'm', 'y', 'k', 'w'),
2461+
colors=None,
24612462
autopct=None, pctdistance=0.6, shadow=False,
24622463
labeldistance=1.1, startangle=None, radius=None,
24632464
counterclock=True, wedgeprops=None, textprops=None,
@@ -2477,7 +2478,8 @@ def pie(self, x, explode=None, labels=None, colors=None,
24772478
24782479
*colors*: [ *None* | color sequence ]
24792480
A sequence of matplotlib color args through which the pie chart
2480-
will cycle.
2481+
will cycle. If `None`, will use the colors in the currently
2482+
active cycle.
24812483
24822484
*labels*: [ *None* | len(x) sequence of strings ]
24832485
A sequence of strings providing the labels for each wedge
@@ -2566,7 +2568,12 @@ def pie(self, x, explode=None, labels=None, colors=None,
25662568
if len(x) != len(explode):
25672569
raise ValueError("'explode' must be of length 'x'")
25682570
if colors is None:
2569-
colors = ('b', 'g', 'r', 'c', 'm', 'y', 'k', 'w')
2571+
get_next_color = self._get_patches_for_fill.get_next_color
2572+
else:
2573+
color_cycle = itertools.cycle(colors)
2574+
2575+
def get_next_color():
2576+
return six.next(color_cycle)
25702577

25712578
if radius is None:
25722579
radius = 1
@@ -2602,7 +2609,7 @@ def pie(self, x, explode=None, labels=None, colors=None,
26022609

26032610
w = mpatches.Wedge((x, y), radius, 360. * min(theta1, theta2),
26042611
360. * max(theta1, theta2),
2605-
facecolor=colors[i % len(colors)],
2612+
facecolor=get_next_color(),
26062613
**wedgeprops)
26072614
slices.append(w)
26082615
self.add_patch(w)
@@ -3022,8 +3029,8 @@ def xywhere(xs, ys, mask):
30223029
l0, = self.plot(x, y, fmt, label='_nolegend_', **kwargs)
30233030

30243031
if ecolor is None:
3025-
if l0 is None and 'color' in self._get_lines._prop_keys:
3026-
ecolor = next(self._get_lines.prop_cycler)['color']
3032+
if l0 is None:
3033+
ecolor = self._get_lines.get_next_color()
30273034
else:
30283035
ecolor = l0.get_color()
30293036

@@ -3844,7 +3851,10 @@ def scatter(self, x, y, s=None, c=None, marker='o', cmap=None, norm=None,
38443851
if facecolors is not None:
38453852
c = facecolors
38463853
else:
3847-
c = 'b' # The original default
3854+
if rcParams['_internal.classic_mode']:
3855+
c = 'b' # The original default
3856+
else:
3857+
c = self._get_patches_for_fill.get_next_color()
38483858

38493859
if edgecolors is None and not rcParams['_internal.classic_mode']:
38503860
edgecolors = 'face'
@@ -6015,9 +6025,8 @@ def _normalize_input(inp, ename='input'):
60156025
raise ValueError(
60166026
'weights should have the same shape as x')
60176027

6018-
if color is None and 'color' in self._get_lines._prop_keys:
6019-
color = [next(self._get_lines.prop_cycler)['color']
6020-
for i in xrange(nx)]
6028+
if color is None:
6029+
color = [self._get_lines.get_next_color() for i in xrange(nx)]
60216030
else:
60226031
color = mcolors.colorConverter.to_rgba_array(color)
60236032
if len(color) != nx:
@@ -7503,6 +7512,12 @@ def violin(self, vpstats, positions=None, vert=True, widths=0.5,
75037512
perp_lines = self.vlines
75047513
par_lines = self.hlines
75057514

7515+
if rcParams['_internal.classic_mode']:
7516+
fillcolor = 'y'
7517+
edgecolor = 'r'
7518+
else:
7519+
fillcolor = edgecolor = self._get_lines.get_next_color()
7520+
75067521
# Render violins
75077522
bodies = []
75087523
for stats, pos, width in zip(vpstats, positions, widths):
@@ -7513,7 +7528,7 @@ def violin(self, vpstats, positions=None, vert=True, widths=0.5,
75137528
bodies += [fill(stats['coords'],
75147529
-vals + pos,
75157530
vals + pos,
7516-
facecolor='y',
7531+
facecolor=fillcolor,
75177532
alpha=0.3)]
75187533
means.append(stats['mean'])
75197534
mins.append(stats['min'])
@@ -7523,20 +7538,24 @@ def violin(self, vpstats, positions=None, vert=True, widths=0.5,
75237538

75247539
# Render means
75257540
if showmeans:
7526-
artists['cmeans'] = perp_lines(means, pmins, pmaxes, colors='r')
7541+
artists['cmeans'] = perp_lines(means, pmins, pmaxes,
7542+
colors=edgecolor)
75277543

75287544
# Render extrema
75297545
if showextrema:
7530-
artists['cmaxes'] = perp_lines(maxes, pmins, pmaxes, colors='r')
7531-
artists['cmins'] = perp_lines(mins, pmins, pmaxes, colors='r')
7532-
artists['cbars'] = par_lines(positions, mins, maxes, colors='r')
7546+
artists['cmaxes'] = perp_lines(maxes, pmins, pmaxes,
7547+
colors=edgecolor)
7548+
artists['cmins'] = perp_lines(mins, pmins, pmaxes,
7549+
colors=edgecolor)
7550+
artists['cbars'] = par_lines(positions, mins, maxes,
7551+
colors=edgecolor)
75337552

75347553
# Render medians
75357554
if showmedians:
75367555
artists['cmedians'] = perp_lines(medians,
75377556
pmins,
75387557
pmaxes,
7539-
colors='r')
7558+
colors=edgecolor)
75407559

75417560
return artists
75427561

‎lib/matplotlib/axes/_base.py

Copy file name to clipboardExpand all lines: lib/matplotlib/axes/_base.py
+19-1Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ def _process_plot_format(fmt):
5252
* 'ko': black circles
5353
* '.b': blue dots
5454
* 'r--': red dashed lines
55+
* 'C2--': the third color in the color cycle, dashed lines
5556
5657
.. seealso::
5758
@@ -97,7 +98,9 @@ def _process_plot_format(fmt):
9798

9899
chars = [c for c in fmt]
99100

100-
for c in chars:
101+
i = 0
102+
while i < len(chars):
103+
c = chars[i]
101104
if c in mlines.lineStyles:
102105
if linestyle is not None:
103106
raise ValueError(
@@ -113,9 +116,14 @@ def _process_plot_format(fmt):
113116
raise ValueError(
114117
'Illegal format string "%s"; two color symbols' % fmt)
115118
color = c
119+
elif c == 'C' and i < len(chars) - 1:
120+
color_cycle_number = int(chars[i + 1])
121+
color = mcolors.colorConverter._get_nth_color(color_cycle_number)
122+
i += 1
116123
else:
117124
raise ValueError(
118125
'Unrecognized character %c in format string' % c)
126+
i += 1
119127

120128
if linestyle is None and marker is None:
121129
linestyle = rcParams['lines.linestyle']
@@ -161,6 +169,10 @@ def set_prop_cycle(self, *args, **kwargs):
161169
else:
162170
prop_cycler = cycler(*args, **kwargs)
163171

172+
# Make sure the cycler always has at least one color
173+
if 'color' not in prop_cycler.keys:
174+
prop_cycler = prop_cycler * cycler('color', ['k'])
175+
164176
self.prop_cycler = itertools.cycle(prop_cycler)
165177
# This should make a copy
166178
self._prop_keys = prop_cycler.keys
@@ -186,6 +198,12 @@ def __call__(self, *args, **kwargs):
186198
ret = self._grab_next_args(*args, **kwargs)
187199
return ret
188200

201+
def get_next_color(self):
202+
"""
203+
Return the next color in the cycle.
204+
"""
205+
return six.next(self.prop_cycler)['color']
206+
189207
def set_lineprops(self, line, **kwargs):
190208
assert self.command == 'plot', 'set_lineprops only works with "plot"'
191209
line.set(**kwargs)

‎lib/matplotlib/colors.py

Copy file name to clipboardExpand all lines: lib/matplotlib/colors.py
+50Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@
3131
- k: black
3232
- w: white
3333
34+
To use the colors that are part of the active color cycle in the current style,
35+
use `C` followed by a digit. For example:
36+
37+
- `C0`: The first color in the cycle
38+
- `C1`: The second color in the cycle
39+
3440
Gray shades can be given as a string encoding a float in the 0-1 range, e.g.::
3541
3642
color = '0.75'
@@ -67,6 +73,19 @@
6773

6874
def is_color_like(c):
6975
'Return *True* if *c* can be converted to *RGB*'
76+
77+
# Special-case the N-th color cycle syntax, because its parsing
78+
# needs to be deferred. We may be reading a value from rcParams
79+
# here before the color_cycle rcParam has been parsed.
80+
if isinstance(c, bytes):
81+
match = re.match(b'^C[0-9]$', c)
82+
if match is not None:
83+
return True
84+
elif isinstance(c, six.text_type):
85+
match = re.match('^C[0-9]$', c)
86+
if match is not None:
87+
return True
88+
7089
try:
7190
colorConverter.to_rgb(c)
7291
return True
@@ -114,9 +133,36 @@ class ColorConverter(object):
114133
'k': (0, 0, 0),
115134
'w': (1, 1, 1)}
116135

136+
_prop_cycler = None
137+
117138
cache = {}
118139
CN_LOOKUPS = [COLOR_NAMES[k] for k in ['css4', 'xkcd']]
119140

141+
@classmethod
142+
def _get_nth_color(cls, val):
143+
"""
144+
Get the Nth color in the current color cycle. If N is greater
145+
than the number of colors in the cycle, it is wrapped around.
146+
"""
147+
from matplotlib.rcsetup import cycler
148+
from matplotlib import rcParams
149+
150+
prop_cycler = rcParams['axes.prop_cycle']
151+
if prop_cycler is None and 'axes.color_cycle' in rcParams:
152+
clist = rcParams['axes.color_cycle']
153+
prop_cycler = cycler('color', clist)
154+
155+
colors = prop_cycler._transpose()['color']
156+
return colors[val % len(colors)]
157+
158+
@classmethod
159+
def _parse_nth_color(cls, val):
160+
match = re.match('^C[0-9]$', val)
161+
if match is not None:
162+
return cls._get_nth_color(int(val[1]))
163+
164+
raise ValueError("Not a color cycle color")
165+
120166
def to_rgb(self, arg):
121167
"""
122168
Returns an *RGB* tuple of three floats from 0-1.
@@ -154,6 +200,10 @@ def to_rgb(self, arg):
154200
argl = arg.lower()
155201
color = self.colors.get(argl, None)
156202
if color is None:
203+
try:
204+
argl = self._parse_nth_color(arg)
205+
except ValueError:
206+
pass
157207
for cmapping in self.CN_LOOKUPS:
158208
str1 = cmapping.get(argl, argl)
159209
if str1 != argl:

‎lib/matplotlib/rcsetup.py

Copy file name to clipboardExpand all lines: lib/matplotlib/rcsetup.py
+10-7Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -852,7 +852,7 @@ def validate_hist_bins(s):
852852
# line props
853853
'lines.linewidth': [1.5, validate_float], # line width in points
854854
'lines.linestyle': ['-', six.text_type], # solid line
855-
'lines.color': ['b', validate_color], # blue
855+
'lines.color': ['C0', validate_color], # first color in color cycle
856856
'lines.marker': ['None', six.text_type], # black
857857
'lines.markeredgewidth': [1.0, validate_float],
858858
'lines.markersize': [6, validate_float], # markersize, in points
@@ -871,7 +871,7 @@ def validate_hist_bins(s):
871871
## patch props
872872
'patch.linewidth': [None, validate_float_or_None], # line width in points
873873
'patch.edgecolor': ['k', validate_color], # black
874-
'patch.facecolor': ['#1f77b4', validate_color], # blue (first color in color cycle)
874+
'patch.facecolor': ['C0', validate_color], # first color in color cycle
875875
'patch.antialiased': [True, validate_bool], # antialiased (no jaggies)
876876

877877
## hatch props
@@ -892,27 +892,27 @@ def validate_hist_bins(s):
892892
'boxplot.showfliers': [True, validate_bool],
893893
'boxplot.meanline': [False, validate_bool],
894894

895-
'boxplot.flierprops.color': ['b', validate_color],
895+
'boxplot.flierprops.color': ['C0', validate_color],
896896
'boxplot.flierprops.marker': ['+', six.text_type],
897897
'boxplot.flierprops.markerfacecolor': ['auto', validate_color_or_auto],
898898
'boxplot.flierprops.markeredgecolor': ['k', validate_color],
899899
'boxplot.flierprops.markersize': [6, validate_float],
900900
'boxplot.flierprops.linestyle': ['none', six.text_type],
901901
'boxplot.flierprops.linewidth': [1.0, validate_float],
902902

903-
'boxplot.boxprops.color': ['b', validate_color],
903+
'boxplot.boxprops.color': ['C0', validate_color],
904904
'boxplot.boxprops.linewidth': [1.0, validate_float],
905905
'boxplot.boxprops.linestyle': ['-', six.text_type],
906906

907-
'boxplot.whiskerprops.color': ['b', validate_color],
907+
'boxplot.whiskerprops.color': ['C0', validate_color],
908908
'boxplot.whiskerprops.linewidth': [1.0, validate_float],
909909
'boxplot.whiskerprops.linestyle': ['--', six.text_type],
910910

911911
'boxplot.capprops.color': ['k', validate_color],
912912
'boxplot.capprops.linewidth': [1.0, validate_float],
913913
'boxplot.capprops.linestyle': ['-', six.text_type],
914914

915-
'boxplot.medianprops.color': ['r', validate_color],
915+
'boxplot.medianprops.color': ['C1', validate_color],
916916
'boxplot.medianprops.linewidth': [1.0, validate_float],
917917
'boxplot.medianprops.linestyle': ['-', six.text_type],
918918

@@ -1023,7 +1023,10 @@ def validate_hist_bins(s):
10231023
'axes.formatter.use_mathtext': [False, validate_bool],
10241024
'axes.formatter.useoffset': [True, validate_bool],
10251025
'axes.unicode_minus': [True, validate_bool],
1026-
'axes.color_cycle': [['b', 'g', 'r', 'c', 'm', 'y', 'k'],
1026+
'axes.color_cycle': [
1027+
['#1f77b4', '#ff7f0e', '#2ca02c', '#d62728',
1028+
'#9467bd', '#8c564b', '#e377c2', '#7f7f7f',
1029+
'#bcbd22', '#17becf'],
10271030
deprecate_axes_colorcycle], # cycle of plot
10281031
# line colors
10291032
# This entry can be either a cycler object or a

‎lib/matplotlib/sankey.py

Copy file name to clipboardExpand all lines: lib/matplotlib/sankey.py
+10-5Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
from matplotlib.transforms import Affine2D
4646
from matplotlib import verbose
4747
from matplotlib import docstring
48+
from matplotlib import rcParams
4849

4950
__author__ = "Kevin L. Davies"
5051
__credits__ = ["Yannick Copin"]
@@ -770,11 +771,15 @@ def _get_angle(a, r):
770771
print("lrpath\n", self._revert(lrpath))
771772
xs, ys = list(zip(*vertices))
772773
self.ax.plot(xs, ys, 'go-')
773-
patch = PathPatch(Path(vertices, codes),
774-
fc=kwargs.pop('fc', kwargs.pop('facecolor',
775-
'#bfd1d4')), # Custom defaults
776-
lw=kwargs.pop('lw', kwargs.pop('linewidth', 0.5)),
777-
**kwargs)
774+
if rcParams['_internal.classic_mode']:
775+
fc = kwargs.pop('fc', kwargs.pop('facecolor', '#bfd1d4'))
776+
lw = kwargs.pop('lw', kwargs.pop('linewidth', 0.5))
777+
else:
778+
fc = kwargs.pop('fc', kwargs.pop('facecolor', None))
779+
lw = kwargs.pop('lw', kwargs.pop('linewidth', None))
780+
if fc is None:
781+
fc = six.next(self.ax._get_patches_for_fill.prop_cycler)['color']
782+
patch = PathPatch(Path(vertices, codes), fc=fc, lw=lw, **kwargs)
778783
self.ax.add_patch(patch)
779784

780785
# Add the path labels.

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.