Skip to content

Commit e112aef

Browse files
committed
Feature implementation for Issue 2841
1 parent 9348a46 commit e112aef

File tree

6 files changed

+185
-20
lines changed

6 files changed

+185
-20
lines changed

lib/matplotlib/axes/_axes.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2876,7 +2876,7 @@ def xywhere(xs, ys, mask):
28762876

28772877
if ecolor is None:
28782878
if l0 is None:
2879-
ecolor = six.next(self._get_lines.color_cycle)
2879+
ecolor = self._get_lines.cycle.get_next_color()
28802880
else:
28812881
ecolor = l0.get_color()
28822882

@@ -5677,7 +5677,7 @@ def hist(self, x, bins=10, range=None, normed=False, weights=None,
56775677
nx = len(x) # number of datasets
56785678

56795679
if color is None:
5680-
color = [six.next(self._get_lines.color_cycle)
5680+
color = [self._get_lines.cycle.get_next_color()
56815681
for i in xrange(nx)]
56825682
else:
56835683
color = mcolors.colorConverter.to_rgba_array(color)

lib/matplotlib/axes/_base.py

Lines changed: 43 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@
2929
import matplotlib.font_manager as font_manager
3030
import matplotlib.text as mtext
3131
import matplotlib.image as mimage
32+
import matplotlib.cycle as mcycle
33+
3234
from matplotlib.artist import allow_rasterization
3335
from matplotlib.cbook import iterable
3436

@@ -137,20 +139,15 @@ class _process_plot_var_args(object):
137139
def __init__(self, axes, command='plot'):
138140
self.axes = axes
139141
self.command = command
140-
self.set_color_cycle()
142+
self.cycle = mcycle.Cycle()
141143

142144
def __getstate__(self):
143145
# note: it is not possible to pickle a itertools.cycle instance
144146
return {'axes': self.axes, 'command': self.command}
145147

146148
def __setstate__(self, state):
147149
self.__dict__ = state.copy()
148-
self.set_color_cycle()
149-
150-
def set_color_cycle(self, clist=None):
151-
if clist is None:
152-
clist = rcParams['axes.color_cycle']
153-
self.color_cycle = itertools.cycle(clist)
150+
self.cycle.set_color_cycle()
154151

155152
def __call__(self, *args, **kwargs):
156153

@@ -229,12 +226,10 @@ def _xy_from_xy(self, x, y):
229226
return x, y
230227

231228
def _makeline(self, x, y, kw, kwargs):
232-
kw = kw.copy() # Don't modify the original kw.
229+
args = self.cycle.next()
230+
args.update(kw)
231+
kw = args.copy() # Don't modify the original kw.
233232
kwargs = kwargs.copy()
234-
if kw.get('color', None) is None and kwargs.get('color', None) is None:
235-
kwargs['color'] = kw['color'] = six.next(self.color_cycle)
236-
# (can't use setdefault because it always evaluates
237-
# its second argument)
238233
seg = mlines.Line2D(x, y,
239234
**kw
240235
)
@@ -245,7 +240,7 @@ def _makefill(self, x, y, kw, kwargs):
245240
try:
246241
facecolor = kw['color']
247242
except KeyError:
248-
facecolor = six.next(self.color_cycle)
243+
facecolor = self.cycle.get_next_color()
249244
seg = mpatches.Polygon(np.hstack((x[:, np.newaxis],
250245
y[:, np.newaxis])),
251246
facecolor=facecolor,
@@ -971,14 +966,47 @@ def clear(self):
971966
"""clear the axes"""
972967
self.cla()
973968

969+
def set_cycle(self, style, slist):
970+
"""
971+
Set the cycle for a line attribute for any future plot commands
972+
on this Axes
973+
974+
*style* is a key to a dictionary for cycles in the cycle class
975+
*slist* is a list of mpl style specifiers
976+
"""
977+
self._get_lines.cycle.set_cycle(style, slist)
978+
979+
def clear_all_cycle(self):
980+
"""
981+
Clear all the current line attribute cycles
982+
"""
983+
self._get_lines.cycle.clear_all_cycle()
984+
985+
def clear_cycle(self, style):
986+
"""
987+
Clear a cycle for a line attribute specified by style
988+
989+
*style* is a key to a dictionary for cycles in the cycle class
990+
"""
991+
self._get_lines.cycle.clear_cycle(style)
992+
974993
def set_color_cycle(self, clist):
975994
"""
976995
Set the color cycle for any future plot commands on this Axes.
977996
978997
*clist* is a list of mpl color specifiers.
979998
"""
980-
self._get_lines.set_color_cycle(clist)
981-
self._get_patches_for_fill.set_color_cycle(clist)
999+
self._get_lines.cycle.set_color_cycle(clist)
1000+
self._get_patches_for_fill.cycle.set_color_cycle(clist)
1001+
1002+
def set_line_cycle(self, llist):
1003+
"""
1004+
Set the line style cycle for any future plot commands on this
1005+
Axes.
1006+
1007+
*llist* is a list of mpl line style specifiers.
1008+
"""
1009+
self._get_lines.cycle.set_line_cycle(llist)
9821010

9831011
def ishold(self):
9841012
"""return the HOLD status of the axes"""

lib/matplotlib/cycle.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import itertools
2+
import six
3+
from matplotlib import rcParams
4+
5+
6+
class Cycle:
7+
8+
def __init__(self):
9+
"""
10+
Set the initial cycle of styles to be used by the lines of the graph
11+
"""
12+
self._styles = {'color': None,
13+
'linestyle': None,
14+
'linewidth': None,
15+
'marker': None,
16+
'markersize': None,
17+
'markeredgewidth': None,
18+
'markeredgecolor': None,
19+
'markerfacecolor': None,
20+
'antialiased': None,
21+
'dash_capstyle': None,
22+
'solid_capstyle': None,
23+
'dash_joinstyle': None,
24+
'solid_joinstyle': None,
25+
}
26+
self.set_color_cycle()
27+
self.set_line_cycle()
28+
29+
def next(self):
30+
"""
31+
Returns the next set of line attributes for a line on the graph to use
32+
"""
33+
args = {}
34+
for i in self._styles.keys():
35+
if self._styles[i] != None:
36+
args[i] = six.next(self._styles[i])
37+
return args
38+
39+
def set_cycle(self, style, slist):
40+
"""
41+
Set a cycle for a line attribute specified by style, the cycle to be
42+
used to is specified by slist
43+
44+
*style* is a key to the _style dictionary
45+
*slist* is a list of mpl style specifiers
46+
"""
47+
if self.validate(style, slist):
48+
self._styles[style] = itertools.cycle(slist)
49+
50+
def validate(self, style, plist):
51+
"""
52+
Ensures that the style given is a valid attribute to by cycled over
53+
If the style is a valid cycle, ensure that the list of specifiers
54+
given are valid specifiers for that style
55+
56+
*style* is a key to the _style dictionary
57+
*plist* is a list of mpl style specifiers
58+
"""
59+
if style not in self._styles.keys():
60+
raise ValueError(
61+
"Set cycle value error, %s is not a valid style" % style)
62+
param = 'lines.' + style
63+
for val in set(plist):
64+
try:
65+
rcParams.validate[param](val)
66+
except ValueError:
67+
raise ValueError(
68+
"Set cycle value error, Style %s: %s" % (style, str(val)))
69+
return True
70+
71+
def clear_cycle(self, style):
72+
"""
73+
Clears(resets) a cycle for a line attribute specified by style
74+
75+
*style* is a key to the _style dictionary
76+
"""
77+
self._styles[style] = None
78+
79+
def clear_all_cycle(self):
80+
"""
81+
Clears (resets) all cycles for the lines on a plot
82+
"""
83+
for style in self._styles.keys():
84+
self._styles[style] = None
85+
86+
def get_next_color(self):
87+
"""
88+
Return the next color to be used by a line
89+
"""
90+
return six.next(self._styles['color'])
91+
92+
def set_color_cycle(self, clist=None):
93+
"""
94+
Sets a color cycle to be used for the lines on the graph, if none are
95+
specified the default color cycle will be used
96+
"""
97+
if clist is None:
98+
clist = rcParams['axes.color_cycle']
99+
self._styles['color'] = itertools.cycle(clist)
100+
101+
def set_line_cycle(self, llist=None):
102+
"""
103+
Sets a line style cycle to be used for the lines on the graph, if none are
104+
specified the default line style cycle will be used
105+
"""
106+
if llist is None:
107+
llist = rcParams['axes.line_cycle']
108+
self._styles['linestyle'] = itertools.cycle(llist)

lib/matplotlib/rcsetup.py

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,24 @@ def __call__(self, s):
234234
except ValueError:
235235
raise ValueError('Could not convert all entries to ints')
236236

237+
def validate_line(s):
238+
'return a valid arg'
239+
if s in (None, 'none', 'None', ' ', ''):
240+
return 'None'
241+
242+
if s in ('-', '--', '-.', ':'):
243+
return s
244+
245+
raise ValueError("line given not valid")
246+
247+
248+
def validate_markercolor(s):
249+
'return a valid marker color arg'
250+
if s == 'auto':
251+
return s
252+
else:
253+
return validate_color(s)
254+
237255

238256
def validate_color(s):
239257
'return a valid color arg'
@@ -278,6 +296,14 @@ def validate_colorlist(s):
278296
msg = "'s' must be of type [ string | list | tuple ]"
279297
raise ValueError(msg)
280298

299+
def validate_linelist(s):
300+
'return a list of colorspecs'
301+
if isinstance(s, six.string_types):
302+
return [validate_line(l.strip()) for l in s.split(',')]
303+
else:
304+
assert type(s) in [list, tuple]
305+
return [validate_line(l) for l in s]
306+
281307
def validate_stringlist(s):
282308
'return a list'
283309
if isinstance(s, six.string_types):
@@ -529,6 +555,8 @@ def __call__(self, s):
529555
'lines.marker': ['None', six.text_type], # black
530556
'lines.markeredgewidth': [0.5, validate_float],
531557
'lines.markersize': [6, validate_float], # markersize, in points
558+
'lines.markeredgecolor' : ['auto', validate_markercolor],
559+
'lines.markerfacecolor' : ['auto', validate_markercolor],
532560
'lines.antialiased': [True, validate_bool], # antialised (no jaggies)
533561
'lines.dash_joinstyle': ['round', validate_joinstyle],
534562
'lines.solid_joinstyle': ['round', validate_joinstyle],
@@ -637,6 +665,7 @@ def __call__(self, s):
637665
'axes.color_cycle': [['b', 'g', 'r', 'c', 'm', 'y', 'k'],
638666
validate_colorlist], # cycle of plot
639667
# line colors
668+
'axes.line_cycle':[['-'],validate_linelist],
640669
'axes.xmargin': [0, ValidateInterval(0, 1,
641670
closedmin=True,
642671
closedmax=True)], # margin added to xaxis

lib/matplotlib/stackplot.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,13 +103,13 @@ def stackplot(axes, x, *args, **kwargs):
103103

104104
# Color between x = 0 and the first array.
105105
r.append(axes.fill_between(x, first_line, stack[0, :],
106-
facecolor=six.next(axes._get_lines.color_cycle),
106+
facecolor=axes._get_lines.cycle.get_next_color(),
107107
label= six.next(labels, None),
108108
**kwargs))
109109

110110
# Color between array i-1 and array i
111111
for i in xrange(len(y) - 1):
112-
color = six.next(axes._get_lines.color_cycle)
112+
color = axes._get_lines.cycle.get_next_color()
113113
r.append(axes.fill_between(x, stack[i, :], stack[i + 1, :],
114114
facecolor= color,
115115
label= six.next(labels, None),

lib/matplotlib/streamplot.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ def streamplot(axes, x, y, u, v, density=1, linewidth=None, color=None,
8080
transform = axes.transData
8181

8282
if color is None:
83-
color = six.next(axes._get_lines.color_cycle)
83+
color = axes._get_lines.get_next_color()
8484

8585
if linewidth is None:
8686
linewidth = matplotlib.rcParams['lines.linewidth']

0 commit comments

Comments
 (0)