Skip to content

Commit fb5b3ea

Browse files
committed
Clean up colorbar and legend internals
1 parent b42bda8 commit fb5b3ea

File tree

1 file changed

+49
-41
lines changed

1 file changed

+49
-41
lines changed

proplot/axes/base.py

Lines changed: 49 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -982,7 +982,7 @@ def _add_colorbar(
982982
"""
983983
The driver function for adding axes colorbars.
984984
"""
985-
# Parse input args
985+
# Parse input arguments and apply defaults
986986
# TODO: Get the 'best' inset colorbar location using the legend algorithm
987987
# and implement inset colorbars the same as inset legends.
988988
grid = _not_none(grid=grid, edges=edges, drawedges=drawedges, default=rc['colorbar.grid']) # noqa: E501
@@ -1001,21 +1001,13 @@ def _add_colorbar(
10011001
ticklenratio = _not_none(ticklenratio, rc['tick.lenratio'])
10021002
tickwidthratio = _not_none(tickwidthratio, rc['tick.widthratio'])
10031003
rasterized = _not_none(rasterized, rc['colorbar.rasterized'])
1004+
1005+
# Build label and locator keyword argument dicts
1006+
# NOTE: This carefully handles the 'maxn' and 'maxn_minor' deprecations
1007+
kw_label = {}
10041008
locator_kw = locator_kw or {}
10051009
formatter_kw = formatter_kw or {}
10061010
minorlocator_kw = minorlocator_kw or {}
1007-
for b, kw in enumerate((locator_kw, minorlocator_kw)):
1008-
key = 'maxn_minor' if b else 'maxn'
1009-
name = 'minorlocator' if b else 'locator'
1010-
nbins = kwargs.pop('maxn_minor' if b else 'maxn', None)
1011-
if nbins is not None:
1012-
kw['nbins'] = nbins
1013-
warnings._warn_proplot(
1014-
f'The colorbar() keyword {key!r} was deprecated in v0.10. To '
1015-
"achieve the same effect, you can pass 'nbins' to the new default "
1016-
f"locator DiscreteLocator using {name}_kw={{'nbins': {nbins}}}."
1017-
)
1018-
kw_label = {}
10191011
for key, value in (
10201012
('size', labelsize),
10211013
('weight', labelweight),
@@ -1032,6 +1024,17 @@ def _add_colorbar(
10321024
):
10331025
if value is not None:
10341026
kw_ticklabels[key] = value
1027+
for b, kw in enumerate((locator_kw, minorlocator_kw)):
1028+
key = 'maxn_minor' if b else 'maxn'
1029+
name = 'minorlocator' if b else 'locator'
1030+
nbins = kwargs.pop('maxn_minor' if b else 'maxn', None)
1031+
if nbins is not None:
1032+
kw['nbins'] = nbins
1033+
warnings._warn_proplot(
1034+
f'The colorbar() keyword {key!r} was deprecated in v0.10. To '
1035+
"achieve the same effect, you can pass 'nbins' to the new default "
1036+
f"locator DiscreteLocator using {name}_kw={{'nbins': {nbins}}}."
1037+
)
10351038

10361039
# Generate and prepare the colorbar axes
10371040
# NOTE: The inset axes function needs 'label' to know how to pad the box
@@ -1061,7 +1064,21 @@ def _add_colorbar(
10611064
result = cax._parse_colorbar_arg(mappable, values, **kwargs)
10621065
mappable, locator_default, formatter_default, kwargs = result
10631066

1064-
# Parse ticking keyword arguments
1067+
# Parse 'extendsize' and 'extendfrac' keywords
1068+
# TODO: Make this auto-adjust to the subplot size
1069+
if extendsize is not None and extendfrac is not None:
1070+
warnings._warn_proplot(
1071+
f'You cannot specify both an absolute extendsize={extendsize!r} '
1072+
f"and a relative extendfrac={extendfrac!r}. Ignoring 'extendfrac'."
1073+
)
1074+
extendfrac = None
1075+
if extendfrac is None:
1076+
width, height = cax._get_size_inches()
1077+
scale = height if kwargs.get('orientation') == 'vertical' else width
1078+
extendsize = units(extendsize, 'em', 'in')
1079+
extendfrac = extendsize / max(scale - 2 * extendsize, units(1, 'em', 'in'))
1080+
1081+
# Parse the tick locators and formatters
10651082
# NOTE: This uses DiscreteLocator for default discrete minor ticks
10661083
name = 'y' if kwargs.get('orientation') == 'vertical' else 'x'
10671084
axis = cax.yaxis if kwargs.get('orientation') == 'vertical' else cax.xaxis
@@ -1084,26 +1101,15 @@ def _add_colorbar(
10841101
tickminor = rc[name + 'tick.minor.visible']
10851102
if minorlocator is not None:
10861103
minorlocator = constructor.Locator(minorlocator, **minorlocator_kw)
1087-
1088-
# Prepare colorbar keyword arguments
1089-
# WARNING: Critical to not pass empty major locators in matplotlib < 3.5
1090-
# See: https://github.com/lukelbd/proplot/issues/301
10911104
if isinstance(locator, mticker.NullLocator) or not len(getattr(locator, 'locs', (None,))): # noqa: E501
10921105
minorlocator, tickminor = None, False # attempted fix
10931106
for ticker in (locator, formatter, minorlocator):
10941107
if isinstance(ticker, mticker.TickHelper):
10951108
ticker.set_axis(axis)
1096-
if extendsize is not None and extendfrac is not None:
1097-
warnings._warn_proplot(
1098-
f'You cannot specify both an absolute extendsize={extendsize!r} '
1099-
f"and a relative extendfrac={extendfrac!r}. Ignoring 'extendfrac'."
1100-
)
1101-
extendfrac = None
1102-
if extendfrac is None:
1103-
width, height = cax._get_size_inches()
1104-
scale = height if kwargs.get('orientation') == 'vertical' else width
1105-
extendsize = units(extendsize, 'em', 'in')
1106-
extendfrac = extendsize / max(scale - 2 * extendsize, units(1, 'em', 'in'))
1109+
1110+
# Prepare colorbar keyword arguments
1111+
# WARNING: Critical to not pass empty major locators in matplotlib < 3.5
1112+
# See: https://github.com/lukelbd/proplot/issues/301
11071113
kwargs.update(
11081114
{
11091115
'cax': cax,
@@ -1120,9 +1126,7 @@ def _add_colorbar(
11201126
else:
11211127
kwargs['extend'] = extend
11221128

1123-
# Draw and update the colorbar
1124-
# WARNING: Must use colorbar set_label to set text,
1125-
# calling set_text on the axis will do nothing!
1129+
# Create colorbar and update ticks and axis direction
11261130
# WARNING: Colorbar _ticker() internally makes dummy axis and updates view
11271131
# limits. Here we apply actual axis rather than dummy, otherwise default nbins
11281132
# of DiscreteLocator will not work. Not sure if this has side effects...
@@ -1135,13 +1139,17 @@ def _add_colorbar(
11351139
obj.minorticks_on()
11361140
else:
11371141
obj.minorticks_off()
1138-
axis.set_tick_params(which='both', color=color, direction=tickdir)
1139-
axis.set_tick_params(which='major', length=ticklen, width=tickwidth)
1140-
axis.set_tick_params(which='minor', length=ticklen * ticklenratio, width=tickwidth * tickwidthratio) # noqa: E501
11411142
if getattr(mappable.norm, 'descending', None):
11421143
axis.set_inverted(True)
11431144
if reverse: # potentially double reverse, although that would be weird...
11441145
axis.set_inverted(True)
1146+
1147+
# Update other colorbar settings
1148+
# WARNING: Must use colorbar set_label to set text,
1149+
# calling set_text on the axis will do nothing!
1150+
axis.set_tick_params(which='both', color=color, direction=tickdir)
1151+
axis.set_tick_params(which='major', length=ticklen, width=tickwidth)
1152+
axis.set_tick_params(which='minor', length=ticklen * ticklenratio, width=tickwidth * tickwidthratio) # noqa: E501
11451153
if label is not None:
11461154
obj.set_label(label)
11471155
if labelloc is not None:
@@ -1158,7 +1166,7 @@ def _add_colorbar(
11581166
obj.solids.set_rasterized(rasterized)
11591167
cax._fix_patch_edges(obj.solids, edgefix=edgefix)
11601168

1161-
# Return after registering location
1169+
# Register location and return
11621170
self._register_guide('colorbar', obj, (loc, align)) # possibly replace another
11631171
return obj
11641172

@@ -1256,6 +1264,8 @@ def _add_legend(
12561264
)
12571265

12581266
# Add the legend and update patch properties
1267+
# TODO: Add capacity for categorical labels in a single legend like seaborn
1268+
# rather than manual handle overrides with multiple legends.
12591269
if multi:
12601270
objs = lax._parse_legend_centered(pairs, kw_frame=kw_frame, **kwargs)
12611271
else:
@@ -1269,12 +1279,11 @@ def _add_legend(
12691279
lax.add_artist(obj)
12701280

12711281
# Update legend patch and elements
1272-
# TODO: Add capacity for categorical labels in a single legend like seaborn
1273-
# rather than manual handle overrides with multiple legends.
12741282
# WARNING: legendHandles only contains the *first* artist per legend because
12751283
# HandlerBase.legend_artist() called in Legend._init_legend_box() only
12761284
# returns the first artist. Instead we try to iterate through offset boxes.
12771285
for obj in objs:
1286+
obj.set_clip_on(False) # needed for tight bounding box calculations
12781287
box = getattr(obj, '_legend_handle_box', None)
12791288
for obj in guides._iter_children(box):
12801289
if isinstance(obj, mtext.Text):
@@ -1285,13 +1294,12 @@ def _add_legend(
12851294
kw['sizes'] = np.atleast_1d(kw_handle['markersize'])
12861295
obj.update(kw)
12871296

1288-
# Return after registering location
1289-
for obj in objs:
1290-
obj.set_clip_on(False) # critical for tight bounding box calcs
1297+
# Register location and return
12911298
if isinstance(objs[0], mpatches.FancyBboxPatch):
12921299
objs = objs[1:]
12931300
obj = objs[0] if len(objs) == 1 else tuple(objs)
12941301
self._register_guide('legend', obj, (loc, align)) # possibly replace another
1302+
12951303
return obj
12961304

12971305
def _apply_title_above(self):

0 commit comments

Comments
 (0)