Skip to content

Commit 9bded5a

Browse files
committed
More robust _guide_kw management
1 parent c16e8cf commit 9bded5a

File tree

4 files changed

+58
-39
lines changed

4 files changed

+58
-39
lines changed

proplot/axes/base.py

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1567,26 +1567,29 @@ def _register_guide(self, guide, obj, key, **kwargs):
15671567
kwargs_full.update(kwargs)
15681568

15691569
def _update_guide(
1570-
self, objs, colorbar=None, colorbar_kw=None, queue_colorbar=True,
1571-
legend=None, legend_kw=None,
1570+
self, objs, legend=None, legend_kw=None, queue_legend=True,
1571+
colorbar=None, colorbar_kw=None, queue_colorbar=True,
15721572
):
15731573
"""
15741574
Update queues for on-the-fly legends and colorbars or track keyword arguments.
15751575
"""
1576+
# WARNING: Important to always cache the keyword arguments so e.g.
1577+
# duplicate subsequent calls still enforce user and default behavior.
15761578
# WARNING: This should generally be last in the pipeline before calling
15771579
# the plot function or looping over data columns. The colormap parser
15781580
# and standardize functions both modify colorbar_kw and legend_kw.
15791581
legend_kw = legend_kw or {}
15801582
colorbar_kw = colorbar_kw or {}
1581-
if colorbar:
1582-
colorbar_kw.setdefault('queue', queue_colorbar)
1583-
self.colorbar(objs, loc=colorbar, **colorbar_kw)
1584-
else: # store keyword arguments for later
1585-
guides._cache_guide_kw(objs, 'colorbar', colorbar_kw)
1583+
guides._cache_guide_kw(objs, 'legend', legend_kw)
1584+
guides._cache_guide_kw(objs, 'colorbar', colorbar_kw)
15861585
if legend:
1587-
self.legend(objs, loc=legend, queue=True, **legend_kw)
1588-
else: # store keyword arguments for later
1589-
guides._cache_guide_kw(objs, 'legend', legend_kw)
1586+
align = legend_kw.pop('align', None)
1587+
queue = legend_kw.pop('queue', queue_legend)
1588+
self.legend(objs, loc=legend, align=align, queue=queue, **legend_kw)
1589+
if colorbar:
1590+
align = colorbar_kw.pop('align', None)
1591+
queue = colorbar_kw.pop('queue', queue_colorbar)
1592+
self.colorbar(objs, loc=colorbar, align=align, queue=queue, **colorbar_kw)
15901593

15911594
@staticmethod
15921595
def _parse_frame(guide, fancybox=None, shadow=None, **kwargs):
@@ -2792,10 +2795,7 @@ def panel_axes(self, side=None, **kwargs):
27922795

27932796
@docstring._obfuscate_params
27942797
@docstring._snippet_manager
2795-
def colorbar(
2796-
self, mappable, values=None,
2797-
loc=None, location=None, align=None, queue=False, **kwargs
2798-
):
2798+
def colorbar(self, mappable, values=None, loc=None, location=None, **kwargs):
27992799
"""
28002800
Add an inset colorbar or an outer colorbar along the edge of the axes.
28012801
@@ -2848,30 +2848,31 @@ def colorbar(
28482848
proplot.figure.Figure.colorbar
28492849
matplotlib.figure.Figure.colorbar
28502850
"""
2851-
# Either draw right now or queue up for later
2852-
# The queue option lets us successively append objects (e.g. line handles)
2853-
# to a list later used for colorbar levels. Same as legend.
2854-
loc = _not_none(loc=loc, location=location)
2851+
# Translate location and possibly infer from orientation. Also optionally
2852+
# infer align setting from keywords stored on object.
28552853
orientation = kwargs.get('orientation', None)
2854+
kwargs = guides._flush_guide_kw(mappable, 'colorbar', kwargs)
2855+
loc = _not_none(loc=loc, location=location)
28562856
if orientation is not None: # possibly infer loc from orientation
28572857
if orientation not in ('vertical', 'horizontal'):
28582858
raise ValueError(f"Invalid colorbar orientation {orientation!r}. Must be 'vertical' or 'horizontal'.") # noqa: E501
28592859
if loc is None:
28602860
loc = {'vertical': 'right', 'horizontal': 'bottom'}[orientation]
28612861
loc = _translate_loc(loc, 'colorbar', default=rc['colorbar.loc'])
2862-
align = _translate_loc(align, 'panel', default='center', c='center', center='center') # noqa: E501
2863-
kwargs = guides._flush_guide_kw(mappable, 'colorbar', kwargs)
2862+
align = kwargs.pop('align', None)
2863+
align = _translate_loc(align, 'align', default='center')
2864+
2865+
# Either draw right now or queue up for later. The queue option lets us
2866+
# successively append objects (e.g. lines) to a colorbar artist list.
2867+
queue = kwargs.pop('queue', False)
28642868
if queue:
28652869
self._register_guide('colorbar', (mappable, values), (loc, align), **kwargs)
28662870
else:
28672871
return self._add_colorbar(mappable, values, loc=loc, align=align, **kwargs)
28682872

28692873
@docstring._concatenate_inherited # also obfuscates params
28702874
@docstring._snippet_manager
2871-
def legend(
2872-
self, handles=None, labels=None,
2873-
loc=None, location=None, align=None, queue=False, **kwargs
2874-
):
2875+
def legend(self, handles=None, labels=None, loc=None, location=None, **kwargs):
28752876
"""
28762877
Add an *inset* legend or *outer* legend along the edge of the axes.
28772878
@@ -2919,13 +2920,17 @@ def legend(
29192920
proplot.figure.Figure.legend
29202921
matplotlib.axes.Axes.legend
29212922
"""
2922-
# Either draw right now or queue up for later
2923-
# Handles can be successively added to a single location this way. This
2924-
# is used internally for on-the-fly legends.
2923+
# Translate location and possibly infer from orientation. Also optionally
2924+
# infer align setting from keywords stored on object.
2925+
kwargs = guides._flush_guide_kw(handles, 'legend', kwargs)
29252926
loc = _not_none(loc=loc, location=location)
29262927
loc = _translate_loc(loc, 'legend', default=rc['legend.loc'])
2927-
align = _translate_loc(align, 'panel', default='center', c='center', center='center') # noqa: E501
2928-
kwargs = guides._flush_guide_kw(handles, 'legend', kwargs)
2928+
align = kwargs.pop('align', None)
2929+
align = _translate_loc(align, 'align', default='center')
2930+
2931+
# Either draw right now or queue up for later. Handles can be successively
2932+
# added to a single location this way. Used for on-the-fly legends.
2933+
queue = kwargs.pop('queue', False)
29292934
if queue:
29302935
self._register_guide('legend', (handles, labels), (loc, align), **kwargs)
29312936
else:

proplot/internals/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,7 +391,9 @@ def _translate_loc(loc, mode, *, default=None, **kwargs):
391391
# Create specific options dictionary
392392
# NOTE: This is not inside validators.py because it is also used to
393393
# validate various user-input locations.
394-
if mode == 'panel':
394+
if mode == 'align':
395+
loc_dict = rcsetup.ALIGN_LOCS
396+
elif mode == 'panel':
395397
loc_dict = rcsetup.PANEL_LOCS
396398
elif mode == 'legend':
397399
loc_dict = rcsetup.LEGEND_LOCS

proplot/internals/guides.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,16 @@
1111
from . import ic # noqa: F401
1212
from . import warnings
1313

14+
# Global constants
15+
REMOVE_AFTER_FLUSH = (
16+
'pad', 'space', 'width', 'length', 'shrink', 'align', 'queue',
17+
)
18+
GUIDE_ALIASES = (
19+
('title', 'label'),
20+
('locator', 'ticks'),
21+
('format', 'formatter', 'ticklabels')
22+
)
23+
1424

1525
def _add_guide_kw(name, kwargs, **opts):
1626
"""
@@ -50,6 +60,8 @@ def _flush_guide_kw(obj, name, kwargs):
5060
opts = getattr(obj, f'_{name}_kw', None)
5161
if opts:
5262
_update_kw(kwargs, overwrite=False, **opts)
63+
for key in REMOVE_AFTER_FLUSH:
64+
opts.pop(key, None)
5365
if isinstance(obj, (tuple, list, np.ndarray)):
5466
for member in obj: # possibly iterate over matplotlib tuple/list subclasses
5567
_flush_guide_kw(member, name, kwargs)
@@ -60,15 +72,10 @@ def _update_kw(kwargs, overwrite=False, **opts):
6072
"""
6173
Add the keyword arguments to the dictionary if not already present.
6274
"""
63-
aliases = (
64-
('title', 'label'),
65-
('locator', 'ticks'),
66-
('format', 'formatter', 'ticklabels')
67-
)
6875
for key, value in opts.items():
6976
if value is None:
7077
continue
71-
keys = tuple(k for opts in aliases for k in opts if key in opts)
78+
keys = tuple(k for opts in GUIDE_ALIASES for k in opts if key in opts)
7279
keys = keys or (key,) # e.g. 'extend' or something
7380
keys_found = tuple(key for key in keys if kwargs.get(key) is not None)
7481
if not keys_found:

proplot/internals/rcsetup.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -106,24 +106,29 @@
106106
if _loc not in LEGEND_LOCS:
107107
LEGEND_LOCS[_loc] = _loc # identity assignments
108108
TEXT_LOCS = {
109-
key: value for key, value in LEGEND_LOCS.items() if value in (
109+
key: val for key, val in LEGEND_LOCS.items() if val in (
110110
'left', 'center', 'right',
111111
'upper left', 'upper center', 'upper right',
112112
'lower left', 'lower center', 'lower right',
113113
)
114114
}
115115
COLORBAR_LOCS = {
116-
key: value for key, value in LEGEND_LOCS.items() if value in (
116+
key: val for key, val in LEGEND_LOCS.items() if val in (
117117
'fill', 'best',
118118
'left', 'right', 'top', 'bottom',
119119
'upper left', 'upper right', 'lower left', 'lower right',
120120
)
121121
}
122122
PANEL_LOCS = {
123-
key: value for key, value in LEGEND_LOCS.items() if value in (
123+
key: val for key, val in LEGEND_LOCS.items() if val in (
124124
'left', 'right', 'top', 'bottom'
125125
)
126126
}
127+
ALIGN_LOCS = {
128+
key: val for key, val in LEGEND_LOCS.items() if isinstance(key, str) and val in (
129+
'left', 'right', 'top', 'bottom', 'center',
130+
)
131+
}
127132

128133
# Matplotlib setting categories
129134
EM_KEYS = ( # em-width units

0 commit comments

Comments
 (0)