From de2f714382e2e5c502a550dba1860e4d961f808d Mon Sep 17 00:00:00 2001 From: fredrik-1 Date: Sun, 15 Apr 2018 18:47:52 +0200 Subject: [PATCH] fix figure.clf and subplot_adjust --- .../subplots_adjust_keyword.rst | 13 ++ lib/matplotlib/figure.py | 138 ++++++++++++------ lib/matplotlib/pyplot.py | 50 +++++-- lib/matplotlib/tests/test_figure.py | 42 ++++++ 4 files changed, 183 insertions(+), 60 deletions(-) create mode 100644 doc/users/next_whats_new/subplots_adjust_keyword.rst diff --git a/doc/users/next_whats_new/subplots_adjust_keyword.rst b/doc/users/next_whats_new/subplots_adjust_keyword.rst new file mode 100644 index 000000000000..b902aa182a98 --- /dev/null +++ b/doc/users/next_whats_new/subplots_adjust_keyword.rst @@ -0,0 +1,13 @@ +subplots_adjust has a new ``kwarg``: ``rc_default`` +--------------------------------------------------- + +`.Figure.subplots_adjust` and `.pyplot.subplots_adjust` have a new ``kwarg``: +``rc_default`` that determines the default values for the subplot parameters. + +The `.figure.SubplotParams` object has a new get method +:meth:`~.SubplotParams.get_subplot_params` + + + + + diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index b97655e8fb39..5567cf761e20 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -173,77 +173,82 @@ def __init__(self, left=None, bottom=None, right=None, top=None, wspace=None, hspace=None): """ All dimensions are fractions of the figure width or height. - Defaults are given by :rc:`figure.subplot.[name]`. + Defaults are given by :rc:`figure.subplot.*`. Parameters ---------- - left : float + left : float, optional The left side of the subplots of the figure. - right : float + right : float, optional The right side of the subplots of the figure. - bottom : float + bottom : float, optional The bottom of the subplots of the figure. - top : float + top : float, optional The top of the subplots of the figure. - wspace : float + wspace : float, optional The amount of width reserved for space between subplots, expressed as a fraction of the average axis width. - hspace : float + hspace : float, optional The amount of height reserved for space between subplots, expressed as a fraction of the average axis height. """ self.validate = True self.update(left, bottom, right, top, wspace, hspace) + def __repr__(self): + return ("SubplotParams(left={}, bottom={}, right={}, top={}, " + "wspace={}, hspace={})").format( + self.left, self.bottom, self.right, self.top, + self.wspace, self.hspace) + def update(self, left=None, bottom=None, right=None, top=None, - wspace=None, hspace=None): - """ - Update the dimensions of the passed parameters. *None* means unchanged. - """ - thisleft = getattr(self, 'left', None) - thisright = getattr(self, 'right', None) - thistop = getattr(self, 'top', None) - thisbottom = getattr(self, 'bottom', None) - thiswspace = getattr(self, 'wspace', None) - thishspace = getattr(self, 'hspace', None) - - self._update_this('left', left) - self._update_this('right', right) - self._update_this('bottom', bottom) - self._update_this('top', top) - self._update_this('wspace', wspace) - self._update_this('hspace', hspace) - - def reset(): - self.left = thisleft - self.right = thisright - self.top = thistop - self.bottom = thisbottom - self.wspace = thiswspace - self.hspace = thishspace + wspace=None, hspace=None, rc_default=False): + """ + Update the dimensions of the passed parameters. *None* means + unchanged if the attribute is set and *rc_default* is *False*, and + :rc:`figure.subplot.*` otherwise. + """ + + varDict = dict(left=left, bottom=bottom, right=right, top=top, + wspace=wspace, hspace=hspace) + oldVarDict = {key: getattr(self, key, None) for key in varDict.keys()} + self._update(varDict, rc_default) if self.validate: if self.left >= self.right: - reset() + self._update(oldVarDict) raise ValueError('left cannot be >= right') if self.bottom >= self.top: - reset() + self._update(oldVarDict) raise ValueError('bottom cannot be >= top') - def _update_this(self, s, val): - if val is None: - val = getattr(self, s, None) - if val is None: - key = 'figure.subplot.' + s - val = rcParams[key] + def _update(self, varDict, rc_default=None): + for att, value in varDict.items(): + if value is None: + if not rc_default: + value = getattr(self, att, None) + if value is None: + key = 'figure.subplot.' + att + value = rcParams[key] - setattr(self, s, val) + setattr(self, att, value) + + def get_subplot_params(self): + """ + Returns + ------- + subplot_params : dictionary + A dictionary with the subplot parameters + """ + subplot_params = self.__dict__.copy() + del subplot_params['validate'] + return subplot_params class Figure(Artist): @@ -1406,8 +1411,11 @@ def clf(self, keep_observers=False): """ Clear the figure. - Set *keep_observers* to True if, for example, - a gui widget is tracking the axes in the figure. + Parameters + ---------- + keep_observers : bool, optional + Set *keep_observers* to True if, for example, + a gui widget is tracking the axes in the figure. """ self.suppressComposite = None self.callbacks = cbook.CallbackRegistry() @@ -1426,6 +1434,7 @@ def clf(self, keep_observers=False): self.texts = [] self.images = [] self.legends = [] + self.subplotpars.update(rc_default=True) if not keep_observers: self._axobservers = [] self._suptitle = None @@ -1915,13 +1924,48 @@ def colorbar(self, mappable, cax=None, ax=None, use_gridspec=True, **kw): return cb def subplots_adjust(self, left=None, bottom=None, right=None, top=None, - wspace=None, hspace=None): + wspace=None, hspace=None, rc_default=False): """ - Update the :class:`SubplotParams` with *kwargs* (defaulting to rc when - *None*) and update the subplot locations. + Tune the subplots layout by updating the subplots parameters and + the subplot locations. + + All dimensions are fractions of the figure width or height. + + Parameters + ---------- + left : float, optional + The left side of the subplots of the figure. + + right : float, optional + The right side of the subplots of the figure. + + bottom : float, optional + The bottom of the subplots of the figure. + top : float, optional + The top of the subplots of the figure. + + wspace : float, optional + The amount of width reserved for space between subplots, + expressed as a fraction of the average axis width. + + hspace : float, optional + The amount of height reserved for space between subplots, + expressed as a fraction of the average axis height. + + rc_default : bool, optional + Determine the defaults. *False*, the default, and the values + are unchanged. *True* and the values are taken from + :rc:`figure.subplot.*` + + Notes + ----- + The subplots parameters are stored in the `~.Figure` attribute + ``subplotpars`` as a `~.SubplotParams` object. """ - self.subplotpars.update(left, bottom, right, top, wspace, hspace) + + self.subplotpars.update(left, bottom, right, top, wspace, + hspace, rc_default) for ax in self.axes: if not isinstance(ax, SubplotBase): # Check if sharing a subplots axis diff --git a/lib/matplotlib/pyplot.py b/lib/matplotlib/pyplot.py index cbd5371d32a0..18c3d1b6e14a 100644 --- a/lib/matplotlib/pyplot.py +++ b/lib/matplotlib/pyplot.py @@ -1204,25 +1204,49 @@ def twiny(ax=None): def subplots_adjust(left=None, bottom=None, right=None, top=None, - wspace=None, hspace=None): + wspace=None, hspace=None, rc_default=False): """ - Tune the subplot layout. + Tune the subplots layout by updating the subplots parameters and + the subplot locations. - The parameter meanings (and suggested defaults) are:: + All dimensions are fractions of the figure width or height. - left = 0.125 # the left side of the subplots of the figure - right = 0.9 # the right side of the subplots of the figure - bottom = 0.1 # the bottom of the subplots of the figure - top = 0.9 # the top of the subplots of the figure - wspace = 0.2 # the amount of width reserved for space between subplots, - # expressed as a fraction of the average axis width - hspace = 0.2 # the amount of height reserved for space between subplots, - # expressed as a fraction of the average axis height + Parameters + ---------- + left : float, optional + The left side of the subplots of the figure. + + right : float, optional + The right side of the subplots of the figure. + + bottom : float, optional + The bottom of the subplots of the figure. + + top : float, optional + The top of the subplots of the figure. + + wspace : float, optional + The amount of width reserved for space between subplots, + expressed as a fraction of the average axis width. - The actual defaults are controlled by the rc file + hspace : float, optional + The amount of height reserved for space between subplots, + expressed as a fraction of the average axis height. + + rc_default : bool, optional + Determine the defaults. *False*, the default, and the values + are unchanged. *True* and the values are taken from + :rc:`figure.subplot.*` + + + Notes + ----- + The subplots parameters are stored in the `~.Figure` attribute + ``subplotpars`` as a `~.SubplotParams` object. """ + fig = gcf() - fig.subplots_adjust(left, bottom, right, top, wspace, hspace) + fig.subplots_adjust(left, bottom, right, top, wspace, hspace, rc_default) def subplot_tool(targetfig=None): diff --git a/lib/matplotlib/tests/test_figure.py b/lib/matplotlib/tests/test_figure.py index d0079012a5e6..007566b0bbbd 100644 --- a/lib/matplotlib/tests/test_figure.py +++ b/lib/matplotlib/tests/test_figure.py @@ -383,3 +383,45 @@ def test_fspath(fmt, tmpdir): # All the supported formats include the format name (case-insensitive) # in the first 100 bytes. assert fmt.encode("ascii") in file.read(100).lower() + + +def test_clf_subplotpars(): + keys = ('left', 'right', 'bottom', 'top', 'wspace', 'hspace') + rc_params = {key: plt.rcParams['figure.subplot.'+key] for key in keys} + + fig = plt.figure(1) + fig.subplots_adjust(left=0.1) + fig.clf() + assert fig.subplotpars.get_subplot_params() == rc_params + + +def test_suplots_adjust_1(): + fig = plt.figure(1) + wspace = 0 + fig.subplots_adjust(wspace=wspace) + inDict = dict(left=0.1, right=0.7, bottom=0, top=0.9, hspace=0.05) + fig.subplots_adjust(**inDict) + inDict['wspace'] = wspace + assert fig.subplotpars.get_subplot_params() == inDict + + +def test_suplots_adjust_2(): + fig = plt.figure(1) + fig.subplots_adjust(wspace=0) + inDict = dict(left=0.1, right=0.7, bottom=0, top=0.9, hspace=0.05, + rc_default=True) + fig.subplots_adjust(**inDict) + inDict['wspace'] = plt.rcParams['figure.subplot.wspace'] + del inDict['rc_default'] + assert fig.subplotpars.get_subplot_params() == inDict + + +def test_suplots_adjust_plt(): + plt.figure(1) + plt.subplots_adjust(wspace=0) + inDict = dict(left=0.1, right=0.7, bottom=0, top=0.9, hspace=0.05, + rc_default=True) + plt.subplots_adjust(**inDict) + inDict['wspace'] = plt.rcParams['figure.subplot.wspace'] + del inDict['rc_default'] + assert plt.gcf().subplotpars.get_subplot_params() == inDict