Skip to content

Prepare for merging SubplotBase into AxesBase. #18564

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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Sep 25, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions doc/api/next_api_changes/behavior/18564-AL.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
Axes3D no longer adds itself to figure
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

New `.Axes3D` objects previously added themselves to figures when they were
created, which lead to them being added twice if
``fig.add_subplot(111, projection='3d')`` was called. Now ``ax = Axes3d(fig)``
will need to be explicitly added to the figure with ``fig.add_axes(ax)``, as
also needs to be done for normal `.axes.Axes`.
16 changes: 16 additions & 0 deletions doc/api/next_api_changes/deprecations/18564-AL.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Subplot-related attributes and methods
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Some ``SubplotBase`` attributes have been deprecated and/or moved to
`.SubplotSpec`: ``get_geometry`` (use `.SubplotBase.get_subplotspec`
instead), ``change_geometry`` (use `.SubplotBase.set_subplotspec` instead),
``is_first_row``, ``is_last_row``, ``is_first_col``, ``is_last_col`` (use the
corresponding methods on the `.SubplotSpec` instance instead), ``figbox`` (use
``ax.get_subplotspec().get_geometry(ax.figure)`` instead to recompute the
geometry, or ``ax.get_position()`` to read its current value), ``numRows``,
``numCols`` (use the ``nrows`` and ``ncols`` attribute on the `.GridSpec`
instead).

Axes constructor
~~~~~~~~~~~~~~~~
Parameters of the Axes constructor other than *fig* and *rect* will become
keyword-only in a future version.
9 changes: 5 additions & 4 deletions examples/userdemo/demo_gridspec06.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,10 @@ def squiggle_xy(a, b, c, d):

# show only the outside spines
for ax in fig.get_axes():
ax.spines['top'].set_visible(ax.is_first_row())
ax.spines['bottom'].set_visible(ax.is_last_row())
ax.spines['left'].set_visible(ax.is_first_col())
ax.spines['right'].set_visible(ax.is_last_col())
ss = ax.get_subplotspec()
ax.spines['top'].set_visible(ss.is_first_row())
ax.spines['bottom'].set_visible(ss.is_last_row())
ax.spines['left'].set_visible(ss.is_first_col())
ax.spines['right'].set_visible(ss.is_last_col())

plt.show()
1 change: 1 addition & 0 deletions lib/matplotlib/axes/_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,7 @@ 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)

@cbook._make_keyword_only("3.4", "facecolor")
def __init__(self, fig, rect,
facecolor=None, # defaults to rc axes.facecolor
frameon=True,
Expand Down
45 changes: 34 additions & 11 deletions lib/matplotlib/axes/_subplots.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,10 @@ def __init__(self, fig, *args, **kwargs):
**kwargs
Keyword arguments are passed to the Axes (sub)class constructor.
"""

self.figure = fig
self._subplotspec = SubplotSpec._from_subplot_args(fig, args)
self.update_params()
# _axes_class is set in the subplot_class_factory
self._axes_class.__init__(self, fig, self.figbox, **kwargs)
self._axes_class.__init__(self, fig, [0, 0, 1, 1], **kwargs)
# This will also update the axes position.
self.set_subplotspec(SubplotSpec._from_subplot_args(fig, args))

def __reduce__(self):
# get the first axes class which does not inherit from a subplotbase
Expand All @@ -49,12 +47,15 @@ def __reduce__(self):
(axes_class,),
self.__getstate__())

@cbook.deprecated(
"3.4", alternative="get_subplotspec",
addendum="(get_subplotspec returns a SubplotSpec instance.)")
def get_geometry(self):
"""Get the subplot geometry, e.g., (2, 2, 3)."""
rows, cols, num1, num2 = self.get_subplotspec().get_geometry()
return rows, cols, num1 + 1 # for compatibility

# COVERAGE NOTE: Never used internally or from examples
@cbook.deprecated("3.4", alternative="set_subplotspec")
def change_geometry(self, numrows, numcols, num):
"""Change subplot geometry, e.g., from (1, 1, 1) to (2, 2, 3)."""
self._subplotspec = GridSpec(numrows, numcols,
Expand All @@ -69,16 +70,33 @@ def get_subplotspec(self):
def set_subplotspec(self, subplotspec):
"""Set the `.SubplotSpec`. instance associated with the subplot."""
self._subplotspec = subplotspec
self._set_position(subplotspec.get_position(self.figure))

def get_gridspec(self):
"""Return the `.GridSpec` instance associated with the subplot."""
return self._subplotspec.get_gridspec()

@cbook.deprecated(
"3.4", alternative="get_subplotspec().get_position(self.figure)")
@property
def figbox(self):
return self.get_subplotspec().get_position(self.figure)

@cbook.deprecated("3.4", alternative="get_gridspec().nrows")
@property
def numRows(self):
return self.get_gridspec().nrows

@cbook.deprecated("3.4", alternative="get_gridspec().ncols")
@property
def numCols(self):
return self.get_gridspec().ncols

@cbook.deprecated("3.4")
def update_params(self):
"""Update the subplot position from ``self.figure.subplotpars``."""
self.figbox, _, _, self.numRows, self.numCols = \
self.get_subplotspec().get_position(self.figure,
return_all=True)
# Now a no-op, as figbox/numRows/numCols are (deprecated) auto-updating
# properties.

@cbook.deprecated("3.2", alternative="ax.get_subplotspec().rowspan.start")
@property
Expand All @@ -90,15 +108,19 @@ def rowNum(self):
def colNum(self):
return self.get_subplotspec().colspan.start

@cbook.deprecated("3.4", alternative="ax.get_subplotspec().is_first_row()")
def is_first_row(self):
return self.get_subplotspec().rowspan.start == 0

@cbook.deprecated("3.4", alternative="ax.get_subplotspec().is_last_row()")
def is_last_row(self):
return self.get_subplotspec().rowspan.stop == self.get_gridspec().nrows

@cbook.deprecated("3.4", alternative="ax.get_subplotspec().is_first_col()")
def is_first_col(self):
return self.get_subplotspec().colspan.start == 0

@cbook.deprecated("3.4", alternative="ax.get_subplotspec().is_last_col()")
def is_last_col(self):
return self.get_subplotspec().colspan.stop == self.get_gridspec().ncols

Expand All @@ -109,8 +131,9 @@ def label_outer(self):
x-labels are only kept for subplots on the last row; y-labels only for
subplots on the first column.
"""
lastrow = self.is_last_row()
firstcol = self.is_first_col()
ss = self.get_subplotspec()
lastrow = ss.is_last_row()
firstcol = ss.is_first_col()
if not lastrow:
for label in self.get_xticklabels(which="both"):
label.set_visible(False)
Expand Down
2 changes: 0 additions & 2 deletions lib/matplotlib/colorbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -1540,8 +1540,6 @@ def make_axes_gridspec(parent, *, location=None, orientation=None,
aspect = 1 / aspect

parent.set_subplotspec(ss_main)
parent.update_params()
parent._set_position(parent.figbox)
parent.set_anchor(loc_settings["panchor"])

fig = parent.get_figure()
Expand Down
7 changes: 3 additions & 4 deletions lib/matplotlib/figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -607,15 +607,15 @@ def autofmt_xdate(
"3.3", message="Support for passing which=None to mean "
"which='major' is deprecated since %(since)s and will be "
"removed %(removal)s.")
allsubplots = all(hasattr(ax, 'is_last_row') for ax in self.axes)
allsubplots = all(hasattr(ax, 'get_subplotspec') for ax in self.axes)
if len(self.axes) == 1:
for label in self.axes[0].get_xticklabels(which=which):
label.set_ha(ha)
label.set_rotation(rotation)
else:
if allsubplots:
for ax in self.get_axes():
if ax.is_last_row():
if ax.get_subplotspec().is_last_row():
for label in ax.get_xticklabels(which=which):
label.set_ha(ha)
label.set_rotation(rotation)
Expand Down Expand Up @@ -2420,8 +2420,7 @@ def subplots_adjust(self, left=None, bottom=None, right=None, top=None,
self.subplotpars.update(left, bottom, right, top, wspace, hspace)
for ax in self.axes:
if isinstance(ax, SubplotBase):
ax.update_params()
ax.set_position(ax.figbox)
ax._set_position(ax.get_subplotspec().get_position(self))
self.stale = True

def ginput(self, n=1, timeout=30, show_clicks=True,
Expand Down
17 changes: 15 additions & 2 deletions lib/matplotlib/gridspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -489,8 +489,8 @@ def update(self, **kwargs):
if isinstance(ax, mpl.axes.SubplotBase):
ss = ax.get_subplotspec().get_topmost_subplotspec()
if ss.get_gridspec() == self:
ax.update_params()
ax._set_position(ax.figbox)
ax._set_position(
ax.get_subplotspec().get_position(ax.figure))

def get_subplot_params(self, figure=None):
"""
Expand Down Expand Up @@ -764,6 +764,19 @@ def colspan(self):
c1, c2 = sorted([self.num1 % ncols, self.num2 % ncols])
return range(c1, c2 + 1)

def is_first_row(self):
return self.rowspan.start == 0

def is_last_row(self):
return self.rowspan.stop == self.get_gridspec().nrows

def is_first_col(self):
return self.colspan.start == 0

def is_last_col(self):
return self.colspan.stop == self.get_gridspec().ncols

@cbook._delete_parameter("3.4", "return_all")
def get_position(self, figure, return_all=False):
"""
Update the subplot position from ``figure.subplotpars``.
Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/tests/test_collections.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ def test_polycollection_close():
[[3., 0.], [3., 1.], [4., 1.], [4., 0.]]]

fig = plt.figure()
ax = Axes3D(fig)
ax = fig.add_axes(Axes3D(fig))

colors = ['r', 'g', 'b', 'y', 'k']
zpos = list(range(5))
Expand Down
6 changes: 3 additions & 3 deletions lib/matplotlib/tests/test_colorbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,13 +224,13 @@ def test_remove_from_figure(use_gridspec):
fig, ax = plt.subplots()
sc = ax.scatter([1, 2], [3, 4], cmap="spring")
sc.set_array(np.array([5, 6]))
pre_figbox = np.array(ax.figbox)
pre_position = ax.get_position()
cb = fig.colorbar(sc, use_gridspec=use_gridspec)
fig.subplots_adjust()
cb.remove()
fig.subplots_adjust()
post_figbox = np.array(ax.figbox)
assert (pre_figbox == post_figbox).all()
post_position = ax.get_position()
assert (pre_position.get_points() == post_position.get_points()).all()


def test_colorbarbase():
Expand Down
2 changes: 1 addition & 1 deletion lib/matplotlib/tests/test_figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ def test_gca():
# Changing the projection will throw a warning
assert fig.gca(polar=True) is not ax3
assert fig.gca(polar=True) is not ax2
assert fig.gca().get_geometry() == (1, 1, 1)
assert fig.gca().get_subplotspec().get_geometry() == (1, 1, 0, 0)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should you check that the subplot spec is 1, 1, 1?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's an equivalent check?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooops, I see. You are indeed correct...


fig.sca(ax1)
assert fig.gca(projection='rectilinear') is ax1
Expand Down
2 changes: 0 additions & 2 deletions lib/mpl_toolkits/mplot3d/axes3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,6 @@ def __init__(
pseudo_bbox = self.transLimits.inverted().transform([(0, 0), (1, 1)])
self._pseudo_w, self._pseudo_h = pseudo_bbox[1] - pseudo_bbox[0]

self.figure.add_axes(self)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this going to break basically every usage of 3D axes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, only those using the rather unidiomatic Axes3D(fig) instead of fig.add_subplot(..., projection="3d").


# mplot3d currently manages its own spines and needs these turned off
# for bounding box calculations
for k in self.spines.keys():
Expand Down
2 changes: 1 addition & 1 deletion lib/mpl_toolkits/tests/test_mplot3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -700,7 +700,7 @@ def test_add_collection3d_zs_scalar():
@mpl3d_image_comparison(['axes3d_labelpad.png'], remove_text=False)
def test_axes3d_labelpad():
fig = plt.figure()
ax = Axes3D(fig)
ax = fig.add_axes(Axes3D(fig))
# labelpad respects rcParams
assert ax.xaxis.labelpad == mpl.rcParams['axes.labelpad']
# labelpad can be set in set_label
Expand Down
9 changes: 5 additions & 4 deletions tutorials/intermediate/gridspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,10 +246,11 @@ def squiggle_xy(a, b, c, d, i=np.arange(0.0, 2*np.pi, 0.05)):

# show only the outside spines
for ax in fig11.get_axes():
ax.spines['top'].set_visible(ax.is_first_row())
ax.spines['bottom'].set_visible(ax.is_last_row())
ax.spines['left'].set_visible(ax.is_first_col())
ax.spines['right'].set_visible(ax.is_last_col())
ss = ax.get_subplotspec()
ax.spines['top'].set_visible(ss.is_first_row())
ax.spines['bottom'].set_visible(ss.is_last_row())
ax.spines['left'].set_visible(ss.is_first_col())
ax.spines['right'].set_visible(ss.is_last_col())

plt.show()

Expand Down