@@ -251,8 +251,8 @@ def __init__(self, *args, number=None, main=False, _subplotspec=None, **kwargs):
251251 self .figure ._axes_main .append (self )
252252
253253 # On-the-fly legends and colorbars
254- self ._auto_colorbar = {}
255- self ._auto_legend = {}
254+ self ._queued_colorbars = {}
255+ self ._queued_legends = {}
256256
257257 # Figure row and column labels
258258 # NOTE: Most of these sit empty for most subplots
@@ -370,18 +370,20 @@ def shared(paxs):
370370 for child in children :
371371 child ._sharey_setup (parent )
372372
373- def _draw_auto_legends_colorbars (self ):
373+ def _draw_queued_colorbars_legends (self ):
374374 """
375375 Generate automatic legends and colorbars. Wrapper funcs
376376 let user add handles to location lists with successive calls to
377377 make successive calls to plotting commands.
378378 """
379- for loc , (handles , kwargs ) in self ._auto_colorbar .items ():
379+ for loc , (handles , kwargs ) in self ._queued_colorbars .items ():
380+ kwargs .setdefault ('loc' , loc )
380381 self .colorbar (handles , ** kwargs )
381- for loc , (handles , kwargs ) in self ._auto_legend .items ():
382- self .legend (handles , ** kwargs )
383- self ._auto_legend = {}
384- self ._auto_colorbar = {}
382+ for loc , (handles , kwargs ) in self ._queued_legends .items ():
383+ kwargs .setdefault ('loc' , loc )
384+ self .legend (handles , queue = False , ** kwargs )
385+ self ._queued_legends = {}
386+ self ._queued_colorbars = {}
385387
386388 def _get_extent_axes (self , x , panels = False ):
387389 """
@@ -468,7 +470,7 @@ def _is_panel_group_member(self, other):
468470 or other ._panel_parent is self ._panel_parent # sibling
469471 )
470472
471- def _loc_translate (self , loc , mode = None , allow_manual = True ):
473+ def _loc_translate (self , loc , mode = None ):
472474 """
473475 Return the location string `loc` translated into a standardized form.
474476 """
@@ -510,13 +512,12 @@ def _loc_translate(self, loc, mode=None, allow_manual=True):
510512 + ', ' .join (map (repr , loc_translate )) + '.'
511513 )
512514 elif (
513- allow_manual
514- and mode == 'legend'
515+ mode == 'legend'
515516 and np .iterable (loc )
516517 and len (loc ) == 2
517518 and all (isinstance (l , Number ) for l in loc )
518519 ):
519- loc = np . array (loc )
520+ loc = tuple (loc )
520521 else :
521522 raise KeyError (f'Invalid { mode } location { loc !r} .' )
522523 if mode == 'colorbar' and loc == 'best' : # white lie
@@ -535,6 +536,30 @@ def inset_locator(ax, renderer):
535536 return bb
536537 return inset_locator
537538
539+ def _queue_driver (self , legend , loc , * args , ** kwargs ):
540+ """
541+ Driver function.
542+ """
543+ database = self ._queued_legends if legend else self ._queued_colorbars
544+ database .setdefault (loc , ([], {}))
545+ database [loc ][0 ].extend (args )
546+ if legend and 'labels' in kwargs :
547+ database [loc ][1 ].setdefault ('labels' , [])
548+ database [loc ][1 ]['labels' ].extend (kwargs .pop ('labels' ))
549+ database [loc ][1 ].update (kwargs )
550+
551+ def _queue_colorbar (self , * args , ** kwargs ):
552+ """
553+ Queue up objects for list-of-artist style colorbars.
554+ """
555+ self ._queue_driver (False , * args , ** kwargs )
556+
557+ def _queue_legend (self , * args , ** kwargs ):
558+ """
559+ Queues up objects for legends.
560+ """
561+ self ._queue_driver (True , * args , ** kwargs )
562+
538563 def _range_gridspec (self , x ):
539564 """
540565 Return the column or row gridspec range for the axes.
@@ -1233,18 +1258,12 @@ def colorbar(
12331258
12341259 # Inset colorbar
12351260 else :
1236- # Default props
1261+ # Default properties
12371262 cbwidth , cblength = width , length
12381263 width , height = self .get_size_inches ()
1239- extend = units (_not_none (
1240- kwargs .get ('extendsize' , None ), rc ['colorbar.insetextend' ]
1241- ))
1242- cbwidth = units (_not_none (
1243- cbwidth , rc ['colorbar.insetwidth' ]
1244- )) / height
1245- cblength = units (_not_none (
1246- cblength , rc ['colorbar.insetlength' ]
1247- )) / width
1264+ cbwidth = units (_not_none (cbwidth , rc ['colorbar.insetwidth' ])) / height
1265+ cblength = units (_not_none (cblength , rc ['colorbar.insetlength' ])) / width
1266+ extend = units (_not_none (kwargs .get ('extendsize' , None ), rc ['colorbar.insetextend' ])) # noqa: E501
12481267 pad = units (_not_none (pad , rc ['colorbar.insetpad' ]))
12491268 xpad , ypad = pad / width , pad / height
12501269
@@ -1257,21 +1276,18 @@ def colorbar(
12571276 xspace += 1.2 * rc ['font.size' ] / 72
12581277 xspace /= height # space for labels
12591278 if loc == 'upper right' :
1260- bounds = (1 - xpad - cblength , 1 - ypad - cbwidth )
1261- fbounds = (
1262- 1 - 2 * xpad - cblength ,
1263- 1 - 2 * ypad - cbwidth - xspace
1264- )
1279+ ibounds = (1 - xpad - cblength , 1 - ypad - cbwidth )
1280+ fbounds = (1 - 2 * xpad - cblength , 1 - 2 * ypad - cbwidth - xspace )
12651281 elif loc == 'upper left' :
1266- bounds = (xpad , 1 - ypad - cbwidth )
1282+ ibounds = (xpad , 1 - ypad - cbwidth )
12671283 fbounds = (0 , 1 - 2 * ypad - cbwidth - xspace )
12681284 elif loc == 'lower left' :
1269- bounds = (xpad , ypad + xspace )
1285+ ibounds = (xpad , ypad + xspace )
12701286 fbounds = (0 , 0 )
12711287 else :
1272- bounds = (1 - xpad - cblength , ypad + xspace )
1288+ ibounds = (1 - xpad - cblength , ypad + xspace )
12731289 fbounds = (1 - 2 * xpad - cblength , 0 )
1274- bounds = (bounds [0 ], bounds [1 ], cblength , cbwidth )
1290+ ibounds = (ibounds [0 ], ibounds [1 ], cblength , cbwidth )
12751291 fbounds = (
12761292 fbounds [0 ], fbounds [1 ],
12771293 2 * xpad + cblength , 2 * ypad + cbwidth + xspace
@@ -1280,9 +1296,7 @@ def colorbar(
12801296 # Make frame
12811297 # NOTE: We do not allow shadow effects or fancy edges effect.
12821298 # Also keep zorder same as with legend.
1283- frameon = _not_none (
1284- frame = frame , frameon = frameon , default = rc ['colorbar.frameon' ],
1285- )
1299+ frameon = _not_none (frame = frame , frameon = frameon , default = rc ['colorbar.frameon' ]) # noqa: E501
12861300 if frameon :
12871301 xmin , ymin , width , height = fbounds
12881302 patch = mpatches .Rectangle (
@@ -1303,7 +1317,7 @@ def colorbar(
13031317
13041318 # Make axes
13051319 from .cartesian import CartesianAxes
1306- locator = self ._make_inset_locator (bounds , self .transAxes )
1320+ locator = self ._make_inset_locator (ibounds , self .transAxes )
13071321 bbox = locator (None , None )
13081322 ax = CartesianAxes (self .figure , bbox .bounds , zorder = 5 )
13091323 ax .set_axes_locator (locator )
@@ -1322,16 +1336,14 @@ def colorbar(
13221336 warnings ._warn_proplot (
13231337 'Inset colorbars can only have ticks on the bottom.'
13241338 )
1325- kwargs .update ({
1326- 'orientation' : 'horizontal' , 'ticklocation' : 'bottom'
1327- })
1339+ kwargs .update ({'orientation' : 'horizontal' , 'ticklocation' : 'bottom' })
13281340 kwargs .setdefault ('maxn' , 5 )
13291341 kwargs .setdefault ('extendsize' , extend )
13301342
13311343 # Generate colorbar
13321344 return colorbar_wrapper (ax , * args , ** kwargs )
13331345
1334- def legend (self , * args , loc = None , width = None , space = None , ** kwargs ):
1346+ def legend (self , * args , loc = None , width = None , space = None , queue = True , ** kwargs ):
13351347 """
13361348 Add an *inset* legend or *outer* legend along the edge of the axes.
13371349 See `~proplot.axes.legend_wrapper` for details.
@@ -1372,6 +1384,10 @@ def legend(self, *args, loc=None, width=None, space=None, **kwargs):
13721384 box. Units are interpreted by `~proplot.utils.units`.
13731385 When :rcraw:`tight` is ``True``, this is adjusted automatically.
13741386 Otherwise, the default is :rc:`subplots.panelpad`.
1387+ queue : bool, optional
1388+ Whether to queue the legend or draw it immediately. Default is ``True``.
1389+ This lets you successively add items to the legend in location `loc`
1390+ by calling e.g. `ax.legend(h, loc=loc)` more than once.
13751391
13761392 Other parameters
13771393 ----------------
@@ -1415,8 +1431,21 @@ def legend(self, *args, loc=None, width=None, space=None, **kwargs):
14151431 else :
14161432 raise ValueError (f'Invalid panel side { side !r} .' )
14171433
1418- # Draw legend
1419- return legend_wrapper (self , * args , loc = loc , ** kwargs )
1434+ # Queue legend so that objects can be successively added (like matplotlib but
1435+ # expanding this feature so it works with multiple legends in same location)
1436+ if not args :
1437+ objs = []
1438+ elif len (args ) == 1 :
1439+ objs = args [0 ]
1440+ elif len (args ) == 2 :
1441+ objs = args [0 ]
1442+ kwargs .setdefault ('labels' , args [1 ])
1443+ else :
1444+ raise TypeError (f'Too many arguments. Expected 1 or 2, got { len (args )} .' )
1445+ if queue :
1446+ self ._queue_legend (loc , * objs , ** kwargs )
1447+ else :
1448+ return legend_wrapper (self , * args , loc = loc , ** kwargs )
14201449
14211450 def draw (self , renderer = None , * args , ** kwargs ):
14221451 # Perform extra post-processing steps
0 commit comments