8000 Merge pull request #22737 from tacaswell/auto-backport-of-pr-22138-on… · matplotlib/matplotlib@57b7a17 · GitHub
[go: up one dir, main page]

Skip to content

Commit 57b7a17

Browse files
authored
Merge pull request #22737 from tacaswell/auto-backport-of-pr-22138-on-v3.5.x
Backport PR #22138: Fix clearing subfigures
2 parents 0734a70 + d4588a3 commit 57b7a17

File tree

6 files changed

+152
-38
lines changed

6 files changed

+152
-38
lines changed

doc/api/prev_api_changes/api_changes_3.0.0.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ Hold machinery
471471
Setting or unsetting ``hold`` (:ref:`deprecated in version 2.0<v200_deprecate_hold>`) has now
472472
been completely removed. Matplotlib now always behaves as if ``hold=True``.
473473
To clear an axes you can manually use :meth:`~.axes.Axes.cla()`,
474-
or to clear an entire figure use :meth:`~.figure.Figure.clf()`.
474+
or to clear an entire figure use :meth:`~.figure.Figure.clear()`.
475475

476476

477477
Removal of deprecated backends

doc/users/prev_whats_new/changelog.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3139,7 +3139,7 @@ the `API changes <../../api/api_changes.html>`_.
31393139
Fixed a bug in backend_qt4, reported on mpl-dev - DSD
31403140

31413141
2007-03-26
3142-
Removed colorbar_classic from figure.py; fixed bug in Figure.clf() in which
3142+
Removed colorbar_classic from figure.py; fixed bug in Figure.clear() in which
31433143
_axobservers was not getting cleared. Modernization and cleanups. - EF
31443144

31453145
2007-03-26

lib/matplotlib/figure.py

Lines changed: 59 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -935,6 +935,59 @@ def _break_share_link(ax, grouper):
935935
# Break link between any twinned axes
936936
_break_share_link(ax, ax._twinned_axes)
937937

938+
def clear(self, keep_observers=False):
939+
"""
940+
Clear the figure.
941+
942+
Parameters
943+
----------
944+ keep_observers: bool, default: False
945+
Set *keep_observers* to True if, for example,
946+
a gui widget is tracking the Axes in the figure.
947+
"""
948+
self.suppressComposite = None
949+
self.callbacks = cbook.CallbackRegistry()
950+
951+
# first clear the axes in any subfigures
952+
for subfig in self.subfigs:
953+
subfig.clear(keep_observers=keep_observers)
954+
self.subfigs = []
955+
956+
for ax in tuple(self.axes): # Iterate over the copy.
957+
ax.cla()
958+
self.delaxes(ax) # Remove ax from self._axstack.
959+
960+
self.artists = []
961+
self.lines = []
962+
self.patches = []
963+
self.texts = []
964+
self.images = []
965+
self.legends = []
966+
if not keep_observers:
967+
self._axobservers = cbook.CallbackRegistry()
968+
self._suptitle = None
969+
self._supxlabel = None
970+
self._supylabel = None
971+
972+
self.stale = True
973+
974+
# synonym for `clear`.
975+
def clf(self, keep_observers=False):
976+
"""
977+
Alias for the `clear()` method.
978+
979+
.. admonition:: Discouraged
980+
981+
The use of ``clf()`` is discouraged. Use ``clear()`` instead.
982+
983+
Parameters
984+
----------
985+
keep_observers: bool, default: False
986+
Set *keep_observers* to True if, for example,
987+
a gui widget is tracking the Axes in the figure.
988+
"""
989+
return self.clear(keep_observers=keep_observers)
990+
938991
# Note: in the docstring below, the newlines in the examples after the
939992
# calls to legend() allow replacing it with figlegend() to generate the
940993
# docstring of pyplot.figlegend.
@@ -2310,7 +2363,7 @@ def __init__(self,
23102363
self.set_tight_layout(tight_layout)
23112364

23122365
self._axstack = _AxesStack() # track all figure axes and current axes
2313-
self.clf()
2366+
self.clear()
23142367
self._cachedRenderer = None
23152368

23162369
self.set_constrained_layout(constrained_layout)
@@ -2747,41 +2800,14 @@ def set_figheight(self, val, forward=True):
27472800
"""
27482801
self.set_size_inches(self.get_figwidth(), val, forward=forward)
27492802

2750-
def clf(self, keep_observers=False):
2751-
"""
2752-
Clear the figure.
2753-
2754-
Set *keep_observers* to True if, for example,
2755-
a gui widget is tracking the Axes in the figure.
2756-
"""
2757-
self.suppressComposite = None
2758-
self.callbacks = cbook.CallbackRegistry()
2759-
2760-
for ax in tuple(self.axes): # Iterate over the copy.
2761-
ax.cla()
2762-
self.delaxes(ax) # removes ax from self._axstack
2763-
2803+
def clear(self, keep_observers=False):
2804+
# docstring inherited
2805+
super().clear(keep_observers=keep_observers)
2806+
# FigureBase.clear does not clear toolbars, as
2807+
# only Figure can have toolbars
27642808
toolbar = getattr(self.canvas, 'toolbar', None)
27652809
if toolbar is not None:
27662810
toolbar.update()
2767-
self._axstack.clear()
2768-
self.artists = []
2769-
self.lines = []
2770-
self.patches = []
2771-
self.texts = []
2772-
self.images = []
2773-
self.legends = []
2774-
if not keep_observers:
2775-
self._axobservers = cbook.CallbackRegistry()
2776-
self._suptitle = None
2777-
self._supxlabel = None
2778-
self._supylabel = None
2779-
2780-
self.stale = True
2781-
2782-
def clear(self, keep_observers=False):
2783-
"""Clear the figure -- synonym for `clf`."""
2784-
self.clf(keep_observers=keep_observers)
27852811

27862812
@_finalize_rasterization
27872813
@allow_rasterization

lib/matplotlib/pyplot.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -955,7 +955,7 @@ def close(fig=None):
955955

956956
def clf():
957957
"""Clear the current figure."""
958-
gcf().clf()
958+
gcf().clear()
959959

960960

961961
def draw():

lib/matplotlib/tests/test_figure.py

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
from matplotlib._api.deprecation import MatplotlibDeprecationWarning
1616
from matplotlib.testing.decorators import image_comparison, check_figures_equal
1717
from matplotlib.axes import Axes
18-
from matplotlib.figure import Figure
18+
from matplotlib.figure import Figure, FigureBase
1919
from matplotlib.ticker import AutoMinorLocator, FixedFormatter, ScalarFormatter
2020
import matplotlib.pyplot as plt
2121
import matplotlib.dates as mdates
@@ -689,6 +689,94 @@ def test_removed_axis():
689689
fig.canvas.draw()
690690

691691

692+
@pytest.mark.parametrize('clear_meth', ['clear', 'clf'])
693+
def test_figure_clear(clear_meth):
694+
# we test the following figure clearing scenarios:
695+
fig = plt.figure()
696+
697+
# a) an empty figure
698+
fig.clear()
699+
assert fig.axes == []
700+
701+
# b) a figure with a single unnested axes
702+
ax = fig.add_subplot(111)
703+
getattr(fig, clear_meth)()
704+
assert fig.axes == []
705+
706+
# c) a figure multiple unnested axes
707+
axes = [fig.add_subplot(2, 1, i+1) for i in range(2)]
708+
getattr(fig, clear_meth)()
709+
assert fig.axes == []
710+
711+
# d) a figure with a subfigure
712+
gs = fig.add_gridspec(ncols=2, nrows=1)
713+
subfig = fig.add_subfigure(gs[0])
714+
subaxes = subfig.add_subplot(111)
715+
getattr(fig, clear_meth)()
716+
assert subfig not in fig.subfigs
717+
assert fig.axes == []
718+
719+
# e) a figure with a subfigure and a subplot
720+
subfig = fig.add_subfigure(gs[0])
721+
subaxes = subfig.add_subplot(111)
722+
mainaxes = fig.add_subplot(gs[1])
723+
724+
# e.1) removing just the axes leaves the subplot
725+
mainaxes.remove()
726+
assert fig.axes == [subaxes]
727+
728+
# e.2) removing just the subaxes leaves the subplot
729+
# and subfigure
730+
mainaxes = fig.add_subplot(gs[1])
731+
subaxes.remove()
732+
assert fig.axes == [mainaxes]
733+
assert subfig in fig.subfigs
734+
735+
# e.3) clearing the subfigure leaves the subplot
736+
subaxes = subfig.add_subplot(111)
737+
assert mainaxes in fig.axes
738+
assert subaxes in fig.axes
739+
getattr(subfig, clear_meth)()
740+
assert subfig in fig.subfigs
741+
assert subaxes not in subfig.axes
742+
assert subaxes not in fig.axes
743+
assert mainaxes in fig.axes
744+
745+
# e.4) clearing the whole thing
746+
subaxes = subfig.add_subplot(111)
747+
getattr(fig, clear_meth)()
748+
assert fig.axes == []
749+
assert fig.subfigs == []
750+
751+
# f) multiple subfigures
752+
subfigs = [fig.add_subfigure(gs[i]) for i in [0, 1]]
753+
subaxes = [sfig.add_subplot(111) for sfig in subfigs]
754+
assert all(ax in fig.axes for ax in subaxes)
755+
assert all(sfig in fig.subfigs for sfig in subfigs)
756+
757+
# f.1) clearing only one subfigure
758+
getattr(subfigs[0], clear_meth)()
759+
assert subaxes[0] not in fig.axes
760+
assert subaxes[1] in fig.axes
761+
assert subfigs[1] in fig.subfigs
762+
763+
# f.2) clearing the whole thing
764+
getattr(subfigs[1], clear_meth)()
765+
subfigs = [fig.add_subfigure(gs[i]) for i in [0, 1]]
766+
subaxes = [sfig.add_subplot(111) for sfig in subfigs]
767+
assert all(ax in fig.axes for ax in subaxes)
768+
assert all(sfig in fig.subfigs for sfig in subfigs)
769+
getattr(fig, clear_meth)()
770+
assert fig.subfigs == []
771+
assert fig.axes == []
772+
773+
774+
def test_clf_not_refedined():
775+
for klass in FigureBase.__subclasses__():
776+
# check that subclasses do not get redefined in our Figure subclasses
777+
assert 'clf' not in klass.__dict__
778+
779+
692780
@mpl.style.context('mpl20')
693781
def test_picking_does_not_stale():
694782
fig, ax = plt.subplots()

lib/matplotlib/tests/test_usetex.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def test_minus_no_descent(fontsize):
7171
heights = {}
7272
fig = plt.figure()
7373
for vals in [(1,), (-1,), (-1, 1)]:
74-
fig.clf()
74+
fig.clear()
7575
for x in vals:
7676
fig.text(.5, .5, f"${x}$", usetex=True)
7777
fig.canvas.draw()

0 commit comments

Comments
 (0)
0