Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork7.9k
Merge SubplotBase into AxesBase.#23573
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Uh oh!
There was an error while loading.Please reload this page.
Changes fromall commits
File filter
Filter by extension
Conversations
Uh oh!
There was an error while loading.Please reload this page.
Jump to
Uh oh!
There was an error while loading.Please reload this page.
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
All Axes have ``get_subplotspec`` and ``get_gridspec`` methods now, which returns None for Axes not positioned via a gridspec | ||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
Previously, this method was only present for Axes positioned via a gridspec. | ||
Following this change, checking ``hasattr(ax, "get_gridspec")`` should now be | ||
replaced by ``ax.get_gridspec() is not None``. For compatibility with older | ||
Matplotlib releases, one can also check | ||
``hasattr(ax, "get_gridspec") and ax.get_gridspec() is not None``. |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -38,8 +38,8 @@ Subplot-related attributes and methods | ||
Some ``SubplotBase`` methods and attributes have been deprecated and/or moved | ||
to `.SubplotSpec`: | ||
- ``get_geometry`` (use ``SubplotBase.get_subplotspec`` instead), | ||
- ``change_geometry`` (use ``SubplotBase.set_subplotspec`` instead), | ||
Comment on lines +41 to +42 Member There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. The removal message in 3.6 API changes was not updated like here, which is breaking docs. But should these alternatives really be made code-style instead of linking to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Fair question. I consider the changelog a historic document that reflects the state at that time. We also don't go back and change other things in changelog, that are not correct anymore. So, code-style is correct. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. OK,#24236 | ||
- ``is_first_row``, ``is_last_row``, ``is_first_col``, ``is_last_col`` (use the | ||
corresponding methods on the `.SubplotSpec` instance instead), | ||
- ``update_params`` (now a no-op), | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,18 @@ | ||
from . import_base | ||
from ._axes import * | ||
# Backcompat. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. IMHO we should get rid of this stuff eventually. As a first step, they should becomepending deprecated and we should communicate that people should move away from subplot stuff in the change notes. Do we want to deprecate this stuff? | ||
from ._axes import Axes as Subplot | ||
class _SubplotBaseMeta(type): | ||
def __instancecheck__(self, obj): | ||
return (isinstance(obj, _base._AxesBase) | ||
and obj.get_subplotspec() is not None) | ||
class SubplotBase(metaclass=_SubplotBaseMeta): | ||
pass | ||
def subplot_class_factory(cls): return cls |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
from collections.abc importIterable,MutableSequence | ||
from contextlib import ExitStack | ||
import functools | ||
import inspect | ||
@@ -18,6 +18,7 @@ | ||
import matplotlib.collections as mcoll | ||
import matplotlib.colors as mcolors | ||
import matplotlib.font_manager as font_manager | ||
from matplotlib.gridspec import SubplotSpec | ||
import matplotlib.image as mimage | ||
import matplotlib.lines as mlines | ||
import matplotlib.patches as mpatches | ||
@@ -569,8 +570,8 @@ def __str__(self): | ||
return "{0}({1[0]:g},{1[1]:g};{1[2]:g}x{1[3]:g})".format( | ||
type(self).__name__, self._position.bounds) | ||
def __init__(self, fig, | ||
*args, | ||
facecolor=None, # defaults to rc axes.facecolor | ||
frameon=True, | ||
sharex=None, # use Axes instance's xaxis info | ||
@@ -589,9 +590,18 @@ def __init__(self, fig, rect, | ||
fig : `~matplotlib.figure.Figure` | ||
The Axes is built in the `.Figure` *fig*. | ||
*args | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Is this needed to unite the Axes and Subplot constuctor APIs? That's quite ugly. We should consider to migrate three numbers to "one tuple of three numbers" so that we have always exactly one argument But that may be for later. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. Yes, this is super ugly and I do want to move to "one tuple of 3 numbers", but that cannot be done with yet another deprecation cycle which can be done later. | ||
``*args`` can be a single ``(left, bottom, width, height)`` | ||
rectangle or a single `.Bbox`. This specifies the rectangle (in | ||
figure coordinates) where the Axes is positioned. | ||
``*args`` can also consist of three numbers or a single three-digit | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others.Learn more. We should move away from three separate number parameters towards one tuple, so that we can have a single parameter again. I think this should not be too annoying downstream because few people instantiate Axes directly. This would need a pending deprecation. On the downside, we'd have for the one parameter a 4-tuple meaning a rect and a 3-tuple meaning a subplot spec, but that seems bearable. | ||
number; in the latter case, the digits are considered as | ||
independent numbers. The numbers are interpreted as ``(nrows, | ||
ncols, index)``: ``(nrows, ncols)`` specifies the size of an array | ||
of subplots, and ``index`` is the 1-based index of the subplot | ||
being created. Finally, ``*args`` can also directly be a | ||
`.SubplotSpec` instance. | ||
sharex, sharey : `~.axes.Axes`, optional | ||
The x or y `~.matplotlib.axis` is shared with the x or | ||
@@ -616,10 +626,21 @@ def __init__(self, fig, rect, | ||
""" | ||
super().__init__() | ||
if "rect" in kwargs: | ||
if args: | ||
raise TypeError( | ||
"'rect' cannot be used together with positional arguments") | ||
rect = kwargs.pop("rect") | ||
_api.check_isinstance((mtransforms.Bbox, Iterable), rect=rect) | ||
args = (rect,) | ||
subplotspec = None | ||
if len(args) == 1 and isinstance(args[0], mtransforms.Bbox): | ||
self._position = args[0] | ||
elif len(args) == 1 and np.iterable(args[0]): | ||
self._position = mtransforms.Bbox.from_bounds(*args[0]) | ||
else: | ||
self._position = self._originalPosition = mtransforms.Bbox.unit() | ||
subplotspec = SubplotSpec._from_subplot_args(fig, args) | ||
if self._position.width < 0 or self._position.height < 0: | ||
raise ValueError('Width and height specified must be non-negative') | ||
self._originalPosition = self._position.frozen() | ||
@@ -632,8 +653,16 @@ def __init__(self, fig, rect, | ||
self._sharey = sharey | ||
self.set_label(label) | ||
self.set_figure(fig) | ||
# The subplotspec needs to be set after the figure (so that | ||
# figure-level subplotpars are taken into account), but the figure | ||
# needs to be set after self._position is initialized. | ||
if subplotspec: | ||
self.set_subplotspec(subplotspec) | ||
else: | ||
self._subplotspec = None | ||
self.set_box_aspect(box_aspect) | ||
self._axes_locator = None # Optionally set via update(kwargs). | ||
# placeholder for any colorbars added that use this Axes. | ||
# (see colorbar.py): | ||
self._colorbars = [] | ||
@@ -737,6 +766,19 @@ def __repr__(self): | ||
fields += [f"{name}label={axis.get_label().get_text()!r}"] | ||
return f"<{self.__class__.__name__}: " + ", ".join(fields) + ">" | ||
def get_subplotspec(self): | ||
"""Return the `.SubplotSpec` associated with the subplot, or None.""" | ||
return self._subplotspec | ||
def set_subplotspec(self, subplotspec): | ||
"""Set the `.SubplotSpec`. associated with the subplot.""" | ||
self._subplotspec = subplotspec | ||
self._set_position(subplotspec.get_position(self.figure)) | ||
def get_gridspec(self): | ||
"""Return the `.GridSpec` associated with the subplot, or None.""" | ||
return self._subplotspec.get_gridspec() if self._subplotspec else None | ||
@_api.delete_parameter("3.6", "args") | ||
@_api.delete_parameter("3.6", "kwargs") | ||
def get_window_extent(self, renderer=None, *args, **kwargs): | ||
@@ -4424,17 +4466,23 @@ def get_tightbbox(self, renderer=None, call_axes_locator=True, | ||
def _make_twin_axes(self, *args, **kwargs): | ||
"""Make a twinx Axes of self. This is used for twinx and twiny.""" | ||
if 'sharex' in kwargs and 'sharey' in kwargs: | ||
# The following line is added in v2.2 to avoid breaking Seaborn, | ||
# which currently uses this internal API. | ||
if kwargs["sharex"] is not self and kwargs["sharey"] is not self: | ||
raise ValueError("Twinned Axes may share only one axis") | ||
ss = self.get_subplotspec() | ||
if ss: | ||
twin = self.figure.add_subplot(ss, *args, **kwargs) | ||
else: | ||
twin = self.figure.add_axes( | ||
self.get_position(True), *args, **kwargs, | ||
axes_locator=_TransformedBoundsLocator( | ||
[0, 0, 1, 1], self.transAxes)) | ||
self.set_adjustable('datalim') | ||
twin.set_adjustable('datalim') | ||
self._twinned_axes.join(self,twin) | ||
returntwin | ||
def twinx(self): | ||
""" | ||
@@ -4502,3 +4550,56 @@ def get_shared_x_axes(self): | ||
def get_shared_y_axes(self): | ||
"""Return an immutable view on the shared y-axes Grouper.""" | ||
return cbook.GrouperView(self._shared_axes["y"]) | ||
def label_outer(self): | ||
""" | ||
Only show "outer" labels and tick labels. | ||
x-labels are only kept for subplots on the last row (or first row, if | ||
labels are on the top side); y-labels only for subplots on the first | ||
column (or last column, if labels are on the right side). | ||
""" | ||
self._label_outer_xaxis(check_patch=False) | ||
self._label_outer_yaxis(check_patch=False) | ||
def _label_outer_xaxis(self, *, check_patch): | ||
# see documentation in label_outer. | ||
if check_patch and not isinstance(self.patch, mpl.patches.Rectangle): | ||
return | ||
ss = self.get_subplotspec() | ||
if not ss: | ||
return | ||
label_position = self.xaxis.get_label_position() | ||
if not ss.is_first_row(): # Remove top label/ticklabels/offsettext. | ||
if label_position == "top": | ||
self.set_xlabel("") | ||
self.xaxis.set_tick_params(which="both", labeltop=False) | ||
if self.xaxis.offsetText.get_position()[1] == 1: | ||
self.xaxis.offsetText.set_visible(False) | ||
if not ss.is_last_row(): # Remove bottom label/ticklabels/offsettext. | ||
if label_position == "bottom": | ||
self.set_xlabel("") | ||
self.xaxis.set_tick_params(which="both", labelbottom=False) | ||
if self.xaxis.offsetText.get_position()[1] == 0: | ||
self.xaxis.offsetText.set_visible(False) | ||
def _label_outer_yaxis(self, *, check_patch): | ||
# see documentation in label_outer. | ||
if check_patch and not isinstance(self.patch, mpl.patches.Rectangle): | ||
return | ||
ss = self.get_subplotspec() | ||
if not ss: | ||
return | ||
label_position = self.yaxis.get_label_position() | ||
if not ss.is_first_col(): # Remove left label/ticklabels/offsettext. | ||
if label_position == "left": | ||
self.set_ylabel("") | ||
self.yaxis.set_tick_params(which="both", labelleft=False) | ||
if self.yaxis.offsetText.get_position()[0] == 0: | ||
self.yaxis.offsetText.set_visible(False) | ||
if not ss.is_last_col(): # Remove right label/ticklabels/offsettext. | ||
if label_position == "right": | ||
self.set_ylabel("") | ||
self.yaxis.set_tick_params(which="both", labelright=False) | ||
if self.yaxis.offsetText.get_position()[0] == 1: | ||
self.yaxis.offsetText.set_visible(False) |
Uh oh!
There was an error while loading.Please reload this page.