Skip to content

Commit 71c55ab

Browse files
Lucx33meeseeksmachine
authored andcommitted
Backport PR #29133: Creating_parse_bar_color_args to unify color handling in plt.bar with precedence and sequence support for facecolor and edgecolor
1 parent 53a9605 commit 71c55ab

File tree

2 files changed

+86
-8
lines changed

2 files changed

+86
-8
lines changed

lib/matplotlib/axes/_axes.py

+61-8
Original file line numberDiff line numberDiff line change
@@ -2321,6 +2321,56 @@ def _convert_dx(dx, x0, xconv, convert):
23212321
dx = convert(dx)
23222322
return dx
23232323

2324+
def _parse_bar_color_args(self, kwargs):
2325+
"""
2326+
Helper function to process color-related arguments of `.Axes.bar`.
2327+
2328+
Argument precedence for facecolors:
2329+
2330+
- kwargs['facecolor']
2331+
- kwargs['color']
2332+
- 'Result of ``self._get_patches_for_fill.get_next_color``
2333+
2334+
Argument precedence for edgecolors:
2335+
2336+
- kwargs['edgecolor']
2337+
- None
2338+
2339+
Parameters
2340+
----------
2341+
self : Axes
2342+
2343+
kwargs : dict
2344+
Additional kwargs. If these keys exist, we pop and process them:
2345+
'facecolor', 'edgecolor', 'color'
2346+
Note: The dict is modified by this function.
2347+
2348+
2349+
Returns
2350+
-------
2351+
facecolor
2352+
The facecolor. One or more colors as (N, 4) rgba array.
2353+
edgecolor
2354+
The edgecolor. Not normalized; may be any valid color spec or None.
2355+
"""
2356+
color = kwargs.pop('color', None)
2357+
2358+
facecolor = kwargs.pop('facecolor', color)
2359+
edgecolor = kwargs.pop('edgecolor', None)
2360+
2361+
facecolor = (facecolor if facecolor is not None
2362+
else self._get_patches_for_fill.get_next_color())
2363+
2364+
try:
2365+
facecolor = mcolors.to_rgba_array(facecolor)
2366+
except ValueError as err:
2367+
raise ValueError(
2368+
"'facecolor' or 'color' argument must be a valid color or"
2369+
"sequence of colors."
2370+
) from err
2371+
2372+
return facecolor, edgecolor
2373+
23242374
@_preprocess_data()
23252375
@_docstring.interpd
23262376
def bar(self, x, height, width=0.8, bottom=None, *, align="center",
@@ -2376,7 +2426,12 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center",
23762426
Other Parameters
23772427
----------------
23782428
color : :mpltype:`color` or list of :mpltype:`color`, optional
2429+
The colors of the bar faces. This is an alias for *facecolor*.
2430+
If both are given, *facecolor* takes precedence.
2431+
2432+
facecolor : :mpltype:`color` or list of :mpltype:`color`, optional
23792433
The colors of the bar faces.
2434+
If both *color* and *facecolor are given, *facecolor* takes precedence.
23802435
23812436
edgecolor : :mpltype:`color` or list of :mpltype:`color`, optional
23822437
The colors of the bar edges.
@@ -2441,10 +2496,8 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center",
24412496
bar. See :doc:`/gallery/lines_bars_and_markers/bar_stacked`.
24422497
"""
24432498
kwargs = cbook.normalize_kwargs(kwargs, mpatches.Patch)
2444-
color = kwargs.pop('color', None)
2445-
if color is None:
2446-
color = self._get_patches_for_fill.get_next_color()
2447-
edgecolor = kwargs.pop('edgecolor', None)
2499+
facecolor, edgecolor = self._parse_bar_color_args(kwargs)
2500+
24482501
linewidth = kwargs.pop('linewidth', None)
24492502
hatch = kwargs.pop('hatch', None)
24502503

@@ -2540,9 +2593,9 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center",
25402593

25412594
linewidth = itertools.cycle(np.atleast_1d(linewidth))
25422595
hatch = itertools.cycle(np.atleast_1d(hatch))
2543-
color = itertools.chain(itertools.cycle(mcolors.to_rgba_array(color)),
2544-
# Fallback if color == "none".
2545-
itertools.repeat('none'))
2596+
facecolor = itertools.chain(itertools.cycle(facecolor),
2597+
# Fallback if color == "none".
2598+
itertools.repeat('none'))
25462599
if edgecolor is None:
25472600
edgecolor = itertools.repeat(None)
25482601
else:
@@ -2576,7 +2629,7 @@ def bar(self, x, height, width=0.8, bottom=None, *, align="center",
25762629
bottom = y
25772630

25782631
patches = []
2579-
args = zip(left, bottom, width, height, color, edgecolor, linewidth,
2632+
args = zip(left, bottom, width, height, facecolor, edgecolor, linewidth,
25802633
hatch, patch_labels)
25812634
for l, b, w, h, c, e, lw, htch, lbl in args:
25822635
r = mpatches.Rectangle(

lib/matplotlib/tests/test_axes.py

+25
Original file line numberDiff line numberDiff line change
@@ -9451,3 +9451,28 @@ def test_wrong_use_colorizer():
94519451
for kwrd in kwrds:
94529452
with pytest.raises(ValueError, match=match_str):
94539453
fig.figimage(c, colorizer=cl, **kwrd)
9454+
9455+
9456+
def test_bar_color_precedence():
9457+
# Test the precedence of 'color' and 'facecolor' in bar plots
9458+
fig, ax = plt.subplots()
9459+
9460+
# case 1: no color specified
9461+
bars = ax.bar([1, 2, 3], [4, 5, 6])
9462+
for bar in bars:
9463+
assert mcolors.same_color(bar.get_facecolor(), 'blue')
9464+
9465+
# case 2: Only 'color'
9466+
bars = ax.bar([11, 12, 13], [4, 5, 6], color='red')
9467+
for bar in bars:
9468+
assert mcolors.same_color(bar.get_facecolor(), 'red')
9469+
9470+
# case 3: Only 'facecolor'
9471+
bars = ax.bar([21, 22, 23], [4, 5, 6], facecolor='yellow')
9472+
for bar in bars:
9473+
assert mcolors.same_color(bar.get_facecolor(), 'yellow')
9474+
9475+
# case 4: 'facecolor' and 'color'
9476+
bars = ax.bar([31, 32, 33], [4, 5, 6], color='red', facecolor='green')
9477+
for bar in bars:
9478+
assert mcolors.same_color(bar.get_facecolor(), 'green')

0 commit comments

Comments
 (0)