diff --git a/doc/users/next_whats_new/comapss_notation.rst b/doc/users/next_whats_new/comapss_notation.rst new file mode 100644 index 000000000000..fdbcabb3d118 --- /dev/null +++ b/doc/users/next_whats_new/comapss_notation.rst @@ -0,0 +1,62 @@ +:orphan: + +Compass notation for legend and other anchored artists +------------------------------------------------------ + +The ``loc`` parameter for legends and other anchored artists now accepts +"compass" strings. E.g. to locate such element in the upper right corner, +in addition to ``'upper right'`` and ``1``, you can now use ``'NE'`` as +well as ``'northeast'``. This satisfies the wish for more intuitive and +unambiguous location of legends. The following (case-sensitive) location +specifications are now allowed. + + ============ ============== =============== ============= + Compass Code Compass String Location String Location Code + ============ ============== =============== ============= + .. 'best' 0 + 'NE' 'northeast' 'upper right' 1 + 'NW' 'northwest' 'upper left' 2 + 'SW' 'southwest' 'lower left' 3 + 'SE' 'southeast' 'lower right' 4 + .. 'right' 5 + 'W' 'west' 'center left' 6 + 'E' 'east' 'center right' 7 + 'S' 'south' 'lower center' 8 + 'N' 'north' 'upper center' 9 + 'C' 'center' 'center' 10 + ============ ============== =============== ============= + +Those apply to + + * the axes legends; `matplotlib.pyplot.legend` and + `matplotlib.axes.Axes.legend`, + +and, with the exception of ``'best'`` and ``0``, to + + * the figure legends; `matplotlib.pyplot.figlegend` and + `matplotlib.figure.Figure.legend`, as well as the general + `matplotlib.legend.Legend` class, + * the `matplotlib.offsetbox`'s `matplotlib.offsetbox.AnchoredOffsetbox` and + `matplotlib.offsetbox.AnchoredText`, + * the `mpl_toolkits.axes_grid1.anchored_artists`'s + `~.AnchoredDrawingArea`, `~.AnchoredAuxTransformBox`, + `~.AnchoredEllipse`, `~.AnchoredSizeBar`, `~.AnchoredDirectionArrows` + * the `mpl_toolkits.axes_grid1.inset_locator`'s + `~.axes_grid1.inset_locator.inset_axes`, + `~.axes_grid1.inset_locator.zoomed_inset_axes` and the + `~.axes_grid1.inset_locator.AnchoredSizeLocator` and + `~.axes_grid1.inset_locator.AnchoredZoomLocator` + +Note that those new compass strings *do not* apply to ``table``. + + +Getter/setter for legend and other anchored artists location +------------------------------------------------------------ + +The above mentioned classes (in particular `~.legend.Legend`, +`~.offsetbox.AnchoredOffsetbox`, `~.offsetbox.AnchoredText` etc.) +now have a getter/setter for the location. +This allows to e.g. change the location *after* creating a legend:: + + legend = ax.legend(loc="west") + legend.set_loc("southeast") diff --git a/lib/matplotlib/cbook/__init__.py b/lib/matplotlib/cbook/__init__.py index e935ceed2bad..4063013a8b6f 100644 --- a/lib/matplotlib/cbook/__init__.py +++ b/lib/matplotlib/cbook/__init__.py @@ -2156,3 +2156,86 @@ def _check_in_list(values, **kwargs): raise ValueError( "{!r} is not a valid value for {}; supported values are {}" .format(v, k, ', '.join(map(repr, values)))) + + +# Theses are the valid compass notation codes used internally by legend +# and other anchored boxes. +_COMPASS_LOCS = ['NE', 'NW', 'SW', 'SE', 'E', 'W', 'S', 'N', 'C'] + + +def _map_loc_to_compass(loc, **kwargs): + """ + Map a location (string) to a compass notation string. This is used by + AnchoredOffsetbox and Legend. + + loc : A location, like 'upper right', 'NE', 'northeast', 1 + allowtuple : bool, Whether to allow a tuple of numbers like (0.5, 0.2). + This is useful for legends; other artists may not allow this. + allowbest : bool, Whether to allow for 'best' or 0 as input for loc. + warnonly : bool, if True, warn on invalid input and use fallback, + if False, error out. + fallback : The fallback return value in case of warnonly=True. + asrcparam : string, Use if this function is used as validator for rcParams + """ + codes = { + 'upper right': 'NE', 'northeast': 'NE', 1: 'NE', + 'upper left': 'NW', 'northwest': 'NW', 2: 'NW', + 'lower left': 'SW', 'southwest': 'SW', 3: 'SW', + 'lower right': 'SE', 'southeast': 'SE', 4: 'SE', + 'right': 'E', 5: 'E', + 'center left': 'W', 'west': 'W', 6: 'W', + 'center right': 'E', 'east': 'E', 7: 'E', + 'lower center': 'S', 'south': 'S', 8: 'S', + 'upper center': 'N', 'north': 'N', 9: 'N', + 'center': 'C', 10: 'C' + } + + allowtuple = kwargs.get("allowtuple", False) + allowbest = kwargs.get("allowbest", False) + fallback = kwargs.get("fallback", 'NE') + warnonly = kwargs.get("warnonly", False) + asrcparam = kwargs.get("asrcparam", None) + + if allowbest: + codes.update({'best': 'best', 0: 'best'}) + + if loc in _COMPASS_LOCS: + return loc + + if isinstance(loc, str) or isinstance(loc, int): + if loc in codes: + return codes[loc] + + if allowtuple: + if hasattr(loc, '__len__') and len(loc) == 2: + x, y = loc[0], loc[1] + if isinstance(x, numbers.Number) and isinstance(y, numbers.Number): + return tuple((x, y)) + + msg = "Unrecognized location {!r}. ".format(loc) + if asrcparam: + msg += "This is no valid rc parameter for {!r}. ".format(asrcparam) + if isinstance(loc, str): + if loc.lower() in codes: + fallback = codes[loc.lower()] + elif loc.upper() in _COMPASS_LOCS: + fallback = loc.upper() + msg += "Location strings are now case-sensitive. " + if warnonly: + msg += "Falling back on {!r}. ".format(fallback) + if not allowbest and loc in [0, 'best']: + msg += "Automatic legend placement (loc='best') is not " + msg += "implemented for figure legends or other artists. " + vcodes = [k for k in codes if not isinstance(k, int)] + _COMPASS_LOCS + msg += "Valid locations are '{}'".format("', '".join(vcodes)) + if not asrcparam: + startn = 0 if allowbest else 1 + msg += " as well as the numbers {} to 10. ".format(startn) + if allowtuple: + msg += " In addition a tuple (x,y) of coordinates can be supplied." + if warnonly: + msg += " This will raise an exception %(removal)s." + warn_deprecated("3.1", message=msg) + return fallback + else: + raise ValueError(msg) diff --git a/lib/matplotlib/legend.py b/lib/matplotlib/legend.py index d2c07f562478..1df362252122 100644 --- a/lib/matplotlib/legend.py +++ b/lib/matplotlib/legend.py @@ -25,11 +25,11 @@ import numpy as np -from matplotlib import cbook from matplotlib import rcParams from matplotlib import cbook, docstring from matplotlib.artist import Artist, allow_rasterization -from matplotlib.cbook import silent_list, is_hashable, warn_deprecated +from matplotlib.cbook import (silent_list, is_hashable, warn_deprecated, + _COMPASS_LOCS, _map_loc_to_compass) from matplotlib.font_manager import FontProperties from matplotlib.lines import Line2D from matplotlib.patches import Patch, Rectangle, Shadow, FancyBboxPatch @@ -115,13 +115,14 @@ def _update_bbox_to_anchor(self, loc_in_canvas): The location of the legend. The strings - ``'upper left', 'upper right', 'lower left', 'lower right'`` - place the legend at the corresponding corner of the axes/figure. + ``'upper left', 'upper right', 'lower left', 'lower right'`` and their + corresponding compass strings (see table below) place the legend at the + respective corner of the axes/figure. The strings - ``'upper center', 'lower center', 'center left', 'center right'`` - place the legend at the center of the corresponding edge of the - axes/figure. + ``'upper center', 'lower center', 'center left', 'center right'`` and their + corresponding compass strings place the legend at the center of the + respective edge of the axes/figure. The string ``'center'`` places the legend at the center of the axes/figure. @@ -130,29 +131,32 @@ def _update_bbox_to_anchor(self, loc_in_canvas): artists. This option can be quite slow for plots with large amounts of data; your plotting speed may benefit from providing a specific location. + For backward-compatibility, ``'right'`` is equivalent to + ``'center right'``; and there are numeric codes for all locations as well. + + Possible (case-sensitive) strings and codes are: + + ============ ============== =============== ============= + Compass Code Compass String Location String Location Code + ============ ============== =============== ============= + .. 'best' 0 + 'NE' 'northeast' 'upper right' 1 + 'NW' 'northwest' 'upper left' 2 + 'SW' 'southwest' 'lower left' 3 + 'SE' 'southeast' 'lower right' 4 + .. 'right' 5 + 'W' 'west' 'center left' 6 + 'E' 'east' 'center right' 7 + 'S' 'south' 'lower center' 8 + 'N' 'north' 'upper center' 9 + 'C' 'center' 'center' 10 + ============ ============== =============== ============= + + The location can also be a 2-tuple giving the coordinates of the lower-left corner of the legend in axes coordinates (in which case *bbox_to_anchor* will be ignored). - For back-compatibility, ``'center right'`` (but no other location) can also - be spelled ``'right'``, and each "string" locations can also be given as a - numeric value: - - =============== ============= - Location String Location Code - =============== ============= - 'best' 0 - 'upper right' 1 - 'upper left' 2 - 'lower left' 3 - 'lower right' 4 - 'right' 5 - 'center left' 6 - 'center right' 7 - 'lower center' 8 - 'upper center' 9 - 'center' 10 - =============== ============= bbox_to_anchor : `.BboxBase`, 2-tuple, or 4-tuple of floats Box that is used to position the legend in conjunction with *loc*. @@ -323,18 +327,6 @@ class Legend(Artist): Place a legend on the axes at location loc. """ - codes = {'best': 0, # only implemented for axes legends - 'upper right': 1, - 'upper left': 2, - 'lower left': 3, - 'lower right': 4, - 'right': 5, - 'center left': 6, - 'center right': 7, - 'lower center': 8, - 'upper center': 9, - 'center': 10, - } zorder = 5 @@ -499,36 +491,6 @@ def __init__(self, parent, handles, labels, raise TypeError("Legend needs either Axes or Figure as parent") self.parent = parent - self._loc_used_default = loc is None - if loc is None: - loc = rcParams["legend.loc"] - if not self.isaxes and loc in [0, 'best']: - loc = 'upper right' - if isinstance(loc, str): - if loc not in self.codes: - if self.isaxes: - cbook.warn_deprecated( - "3.1", message="Unrecognized location {!r}. Falling " - "back on 'best'; valid locations are\n\t{}\n" - "This will raise an exception %(removal)s." - .format(loc, '\n\t'.join(self.codes))) - loc = 0 - else: - cbook.warn_deprecated( - "3.1", message="Unrecognized location {!r}. Falling " - "back on 'upper right'; valid locations are\n\t{}\n'" - "This will raise an exception %(removal)s." - .format(loc, '\n\t'.join(self.codes))) - loc = 1 - else: - loc = self.codes[loc] - if not self.isaxes and loc == 0: - cbook.warn_deprecated( - "3.1", message="Automatic legend placement (loc='best') not " - "implemented for figure legend. Falling back on 'upper " - "right'. This will raise an exception %(removal)s.") - loc = 1 - self._mode = mode self.set_bbox_to_anchor(bbox_to_anchor, bbox_transform) @@ -574,6 +536,10 @@ def __init__(self, parent, handles, labels, # init with null renderer self._init_legend_box(handles, labels, markerfirst) + # location must be set after _legend_box is created. + self._loc_used_default = loc is None + self._loc = loc + # If shadow is activated use framealpha if not # explicitly passed. See Issue 8943 if framealpha is None: @@ -584,10 +550,6 @@ def __init__(self, parent, handles, labels, else: self.get_frame().set_alpha(framealpha) - tmp = self._loc_used_default - self._set_loc(loc) - self._loc_used_default = tmp # ignore changes done by _set_loc - # figure out title fontsize: if title_fontsize is None: title_fontsize = rcParams['legend.title_fontsize'] @@ -608,10 +570,19 @@ def _set_artist_props(self, a): a.set_transform(self.get_transform()) def _set_loc(self, loc): + if loc is None: + loc = rcParams["legend.loc"] + if not self.isaxes and loc in [0, 'best']: + loc = 'NE' + if self.isaxes: + loc = _map_loc_to_compass(loc, allowtuple=True, allowbest=True, + fallback="best", warnonly=True) + else: + loc = _map_loc_to_compass(loc, allowtuple=True, allowbest=False, + fallback="NE", warnonly=True) # find_offset function will be provided to _legend_box and # _legend_box will draw itself at the location of the return # value of the find_offset. - self._loc_used_default = False self._loc_real = loc self.stale = True self._legend_box.set_offset(self._findoffset) @@ -621,12 +592,29 @@ def _get_loc(self): _loc = property(_get_loc, _set_loc) + # public getters and setters should be introduced in the next release + # see https://github.com/matplotlib/matplotlib/pull/12679 + #def set_loc(self, loc): + # """ + # Set the legend location. For possible values see the `~.Axes.legend` + # docstring. + # """ + # self._loc_used_default = False + # self._set_loc(loc) + # + #def get_loc(self): + # """ + # Get the legend location. This will be one of 'best', 'NE', 'NW', + # 'SW', 'SE', 'E', 'W', 'E', 'S', 'N', 'C' or a tuple of floats. + # """ + # return self._get_loc() + def _findoffset(self, width, height, xdescent, ydescent, renderer): "Helper function to locate the legend." - if self._loc == 0: # "best". + if self._loc == 'best': x, y = self._find_best_position(width, height, renderer) - elif self._loc in Legend.codes.values(): # Fixed location. + elif isinstance(self._loc, str): # Fixed location. bbox = Bbox.from_bounds(0, 0, width, height) x, y = self._get_anchored_bbox(self._loc, bbox, self.get_bbox_to_anchor(), @@ -1087,34 +1075,19 @@ def _get_anchored_bbox(self, loc, bbox, parentbbox, renderer): Place the *bbox* inside the *parentbbox* according to a given location code. Return the (x,y) coordinate of the bbox. - - loc: a location code in range(1, 11). - This corresponds to the possible values for self._loc, excluding - "best". + - loc: a location code, one of 'NE', 'NW', 'SW', 'SE', 'E', + 'W', 'S', 'N', 'C'. - bbox: bbox to be placed, display coordinate units. - parentbbox: a parent box which will contain the bbox. In display coordinates. """ - assert loc in range(1, 11) # called only internally - - BEST, UR, UL, LL, LR, R, CL, CR, LC, UC, C = range(11) - - anchor_coefs = {UR: "NE", - UL: "NW", - LL: "SW", - LR: "SE", - R: "E", - CL: "W", - CR: "E", - LC: "S", - UC: "N", - C: "C"} - c = anchor_coefs[loc] + assert loc in _COMPASS_LOCS # as this is called only internally fontsize = renderer.points_to_pixels(self._fontsize) container = parentbbox.padded(-(self.borderaxespad) * fontsize) - anchored_box = bbox.anchored(c, container=container) + anchored_box = bbox.anchored(loc, container=container) return anchored_box.x0, anchored_box.y0 def _find_best_position(self, width, height, renderer, consider=None): @@ -1137,10 +1110,10 @@ def _find_best_position(self, width, height, renderer, consider=None): bbox = Bbox.from_bounds(0, 0, width, height) if consider is None: - consider = [self._get_anchored_bbox(x, bbox, + consider = [self._get_anchored_bbox(loc, bbox, self.get_bbox_to_anchor(), renderer) - for x in range(1, len(self.codes))] + for loc in _COMPASS_LOCS] candidates = [] for idx, (l, b) in enumerate(consider): diff --git a/lib/matplotlib/offsetbox.py b/lib/matplotlib/offsetbox.py index 70a6d9c54f8b..7365bc1f8475 100644 --- a/lib/matplotlib/offsetbox.py +++ b/lib/matplotlib/offsetbox.py @@ -27,7 +27,7 @@ FancyBboxPatch, FancyArrowPatch, bbox_artist as mbbox_artist) from matplotlib.text import _AnnotationBase from matplotlib.transforms import Bbox, BboxBase, TransformedBbox - +from matplotlib.cbook import (_COMPASS_LOCS, _map_loc_to_compass) DEBUG = False @@ -963,19 +963,6 @@ class AnchoredOffsetbox(OffsetBox): """ zorder = 5 # zorder of the legend - # Location codes - codes = {'upper right': 1, - 'upper left': 2, - 'lower left': 3, - 'lower right': 4, - 'right': 5, - 'center left': 6, - 'center right': 7, - 'lower center': 8, - 'upper center': 9, - 'center': 10, - } - def __init__(self, loc, pad=0.4, borderpad=0.5, child=None, prop=None, frameon=True, @@ -983,19 +970,26 @@ def __init__(self, loc, bbox_transform=None, **kwargs): """ - loc is a string or an integer specifying the legend location. + loc is a string or an integer specifying the box location. The valid location codes are:: - 'upper right' : 1, - 'upper left' : 2, - 'lower left' : 3, - 'lower right' : 4, - 'right' : 5, (same as 'center right', for back-compatibility) - 'center left' : 6, - 'center right' : 7, - 'lower center' : 8, - 'upper center' : 9, - 'center' : 10, + ============ ============== =============== ============= + Compass Code Compass String Location String Location Code + ============ ============== =============== ============= + 'NE' 'northeast' 'upper right' 1 + 'NW' 'northwest' 'upper left' 2 + 'SW' 'southwest' 'lower left' 3 + 'SE' 'southeast' 'lower right' 4 + .. 'right' 5 + 'W' 'west' 'center left' 6 + 'E' 'east' 'center right' 7 + 'S' 'south' 'lower center' 8 + 'N' 'north' 'upper center' 9 + 'C' 'center' 'center' 10 + ============ ============== =============== ============= + + 'rigth' (5) and 'center right' (7) are identical locations. See + `~.Axes.legend` docstring for explanation. pad : pad around the child for drawing a frame. given in fraction of fontsize. @@ -1018,14 +1012,6 @@ def __init__(self, loc, self.set_bbox_to_anchor(bbox_to_anchor, bbox_transform) self.set_child(child) - if isinstance(loc, str): - try: - loc = self.codes[loc] - except KeyError: - raise ValueError('Unrecognized location "%s". Valid ' - 'locations are\n\t%s\n' - % (loc, '\n\t'.join(self.codes))) - self.loc = loc self.borderpad = borderpad self.pad = pad @@ -1048,6 +1034,23 @@ def __init__(self, loc, self.patch.set_boxstyle("square", pad=0) self._drawFrame = frameon + def set_loc(self, loc): + """ + Set the box location. For possible values see the `~.AnchoredOffsetbox` + docstring. + """ + self._loc = _map_loc_to_compass(loc) + self.stale = True + + def get_loc(self): + """ + Get the box location. This will be one of 'NE', 'NW', + 'SW', 'SE', 'E', 'W', 'E', 'S', 'N', 'C'. + """ + return self._loc + + loc = property(get_loc, set_loc) + def set_child(self, child): "set the child to be anchored" self._child = child @@ -1179,25 +1182,9 @@ def _get_anchored_bbox(self, loc, bbox, parentbbox, borderpad): return the position of the bbox anchored at the parentbbox with the loc code, with the borderpad. """ - assert loc in range(1, 11) # called only internally - - BEST, UR, UL, LL, LR, R, CL, CR, LC, UC, C = range(11) - - anchor_coefs = {UR: "NE", - UL: "NW", - LL: "SW", - LR: "SE", - R: "E", - CL: "W", - CR: "E", - LC: "S", - UC: "N", - C: "C"} - - c = anchor_coefs[loc] - + assert loc in _COMPASS_LOCS # called only internally container = parentbbox.padded(-borderpad) - anchored_box = bbox.anchored(c, container=container) + anchored_box = bbox.anchored(loc, container=container) return anchored_box.x0, anchored_box.y0 diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index dd1f08120038..a4036baa8880 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -598,19 +598,10 @@ def validate_markevery(s): validate_markeverylist = _listify_validator(validate_markevery) -validate_legend_loc = ValidateInStrings( - 'legend_loc', - ['best', - 'upper right', - 'upper left', - 'lower left', - 'lower right', - 'right', - 'center left', - 'center right', - 'lower center', - 'upper center', - 'center'], ignorecase=True) + +def validate_legend_loc(loc): + return cbook._map_loc_to_compass(loc, allowbest=True, warnonly=True, + asrcparam='legend.loc') def validate_svg_fonttype(s): diff --git a/lib/matplotlib/tests/test_legend.py b/lib/matplotlib/tests/test_legend.py index b4bcecde81fa..08b0918abb23 100644 --- a/lib/matplotlib/tests/test_legend.py +++ b/lib/matplotlib/tests/test_legend.py @@ -548,6 +548,43 @@ def test_alpha_handles(): assert lh.get_edgecolor()[:-1] == hh[1].get_edgecolor()[:-1] +_loc_values = [['NW', 'N', 'NE', 'W', 'C', 'E', 'SW', 'S', 'SE'], + ['northwest', 'north', 'northeast', 'west', 'center', + 'east', 'southwest', 'south', 'southeast'], + ['upper left', 'upper center', 'upper right', 'center left', + 'center', 'right', 'lower left', 'lower center', + 'lower right']] + + +@pytest.mark.parametrize('locs', _loc_values) +def test_legend_loc_compass_codes(locs): + codes = [2, 9, 1, 6, 10, 7, 3, 8, 4] + valid = ['NW', 'N', 'NE', 'W', 'C', 'E', 'SW', 'S', 'SE'] + + for loc, val in zip(locs, valid): + plt.rcParams["legend.loc"] = loc + assert plt.rcParams["legend.loc"] == val + + fig1, axes1 = plt.subplots(3, 3) + fig2, axes2 = plt.subplots(3, 3) + + for ax, loc in zip(axes1.flat, locs): + ax.plot([1, 2], label=loc) + ax.legend(loc=loc) + + for ax, loc in zip(axes2.flat, codes): + ax.plot([1, 2], label=loc) + leg = ax.legend(loc="NW") + leg._set_loc(loc) + + for ax1, ax2 in zip(axes1.flat, axes2.flat): + leg1 = ax1.get_legend() + leg2 = ax2.get_legend() + assert leg1._get_loc() == leg2._get_loc() + fig1.canvas.draw() + fig2.canvas.draw() + + def test_warn_big_data_best_loc(): fig, ax = plt.subplots() ax.plot(np.arange(200001), label='Is this big data?') diff --git a/lib/matplotlib/tests/test_offsetbox.py b/lib/matplotlib/tests/test_offsetbox.py index c9aff6ec616d..904c76163202 100644 --- a/lib/matplotlib/tests/test_offsetbox.py +++ b/lib/matplotlib/tests/test_offsetbox.py @@ -79,25 +79,39 @@ def test_offsetbox_clip_children(): assert fig.stale -def test_offsetbox_loc_codes(): - # Check that valid string location codes all work with an AnchoredOffsetbox - codes = {'upper right': 1, - 'upper left': 2, - 'lower left': 3, - 'lower right': 4, - 'right': 5, - 'center left': 6, - 'center right': 7, - 'lower center': 8, - 'upper center': 9, - 'center': 10, - } - fig, ax = plt.subplots() - da = DrawingArea(100, 100) - for code in codes: - anchored_box = AnchoredOffsetbox(loc=code, child=da) +_loc_values = [['NW', 'N', 'NE', 'W', 'C', 'E', 'SW', 'S', 'SE'], + ['northwest', 'north', 'northeast', 'west', 'center', + 'east', 'southwest', 'south', 'southeast'], + ['upper left', 'upper center', 'upper right', 'center left', + 'center', 'right', 'lower left', 'lower center', + 'lower right']] + + +@pytest.mark.parametrize('locs', _loc_values) +def test_offsetbox_loc_codes(locs): + + codes = [2, 9, 1, 6, 10, 7, 3, 8, 4] + + fig1, axes1 = plt.subplots(3, 3) + fig2, axes2 = plt.subplots(3, 3) + + for ax, loc in zip(axes1.flat, locs): + da = DrawingArea(50, 50) + anchored_box = AnchoredOffsetbox(loc=loc, child=da) ax.add_artist(anchored_box) - fig.canvas.draw() + + for ax, loc in zip(axes2.flat, codes): + da = DrawingArea(50, 50) + anchored_box = AnchoredOffsetbox(loc="NW", child=da) + ax.add_artist(anchored_box) + anchored_box.set_loc(loc) + + for ax1, ax2 in zip(axes1.flat, axes2.flat): + ab1, = ax1.findobj(match=AnchoredOffsetbox) + ab2, = ax2.findobj(match=AnchoredOffsetbox) + assert ab1.get_loc() == ab2.get_loc() + fig1.canvas.draw() + fig2.canvas.draw() def test_expand_with_tight_layout(): diff --git a/lib/mpl_toolkits/axes_grid1/anchored_artists.py b/lib/mpl_toolkits/axes_grid1/anchored_artists.py index fef40db2dcb5..83878c117df1 100644 --- a/lib/mpl_toolkits/axes_grid1/anchored_artists.py +++ b/lib/mpl_toolkits/axes_grid1/anchored_artists.py @@ -28,19 +28,23 @@ def __init__(self, width, height, xdescent, ydescent, xdescent, ydescent : int or float descent of the container in the x- and y- direction, in pixels. - loc : int + loc : str or int Location of this artist. Valid location codes are:: - 'upper right' : 1, - 'upper left' : 2, - 'lower left' : 3, - 'lower right' : 4, - 'right' : 5, - 'center left' : 6, - 'center right' : 7, - 'lower center' : 8, - 'upper center' : 9, - 'center' : 10 + ============ ============== =============== ============= + Compass Code Compass String Location String Location Code + ============ ============== =============== ============= + 'NE' 'northeast' 'upper right' 1 + 'NW' 'northwest' 'upper left' 2 + 'SW' 'southwest' 'lower left' 3 + 'SE' 'southeast' 'lower right' 4 + 'right' 5 + 'W' 'west' 'center left' 6 + 'E' 'east' 'center right' 7 + 'S' 'south' 'lower center' 8 + 'N' 'north' 'upper center' 9 + 'C' 'center' 'center' 10 + ============ ============== =============== ============= pad : int or float, optional Padding around the child objects, in fraction of the font @@ -101,19 +105,23 @@ def __init__(self, transform, loc, The transformation object for the coordinate system in use, i.e., :attr:`matplotlib.axes.Axes.transData`. - loc : int + loc : str or int Location of this artist. Valid location codes are:: - 'upper right' : 1, - 'upper left' : 2, - 'lower left' : 3, - 'lower right' : 4, - 'right' : 5, - 'center left' : 6, - 'center right' : 7, - 'lower center' : 8, - 'upper center' : 9, - 'center' : 10 + ============ ============== =============== ============= + Compass Code Compass String Location String Location Code + ============ ============== =============== ============= + 'NE' 'northeast' 'upper right' 1 + 'NW' 'northwest' 'upper left' 2 + 'SW' 'southwest' 'lower left' 3 + 'SE' 'southeast' 'lower right' 4 + 'right' 5 + 'W' 'west' 'center left' 6 + 'E' 'east' 'center right' 7 + 'S' 'south' 'lower center' 8 + 'N' 'north' 'upper center' 9 + 'C' 'center' 'center' 10 + ============ ============== =============== ============= pad : int or float, optional Padding around the child objects, in fraction of the font @@ -176,19 +184,23 @@ def __init__(self, transform, width, height, angle, loc, angle : int or float Rotation of the ellipse, in degrees, anti-clockwise. - loc : int - Location of this size bar. Valid location codes are:: + loc : str or int + Location of this artist. Valid location codes are:: - 'upper right' : 1, - 'upper left' : 2, - 'lower left' : 3, - 'lower right' : 4, - 'right' : 5, - 'center left' : 6, - 'center right' : 7, - 'lower center' : 8, - 'upper center' : 9, - 'center' : 10 + ============ ============== =============== ============= + Compass Code Compass String Location String Location Code + ============ ============== =============== ============= + 'NE' 'northeast' 'upper right' 1 + 'NW' 'northwest' 'upper left' 2 + 'SW' 'southwest' 'lower left' 3 + 'SE' 'southeast' 'lower right' 4 + 'right' 5 + 'W' 'west' 'center left' 6 + 'E' 'east' 'center right' 7 + 'S' 'south' 'lower center' 8 + 'N' 'north' 'upper center' 9 + 'C' 'center' 'center' 10 + ============ ============== =============== ============= pad : int or float, optional Padding around the ellipse, in fraction of the font size. Defaults @@ -244,19 +256,23 @@ def __init__(self, transform, size, label, loc, label : str Label to display. - loc : int - Location of this size bar. Valid location codes are:: + loc : str or int + Location of this artist. Valid location codes are:: - 'upper right' : 1, - 'upper left' : 2, - 'lower left' : 3, - 'lower right' : 4, - 'right' : 5, - 'center left' : 6, - 'center right' : 7, - 'lower center' : 8, - 'upper center' : 9, - 'center' : 10 + ============ ============== =============== ============= + Compass Code Compass String Location String Location Code + ============ ============== =============== ============= + 'NE' 'northeast' 'upper right' 1 + 'NW' 'northwest' 'upper left' 2 + 'SW' 'southwest' 'lower left' 3 + 'SE' 'southeast' 'lower right' 4 + 'right' 5 + 'W' 'west' 'center left' 6 + 'E' 'east' 'center right' 7 + 'S' 'south' 'lower center' 8 + 'N' 'north' 'upper center' 9 + 'C' 'center' 'center' 10 + ============ ============== =============== ============= pad : int or float, optional Padding around the label and size bar, in fraction of the font @@ -399,19 +415,23 @@ def __init__(self, transform, label_x, label_y, length=0.15, Size of label strings, given in coordinates of *transform*. Defaults to 0.08. - loc : int, optional - Location of the direction arrows. Valid location codes are:: - - 'upper right' : 1, - 'upper left' : 2, - 'lower left' : 3, - 'lower right' : 4, - 'right' : 5, - 'center left' : 6, - 'center right' : 7, - 'lower center' : 8, - 'upper center' : 9, - 'center' : 10 + loc : str or int + Location of this artist. Valid location codes are:: + + ============ ============== =============== ============= + Compass Code Compass String Location String Location Code + ============ ============== =============== ============= + 'NE' 'northeast' 'upper right' 1 + 'NW' 'northwest' 'upper left' 2 + 'SW' 'southwest' 'lower left' 3 + 'SE' 'southeast' 'lower right' 4 + 'right' 5 + 'W' 'west' 'center left' 6 + 'E' 'east' 'center right' 7 + 'S' 'south' 'lower center' 8 + 'N' 'north' 'upper center' 9 + 'C' 'center' 'center' 10 + ============ ============== =============== ============= Defaults to 2. diff --git a/lib/mpl_toolkits/axes_grid1/inset_locator.py b/lib/mpl_toolkits/axes_grid1/inset_locator.py index 9fc40b7b26a5..66a70fb99a61 100644 --- a/lib/mpl_toolkits/axes_grid1/inset_locator.py +++ b/lib/mpl_toolkits/axes_grid1/inset_locator.py @@ -387,7 +387,7 @@ def inset_axes(parent_axes, width, height, loc='upper right', Both sizes used can be specified either in inches or percentage. For example,:: - inset_axes(parent_axes, width='40%%', height='30%%', loc=3) + inset_axes(parent_axes, width='40%%', height='30%%', loc='lower left') creates in inset axes in the lower left corner of *parent_axes* which spans over 30%% in height and 40%% in width of the *parent_axes*. Since the usage @@ -427,19 +427,24 @@ def inset_axes(parent_axes, width, height, loc='upper right', are relative to the parent_axes. Otherwise they are to be understood relative to the bounding box provided via *bbox_to_anchor*. - loc : int or string, optional, default to 1 + loc : int or string, optional, defaults to 'upper right' Location to place the inset axes. The valid locations are:: - 'upper right' : 1, - 'upper left' : 2, - 'lower left' : 3, - 'lower right' : 4, - 'right' : 5, - 'center left' : 6, - 'center right' : 7, - 'lower center' : 8, - 'upper center' : 9, - 'center' : 10 + ============ ============== =============== ============= + Compass Code Compass String Location String Location Code + ============ ============== =============== ============= + 'NE' 'northeast' 'upper right' 1 + 'NW' 'northwest' 'upper left' 2 + 'SW' 'southwest' 'lower left' 3 + 'SE' 'southeast' 'lower right' 4 + 'right' 5 + 'W' 'west' 'center left' 6 + 'E' 'east' 'center right' 7 + 'S' 'south' 'lower center' 8 + 'N' 'north' 'upper center' 9 + 'C' 'center' 'center' 10 + ============ ============== =============== ============= + bbox_to_anchor : tuple or `matplotlib.transforms.BboxBase`, optional Bbox that the inset axes will be anchored to. If None, @@ -545,19 +550,24 @@ def zoomed_inset_axes(parent_axes, zoom, loc='upper right', coordinates (i.e., "zoomed in"), while *zoom* < 1 will shrink the coordinates (i.e., "zoomed out"). - loc : int or string, optional, default to 1 + loc : int or string, optional, default to 'upper right' Location to place the inset axes. The valid locations are:: - 'upper right' : 1, - 'upper left' : 2, - 'lower left' : 3, - 'lower right' : 4, - 'right' : 5, - 'center left' : 6, - 'center right' : 7, - 'lower center' : 8, - 'upper center' : 9, - 'center' : 10 + ============ ============== =============== ============= + Compass Code Compass String Location String Location Code + ============ ============== =============== ============= + 'NE' 'northeast' 'upper right' 1 + 'NW' 'northwest' 'upper left' 2 + 'SW' 'southwest' 'lower left' 3 + 'SE' 'southeast' 'lower right' 4 + 'right' 5 + 'W' 'west' 'center left' 6 + 'E' 'east' 'center right' 7 + 'S' 'south' 'lower center' 8 + 'N' 'north' 'upper center' 9 + 'C' 'center' 'center' 10 + ============ ============== =============== ============= + bbox_to_anchor : tuple or `matplotlib.transforms.BboxBase`, optional Bbox that the inset axes will be anchored to. If None, 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