Skip to content

Commit ec56a0c

Browse files
committed
Lazily resolve colors for Patches.
This is consistent with Line2Ds and avoids complications with _original_{face,edge}color. test_patch_color_none was removed as it doesn't make much sense anymore (fixing it would just mean testing that mcolors.to_rgba is working as expected).
1 parent 4b1f956 commit ec56a0c

File tree

6 files changed

+79
-74
lines changed

6 files changed

+79
-74
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
Lazy color resolution for patches
2+
`````````````````````````````````
3+
4+
Patches now lazily resolve colors and apply alpha at draw time, similarly
5+
to Line2Ds. In particular, this means that they will be affected by later
6+
modifications to :rc:`patch.facecolor` and :rc:`patch.edgecolor`, and that
7+
``patch.get_facecolor()`` and ``patch.get_edgecolor()`` no longer necessarily
8+
return RGBA tuples but rather whatever was passed in with the corresponding
9+
setters.
10+
11+
:rc:`hatch.color` is still resolved at artist creation time as there is no
12+
other way to set the hatch color.

lib/matplotlib/patches.py

Lines changed: 46 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,6 @@ def __init__(self,
6868
if antialiased is None:
6969
antialiased = mpl.rcParams['patch.antialiased']
7070

71-
self._hatch_color = colors.to_rgba(mpl.rcParams['hatch.color'])
7271
self._fill = True # needed for set_facecolor call
7372
if color is not None:
7473
if edgecolor is not None or facecolor is not None:
@@ -87,6 +86,7 @@ def __init__(self,
8786
self.set_linewidth(linewidth)
8887
self.set_antialiased(antialiased)
8988
self.set_hatch(hatch)
89+
self._orig_hatch_color = colors.to_rgba(mpl.rcParams['hatch.color'])
9090
self.set_capstyle(capstyle)
9191
self.set_joinstyle(joinstyle)
9292
self._combined_transform = transforms.IdentityTransform()
@@ -115,7 +115,7 @@ def _process_radius(self, radius):
115115
if isinstance(self._picker, Number):
116116
_radius = self._picker
117117
else:
118-
if self.get_edgecolor()[3] == 0:
118+
if self._get_drawn_edgecolor()[3] == 0:
119119
_radius = 0
120120
else:
121121
_radius = self.get_linewidth()
@@ -170,7 +170,6 @@ def update_from(self, other):
170170
self._facecolor = other._facecolor
171171
self._fill = other._fill
172172
self._hatch = other._hatch
173-
self._hatch_color = other._hatch_color
174173
# copy the unscaled dash pattern
175174
self._us_dashes = other._us_dashes
176175
self.set_linewidth(other._linewidth) # also sets dash properties
@@ -221,13 +220,23 @@ def get_edgecolor(self):
221220
"""
222221
Return the edge color of the :class:`Patch`.
223222
"""
224-
return self._edgecolor
223+
ec = self._edgecolor
224+
if ec is None:
225+
if (mpl.rcParams['patch.force_edgecolor'] or
226+
not self._fill or self._edge_default):
227+
ec = mpl.rcParams['patch.edgecolor']
228+
else:
229+
ec = 'none'
230+
return ec
225231

226232
def get_facecolor(self):
227233
"""
228234
Return the face color of the :class:`Patch`.
229235
"""
230-
return self._facecolor
236+
fc = self._facecolor
237+
if fc is None:
238+
fc = mpl.rcParams['patch.facecolor']
239+
return fc
231240

232241
def get_linewidth(self):
233242
"""
@@ -254,21 +263,6 @@ def set_antialiased(self, aa):
254263
self._antialiased = aa
255264
self.stale = True
256265

257-
def _set_edgecolor(self, color):
258-
set_hatch_color = True
259-
if color is None:
260-
if (mpl.rcParams['patch.force_edgecolor'] or
261-
not self._fill or self._edge_default):
262-
color = mpl.rcParams['patch.edgecolor']
263-
else:
264-
color = 'none'
265-
set_hatch_color = False
266-
267-
self._edgecolor = colors.to_rgba(color, self._alpha)
268-
if set_hatch_color:
269-
self._hatch_color = self._edgecolor
270-
self.stale = True
271-
272266
def set_edgecolor(self, color):
273267
"""
274268
Set the patch edge color.
@@ -277,14 +271,7 @@ def set_edgecolor(self, color):
277271
----------
278272
color : color or None or 'auto'
279273
"""
280-
self._original_edgecolor = color
281-
self._set_edgecolor(color)
282-
283-
def _set_facecolor(self, color):
284-
if color is None:
285-
color = mpl.rcParams['patch.facecolor']
286-
alpha = self._alpha if self._fill else 0
287-
self._facecolor = colors.to_rgba(color, alpha)
274+
self._edgecolor = color
288275
self.stale = True
289276

290277
def set_facecolor(self, color):
@@ -295,8 +282,8 @@ def set_facecolor(self, color):
295282
----------
296283
color : color or None
297284
"""
298-
self._original_facecolor = color
299-
self._set_facecolor(color)
285+
self._facecolor = color
286+
self.stale = True
300287

301288
def set_color(self, c):
302289
"""
@@ -314,24 +301,6 @@ def set_color(self, c):
314301
self.set_facecolor(c)
315302
self.set_edgecolor(c)
316303

317-
def set_alpha(self, alpha):
318-
"""
319-
Set the alpha transparency of the patch.
320-
321-
Parameters
322-
----------
323-
alpha : float or None
324-
"""
325-
if alpha is not None:
326-
try:
327-
float(alpha)
328-
except TypeError:
329-
raise TypeError('alpha must be a float or None')
330-
artist.Artist.set_alpha(self, alpha)
331-
self._set_facecolor(self._original_facecolor)
332-
self._set_edgecolor(self._original_edgecolor)
333-
# stale is already True
334-
335304
def set_linewidth(self, w):
336305
"""
337306
Set the patch linewidth in points
@@ -393,8 +362,7 @@ def set_fill(self, b):
393362
b : bool
394363
"""
395364
self._fill = bool(b)
396-
self._set_facecolor(self._original_facecolor)
397-
self._set_edgecolor(self._original_edgecolor)
365+
self.set_edgecolor(self._edgecolor)
398366
self.stale = True
399367

400368
def get_fill(self):
@@ -479,6 +447,24 @@ def get_hatch(self):
479447
'Return the current hatching pattern'
480448
return self._hatch
481449

450+
def _get_drawn_edgecolor(self):
451+
return colors.to_rgba(self.get_edgecolor(), self._alpha)
452+
453+
def _get_drawn_facecolor(self):
454+
return colors.to_rgba(self.get_facecolor(),
455+
self._alpha if self._fill else 0)
456+
457+
def _get_drawn_hatchcolor(self):
458+
hc = self._edgecolor
459+
if hc is None:
460+
if (mpl.rcParams['patch.force_edgecolor'] or
461+
not self._fill or self._edge_default):
462+
hc = mpl.rcParams['patch.edgecolor']
463+
else:
464+
# Not affected by alpha.
465+
return self._orig_hatch_color
466+
return colors.to_rgba(hc, self._alpha)
467+
482468
@artist.allow_rasterization
483469
def draw(self, renderer):
484470
'Draw the :class:`Patch` to the given *renderer*.'
@@ -488,10 +474,11 @@ def draw(self, renderer):
488474
renderer.open_group('patch', self.get_gid())
489475
gc = renderer.new_gc()
490476

491-
gc.set_foreground(self._edgecolor, isRGBA=True)
477+
edgecolor = self._get_drawn_edgecolor()
478+
gc.set_foreground(edgecolor, isRGBA=True)
492479

493480
lw = self._linewidth
494-
if self._edgecolor[3] == 0:
481+
if edgecolor[3] == 0:
495482
lw = 0
496483
gc.set_linewidth(lw)
497484
gc.set_dashes(0, self._dashes)
@@ -503,7 +490,7 @@ def draw(self, renderer):
503490
gc.set_url(self._url)
504491
gc.set_snap(self.get_snap())
505492

506-
rgbFace = self._facecolor
493+
rgbFace = self._get_drawn_facecolor()
507494
if rgbFace[3] == 0:
508495
rgbFace = None # (some?) renderers expect this as no-fill signal
509496

@@ -512,7 +499,7 @@ def draw(self, renderer):
512499
if self._hatch:
513500
gc.set_hatch(self._hatch)
514501
try:
515-
gc.set_hatch_color(self._hatch_color)
502+
gc.set_hatch_color(self._get_drawn_hatchcolor())
516503
except AttributeError:
517504
# if we end up with a GC that does not have this method
518505
warnings.warn(
@@ -4259,10 +4246,11 @@ def draw(self, renderer):
42594246
renderer.open_group('patch', self.get_gid())
42604247
gc = renderer.new_gc()
42614248

4262-
gc.set_foreground(self._edgecolor, isRGBA=True)
4249+
edgecolor = self._get_drawn_edgecolor()
4250+
gc.set_foreground(edgecolor, isRGBA=True)
42634251

42644252
lw = self._linewidth
4265-
if self._edgecolor[3] == 0:
4253+
if edgecolor[3] == 0:
42664254
lw = 0
42674255
gc.set_linewidth(lw)
42684256
gc.set_dashes(self._dashoffset, self._dashes)
@@ -4272,7 +4260,7 @@ def draw(self, renderer):
42724260
gc.set_capstyle('round')
42734261
gc.set_snap(self.get_snap())
42744262

4275-
rgbFace = self._facecolor
4263+
rgbFace = self._get_drawn_facecolor()
42764264
if rgbFace[3] == 0:
42774265
rgbFace = None # (some?) renderers expect this as no-fill signal
42784266

@@ -4282,7 +4270,7 @@ def draw(self, renderer):
42824270
gc.set_hatch(self._hatch)
42834271
if self._hatch_color is not None:
42844272
try:
4285-
gc.set_hatch_color(self._hatch_color)
4273+
gc.set_hatch_color(self._get_drawn_hatchcolor())
42864274
except AttributeError:
42874275
# if we end up with a GC that does not have this method
42884276
warnings.warn("Your backend does not support setting the "

lib/matplotlib/tests/test_axes.py

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1461,16 +1461,16 @@ def test_bar_color_none_alpha():
14611461
ax = plt.gca()
14621462
rects = ax.bar([1, 2], [2, 4], alpha=0.3, color='none', edgecolor='r')
14631463
for rect in rects:
1464-
assert rect.get_facecolor() == (0, 0, 0, 0)
1465-
assert rect.get_edgecolor() == (1, 0, 0, 0.3)
1464+
assert rect._get_drawn_facecolor() == (0, 0, 0, 0)
1465+
assert rect._get_drawn_edgecolor() == (1, 0, 0, 0.3)
14661466

14671467

14681468
def test_bar_edgecolor_none_alpha():
14691469
ax = plt.gca()
14701470
rects = ax.bar([1, 2], [2, 4], alpha=0.3, color='r', edgecolor='none')
14711471
for rect in rects:
1472-
assert rect.get_facecolor() == (1, 0, 0, 0.3)
1473-
assert rect.get_edgecolor() == (0, 0, 0, 0)
1472+
assert rect._get_drawn_facecolor() == (1, 0, 0, 0.3)
1473+
assert rect._get_drawn_edgecolor() == (0, 0, 0, 0)
14741474

14751475

14761476
@image_comparison(baseline_images=['barh_tick_label'],
@@ -5659,7 +5659,8 @@ def test_bar_broadcast_args():
56595659
ax.bar(0, 1, bottom=range(4), width=1, orientation='horizontal')
56605660
# Check that edgecolor gets broadcasted.
56615661
rect1, rect2 = ax.bar([0, 1], [0, 1], edgecolor=(.1, .2, .3, .4))
5662-
assert rect1.get_edgecolor() == rect2.get_edgecolor() == (.1, .2, .3, .4)
5662+
assert rect1._get_drawn_edgecolor() == rect2._get_drawn_edgecolor() \
5663+
== (.1, .2, .3, .4)
56635664

56645665

56655666
def test_invalid_axis_limits():

lib/matplotlib/tests/test_legend.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@
99
from matplotlib.testing.decorators import image_comparison
1010
import matplotlib.pyplot as plt
1111
import matplotlib as mpl
12-
import matplotlib.transforms as mtransforms
1312
import matplotlib.collections as mcollections
14-
from matplotlib.legend_handler import HandlerTuple
13+
import matplotlib.colors as mcolors
1514
import matplotlib.legend as mlegend
15+
from matplotlib.legend_handler import HandlerTuple
16+
import matplotlib.transforms as mtransforms
1617
from matplotlib.cbook.deprecation import MatplotlibDeprecationWarning
1718

1819

@@ -543,3 +544,14 @@ def test_draggable():
543544
with pytest.warns(MatplotlibDeprecationWarning):
544545
legend.draggable()
545546
assert not legend.get_draggable()
547+
548+
549+
def test_alpha_handles():
550+
x, n, hh = plt.hist([1, 2, 3], alpha=0.25, label='data', color='red')
551+
legend = plt.legend()
552+
for lh in legend.legendHandles:
553+
lh.set_alpha(1.0)
554+
assert mcolors.to_rgb(lh.get_facecolor()) \
555+
== mcolors.to_rgb(hh[1].get_facecolor())
556+
assert mcolors.to_rgb(lh.get_edgecolor()) \
557+
== mcolors.to_rgb(hh[1].get_edgecolor())

lib/matplotlib/tests/test_patches.py

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -176,14 +176,6 @@ def test_patch_alpha_override():
176176
ax.set_ylim([-1, 2])
177177

178178

179-
@pytest.mark.style('default')
180-
def test_patch_color_none():
181-
# Make sure the alpha kwarg does not override 'none' facecolor.
182-
# Addresses issue #7478.
183-
c = plt.Circle((0, 0), 1, facecolor='none', alpha=1)
184-
assert c.get_facecolor()[0] == 0
185-
186-
187179
@image_comparison(baseline_images=['patch_custom_linestyle'],
188180
remove_text=True)
189181
def test_patch_custom_linestyle():

lib/matplotlib/tests/test_rcparams.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ def test_legend_colors(color_type, param_dict, target):
172172
_, ax = plt.subplots()
173173
ax.plot(range(3), label='test')
174174
leg = ax.legend()
175-
assert getattr(leg.legendPatch, get_func)() == target
175+
assert mcolors.same_color(getattr(leg.legendPatch, get_func)(), target)
176176

177177

178178
def test_mfc_rcparams():

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy