Skip to content

MNT: Restore auto-adding Axes3D to their parent figure on init #19496

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 3 commits into from
Feb 18, 2021
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
33 changes: 25 additions & 8 deletions doc/api/next_api_changes/behavior/18564-AL.rst
Original file line number Diff line number Diff line change
@@ -1,8 +1,25 @@
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`.
Axes3D automatically adding self to Figure is deprecated
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

New `.Axes3D` objects previously added themselves to figures when they
were created, unlike all other Axes classes, which lead to them being
added twice if ``fig.add_subplot(111, projection='3d')`` was called.

This behavior is now deprecated and will warn. The new keyword argument
*auto_add_to_figure* controls the behavior and can be used to suppress the
warning. The default value will change to False in mpl3.5, and any
non-False value will be an error in mpl3.6.

In the future, `.Axes3D` will need to be explicitly added to the
figure ::

fig = Figure()
# create Axes3D
ax = Axes3d(fig)
# add to Figure
fig.add_axes(ax)

as needs to be done for other `.axes.Axes` sub-classes. Or, a 3-d
projection can be made via::

fig.add_subplot(projection='3d')
3 changes: 2 additions & 1 deletion examples/pyplots/whats_new_99_mplot3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
Z = np.sin(R)

fig = plt.figure()
ax = Axes3D(fig)
ax = Axes3D(fig, auto_add_to_figure=False)
fig.add_axes(ax)
ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.viridis)

plt.show()
Expand Down
3 changes: 2 additions & 1 deletion lib/matplotlib/figure.py
Original file line number Diff line number Diff line change
Expand Up @@ -1517,7 +1517,8 @@ def _process_projection_requirements(
raise TypeError(
f"projection must be a string, None or implement a "
f"_as_mpl_axes method, not {projection!r}")

if projection_class.__name__ == 'Axes3D':
kwargs.setdefault('auto_add_to_figure', False)
return projection_class, kwargs

def get_default_bbox_extra_artists(self):
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 @@ -391,7 +391,7 @@ def test_polycollection_close():
[[3., 0.], [3., 1.], [4., 1.], [4., 0.]]]

fig = plt.figure()
ax = fig.add_axes(Axes3D(fig))
ax = fig.add_axes(Axes3D(fig, auto_add_to_figure=False))

colors = ['r', 'g', 'b', 'y', 'k']
zpos = list(range(5))
Expand Down
23 changes: 23 additions & 0 deletions lib/mpl_toolkits/mplot3d/axes3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,15 @@ def __init__(
Other axes to share z-limits with.
proj_type : {'persp', 'ortho'}
The projection type, default 'persp'.
auto_add_to_figure : bool, default: True
Prior to Matplotlib 3.4 Axes3D would add themselves
to their host Figure on init. Other Axes class do not
do this.

This behavior is deprecated in 3.4, the default will
change to False in 3.5. The keyword will be undocumented
and a non-False value will be an error in 3.6.

**kwargs
Other optional keyword arguments:

Expand Down Expand Up @@ -97,6 +106,8 @@ def __init__(
self._shared_z_axes.join(self, sharez)
self._adjustable = 'datalim'

auto_add_to_figure = kwargs.pop('auto_add_to_figure', True)

super().__init__(
fig, rect, frameon=True, box_aspect=box_aspect, *args, **kwargs
)
Expand Down Expand Up @@ -129,6 +140,18 @@ def __init__(
# for bounding box calculations
self.spines[:].set_visible(False)

if auto_add_to_figure:
_api.warn_deprecated(
"3.4", removal="3.6", message="Axes3D(fig) adding itself "
"to the figure is deprecated since %(since)s. "
"Pass the keyword argument auto_add_to_figure=False "
"and use fig.add_axes(ax) to suppress this warning. "
"The default value of auto_add_to_figure will change to "
"False in mpl3.5 and True values will "
"no longer work %(removal)s. This is consistent with "
"other Axes classes.")
fig.add_axes(self)

def set_axis_off(self):
self._axis3don = False
self.stale = True
Expand Down
7 changes: 5 additions & 2 deletions lib/mpl_toolkits/tests/test_mplot3d.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,14 @@
from mpl_toolkits.mplot3d import Axes3D, axes3d, proj3d, art3d
import matplotlib as mpl
from matplotlib.backend_bases import MouseButton
from matplotlib.cbook import MatplotlibDeprecationWarning
from matplotlib import cm
from matplotlib import colors as mcolors
from matplotlib.testing.decorators import image_comparison, check_figures_equal
from matplotlib.testing.widgets import mock_event
from matplotlib.collections import LineCollection, PolyCollection
from matplotlib.patches import Circle

import matplotlib.pyplot as plt
import numpy as np

Expand Down Expand Up @@ -725,7 +727,7 @@ def test_add_collection3d_zs_scalar():
@mpl3d_image_comparison(['axes3d_labelpad.png'], remove_text=False)
def test_axes3d_labelpad():
fig = plt.figure()
ax = fig.add_axes(Axes3D(fig))
ax = fig.add_axes(Axes3D(fig, auto_add_to_figure=False))
# labelpad respects rcParams
assert ax.xaxis.labelpad == mpl.rcParams['axes.labelpad']
# labelpad can be set in set_label
Expand Down Expand Up @@ -1148,7 +1150,8 @@ def test_inverted_cla():

def test_ax3d_tickcolour():
fig = plt.figure()
ax = Axes3D(fig)
with pytest.warns(MatplotlibDeprecationWarning):
ax = Axes3D(fig)

ax.tick_params(axis='x', colors='red')
ax.tick_params(axis='y', colors='red')
Expand Down