Skip to content

Commit 1b64aa7

Browse files
committed
ENH: Add new color spec, tuple (matplotlib_color, alpha)
1 parent 3742e7e commit 1b64aa7

File tree

5 files changed

+139
-2
lines changed

5 files changed

+139
-2
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
Add a new valid color format ``(matplotlib_color, alpha)``
2+
----------------------------------------------------------
3+
4+
5+
.. plot::
6+
:include-source: true
7+
8+
import matplotlib.pyplot as plt
9+
from matplotlib.patches import Rectangle
10+
11+
fig, ax = plt.subplots()
12+
13+
rectangle = Rectangle((.2, .2), .6, .6,
14+
facecolor=('blue', 0.2),
15+
edgecolor=('green', 0.5))
16+
ax.add_patch(rectangle)
17+
18+
19+
Users can define a color using the new color specification, *(matplotlib_color, alpha)*.
20+
Note that an explicit alpha keyword argument will override an alpha value from
21+
*(matplotlib_color, alpha)*.

galleries/examples/color/set_alpha.py

+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
"""
2+
=================================
3+
Ways to set a color's alpha value
4+
=================================
5+
6+
Compare setting alpha by the *alpha* keyword argument and by one of the Matplotlib color
7+
formats. Often, the *alpha* keyword is the only tool needed to add transparency to a
8+
color. In some cases, the *(matplotlib_color, alpha)* color format provides an easy way
9+
to fine-tune the appearance of a Figure.
10+
11+
"""
12+
13+
import matplotlib.pyplot as plt
14+
import numpy as np
15+
16+
# Fixing random state for reproducibility.
17+
np.random.seed(19680801)
18+
19+
fig, (ax1, ax2) = plt.subplots(ncols=2, figsize=(8, 4))
20+
21+
x_values = [n for n in range(20)]
22+
y_values = np.random.randn(20)
23+
24+
facecolors = ['green' if y > 0 else 'red' for y in y_values]
25+
edgecolors = facecolors
26+
27+
ax1.bar(x_values, y_values, color=facecolors, edgecolor=edgecolors, alpha=0.5)
28+
ax1.set_title("Explicit 'alpha' keyword value\nshared by all bars and edges")
29+
30+
31+
# Normalize y values to get distinct face alpha values.
32+
abs_y = [abs(y) for y in y_values]
33+
face_alphas = [n / max(abs_y) for n in abs_y]
34+
edge_alphas = [1 - alpha for alpha in face_alphas]
35+
36+
colors_with_alphas = list(zip(facecolors, face_alphas))
37+
edgecolors_with_alphas = list(zip(edgecolors, edge_alphas))
38+
39+
ax2.bar(x_values, y_values, color=colors_with_alphas,
40+
edgecolor=edgecolors_with_alphas)
41+
ax2.set_title('Normalized alphas for\neach bar and each edge')
42+
43+
plt.show()
44+
45+
# %%
46+
#
47+
# .. admonition:: References
48+
#
49+
# The use of the following functions, methods, classes and modules is shown
50+
# in this example:
51+
#
52+
# - `matplotlib.axes.Axes.bar`
53+
# - `matplotlib.pyplot.subplots`

galleries/tutorials/colors/colors.py

+3
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@
6464
| to black if cycle does not | |
6565
| include color. | |
6666
+--------------------------------------+--------------------------------------+
67+
| Tuple of one of the above color | - ``('green', 0.3)`` |
68+
| formats and an alpha float. | - ``('#f00', 0.9)`` |
69+
+--------------------------------------+--------------------------------------+
6770
6871
.. _xkcd color survey: https://xkcd.com/color/rgb/
6972

lib/matplotlib/colors.py

+17-2
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,13 @@ def _to_rgba_no_colorcycle(c, alpha=None):
315315
*alpha* is ignored for the color value ``"none"`` (case-insensitive),
316316
which always maps to ``(0, 0, 0, 0)``.
317317
"""
318+
if isinstance(c, tuple) and len(c) == 2:
319+
if alpha is None:
320+
c, alpha = c
321+
else:
322+
c = c[0]
323+
if alpha is not None and not 0 <= alpha <= 1:
324+
raise ValueError("'alpha' must be between 0 and 1, inclusive")
318325
orig_c = c
319326
if c is np.ma.masked:
320327
return (0., 0., 0., 0.)
@@ -425,6 +432,11 @@ def to_rgba_array(c, alpha=None):
425432
(n, 4) array of RGBA colors, where each channel (red, green, blue,
426433
alpha) can assume values between 0 and 1.
427434
"""
435+
if isinstance(c, tuple) and len(c) == 2:
436+
if alpha is None:
437+
c, alpha = c
438+
else:
439+
c = c[0]
428440
# Special-case inputs that are already arrays, for performance. (If the
429441
# array has the wrong kind or shape, raise the error during one-at-a-time
430442
# conversion.)
@@ -464,9 +476,12 @@ def to_rgba_array(c, alpha=None):
464476
return np.array([to_rgba(c, a) for a in alpha], float)
465477
else:
466478
return np.array([to_rgba(c, alpha)], float)
467-
except (ValueError, TypeError):
479+
except TypeError:
468480
pass
469-
481+
except ValueError as e:
482+
if e.args == ("'alpha' must be between 0 and 1, inclusive", ):
483+
# ValueError is from _to_rgba_no_colorcycle().
484+
raise e
470485
if isinstance(c, str):
471486
raise ValueError(f"{c!r} is not a valid color value.")
472487

lib/matplotlib/tests/test_colors.py

+45
Original file line numberDiff line numberDiff line change
@@ -1307,6 +1307,51 @@ def test_to_rgba_array_alpha_array():
13071307
assert_array_equal(c[:, 3], alpha)
13081308

13091309

1310+
def test_to_rgba_array_accepts_color_alpha_tuple():
1311+
assert_array_equal(
1312+
mcolors.to_rgba_array(('black', 0.9)),
1313+
[[0, 0, 0, 0.9]])
1314+
1315+
1316+
def test_to_rgba_array_explicit_alpha_overrides_tuple_alpha():
1317+
assert_array_equal(
1318+
mcolors.to_rgba_array(('black', 0.9), alpha=0.5),
1319+
[[0, 0, 0, 0.5]])
1320+
1321+
1322+
def test_to_rgba_array_accepts_color_alpha_tuple_with_multiple_colors():
1323+
color_array = np.array([[1., 1., 1., 1.], [0., 0., 1., 0.]])
1324+
assert_array_equal(
1325+
mcolors.to_rgba_array((color_array, 0.2)),
1326+
[[1., 1., 1., 0.2], [0., 0., 1., 0.2]])
1327+
1328+
color_sequence = [[1., 1., 1., 1.], [0., 0., 1., 0.]]
1329+
assert_array_equal(
1330+
mcolors.to_rgba_array((color_sequence, 0.4)),
1331+
[[1., 1., 1., 0.4], [0., 0., 1., 0.4]])
1332+
1333+
1334+
def test_to_rgba_array_error_with_color_invalid_alpha_tuple():
1335+
with pytest.raises(ValueError, match="'alpha' must be between 0 and 1,"):
1336+
mcolors.to_rgba_array(('black', 2.0))
1337+
1338+
1339+
@pytest.mark.parametrize('rgba_alpha',
1340+
[('white', 0.5), ('#ffffff', 0.5), ('#ffffff00', 0.5),
1341+
((1.0, 1.0, 1.0, 1.0), 0.5)])
1342+
def test_to_rgba_accepts_color_alpha_tuple(rgba_alpha):
1343+
assert mcolors.to_rgba(rgba_alpha) == (1, 1, 1, 0.5)
1344+
1345+
1346+
def test_to_rgba_explicit_alpha_overrides_tuple_alpha():
1347+
assert mcolors.to_rgba(('red', 0.1), alpha=0.9) == (1, 0, 0, 0.9)
1348+
1349+
1350+
def test_to_rgba_error_with_color_invalid_alpha_tuple():
1351+
with pytest.raises(ValueError, match="'alpha' must be between 0 and 1"):
1352+
mcolors.to_rgba(('blue', 2.0))
1353+
1354+
13101355
def test_failed_conversions():
13111356
with pytest.raises(ValueError):
13121357
mcolors.to_rgba('5')

0 commit comments

Comments
 (0)