diff --git a/lib/matplotlib/figure.py b/lib/matplotlib/figure.py index 04f0e73d2671..b9a789d57c93 100644 --- a/lib/matplotlib/figure.py +++ b/lib/matplotlib/figure.py @@ -183,6 +183,7 @@ class FigureBase(Artist): Base class for `.figure.Figure` and `.figure.SubFigure` containing the methods that add artists to the figure or subfigure, create Axes, etc. """ + def __init__(self, **kwargs): super().__init__() # remove the non-figure artist _axes property @@ -1051,9 +1052,9 @@ def legend(self, *args, **kwargs): """ handles, labels, extra_args, kwargs = mlegend._parse_legend_args( - self.axes, - *args, - **kwargs) + self.axes, + *args, + **kwargs) # check for third arg if len(extra_args): # _api.warn_deprecated( @@ -1191,6 +1192,11 @@ def subplots_adjust(self, left=None, bottom=None, right=None, top=None, hspace : float, optional The height of the padding between subplots, as a fraction of the average Axes height. + + See Also + -------- + matplotlib.figure.Figure.set_subplotpars + matplotlib.figure.Figure.get_subplotpars """ if self.get_constrained_layout(): self.set_constrained_layout(False) @@ -1204,6 +1210,65 @@ def subplots_adjust(self, left=None, bottom=None, right=None, top=None, ax._set_position(ax.get_subplotspec().get_position(self)) self.stale = True + def set_subplotpars(self, subplotparams={}): + """ + Set the subplot layout parameters. + Accepts either a `.SubplotParams` object, from which the relevant + parameters are copied, or a dictionary of subplot layout parameters. + If a dictionary is provided, this function is a convenience wrapper for + `matplotlib.figure.Figure.subplots_adjust` + + Parameters + ---------- + subplotparams : `~matplotlib.figure.SubplotParams` or dict with keys \ +"left", "bottom", "right", 'top", "wspace", "hspace"] , optional + SubplotParams object to copy new subplot parameters from, or a dict + of SubplotParams constructor arguments. + By default, an empty dictionary is passed, which maintains the + current state of the figure's `.SubplotParams` + + See Also + -------- + matplotlib.figure.Figure.subplots_adjust + matplotlib.figure.Figure.get_subplotpars + """ + subplotparams_args = ["left", "bottom", "right", + "top", "wspace", "hspace"] + kwargs = {} + if isinstance(subplotparams, SubplotParams): + for key in subplotparams_args: + kwargs[key] = getattr(subplotparams, key) + elif isinstance(subplotparams, dict): + for key in subplotparams.keys(): + if key in subplotparams_args: + kwargs[key] = subplotparams[key] + else: + _api.warn_external( + f"'{key}' is not a valid key for set_subplotpars;" + " this key was ignored.") + else: + raise TypeError( + "subplotpars must be a dictionary of keyword-argument pairs or" + " an instance of SubplotParams()") + if kwargs == {}: + self.set_subplotpars(self.get_subplotpars()) + self.subplots_adjust(**kwargs) + + def get_subplotpars(self): + """ + Return the `.SubplotParams` object associated with the Figure. + + Returns + ------- + `.SubplotParams` + + See Also + -------- + matplotlib.figure.Figure.subplots_adjust + matplotlib.figure.Figure.get_subplotpars + """ + return self.subplotpars + def align_xlabels(self, axs=None): """ Align the xlabels of subplots in the same subplot column if label @@ -2115,6 +2180,9 @@ def draw(self, renderer): @docstring.interpd +@cbook._define_aliases({ + "size_inches": ["figsize"] +}) class Figure(FigureBase): """ The top level container for all the plot elements. @@ -2231,24 +2299,6 @@ def __init__(self, """ super().__init__(**kwargs) - if layout is not None: - if tight_layout is not None: - _api.warn_external( - "The Figure parameters 'layout' and 'tight_layout' " - "cannot be used together. Please use 'layout' only.") - if constrained_layout is not None: - _api.warn_external( - "The Figure parameters 'layout' and 'constrained_layout' " - "cannot be used together. Please use 'layout' only.") - if layout == 'constrained': - tight_layout = False - constrained_layout = True - elif layout == 'tight': - tight_layout = True - constrained_layout = False - else: - _api.check_in_list(['constrained', 'tight'], layout=layout) - self.callbacks = cbook.CallbackRegistry() # Callbacks traditionally associated with the canvas (and exposed with # a proxy property), but that actually need to be on the figure for @@ -2299,15 +2349,11 @@ def __init__(self, self.subplotpars = subplotpars # constrained_layout: - self._constrained = False - - self.set_tight_layout(tight_layout) self._axstack = _AxesStack() # track all figure axes and current axes self.clf() self._cachedRenderer = None - - self.set_constrained_layout(constrained_layout) + self.set_layout(layout, tight_layout, constrained_layout) # list of child gridspecs for this figure self._gridspecs = [] @@ -2399,11 +2445,172 @@ def _set_dpi(self, dpi, forward=True): dpi = property(_get_dpi, _set_dpi, doc="The resolution in dots per inch.") + @property + def layout(self): + """ + Return the current figure layout solver. + """ + if hasattr(self, '_constrained'): + if self.get_constrained_layout(): + layout = 'constrained' + elif self.get_tight_layout(): + layout = 'tight' + else: + layout = None + else: + layout = None + return layout + + def get_layout(self): + """ + Return the current figure layout solver. + """ + return self.layout + + def set_layout(self, layout=None, tight_layout=None, + constrained_layout=None): + """ + Set the figure layout specification. (Optionally) sets how + `.tight_layout` or `.set_constrained_layout` is used when a dict is + provided to *tight_layout* or *constrained_layout*. + + + - If *layout* is not *None*, the layout solver is determined + exclusively by *layout*, regardless of *tight_layout* or + *constrained_layout*, but optional padding parameters stored in + *tight_layout* or *constrained_layout* are used with the respective + layout. + For instance: + + - if *layout* is *'tight'*, *tight_layout* is *False*, and + *constrained_layout* is *True*, `.tight_layout` with default paddings + is used to format the figure. + + - If *layout* is *'constrained'*, *tight_layout* is *{'pad':1}*, and + *constrained_layout* is *{'w_pad':1}*, then + `.set_constrained_layout` is called with padding parameters + *{'w_pad':1}*. + + - If *layout* is None, *tight_layout* and *constrained_layout* are + mutually exclusive. That is, only one can be *True* or a dict, as + resolving the case where both are not *False* and *layout* is *None* + is ambiguous. + + Parameters + ---------- + layout : {'constrained', 'tight', None}, optional + The layout mechanism for positioning of plot elements. + Supported values: + + - 'constrained': The constrained layout solver usually gives the + best layout results and is thus recommended. However, it is + computationally expensive and can be slow for complex figures + with many elements. + + See :doc:`/tutorials/intermediate/constrainedlayout_guide` + for examples. + + - 'tight': Use the tight layout mechanism. This is a relatively + simple algorithm, that adjusts the subplot parameters so that + decorations like tick labels, axis labels and titles have enough + space. See `.Figure.set_tight_layout` for further details. + + If not given, fall back to using the parameters *tight_layout* and + *constrained_layout*, including their config defaults + :rc:`figure.autolayout` and :rc:`figure.constrained_layout.use`. + tight_layout : bool or dict with keys "pad", "w_pad", "h_pad", \ +"rect", or None + If a bool, sets whether to call `.tight_layout` upon drawing. + If ``None``, use :rc:`figure.autolayout` instead. + If a dict, pass it as kwargs to `.tight_layout`, overriding the + default paddings. + constrained_layout : bool or dict with keys "w_pad", "h_pad", \ +"wspace", "hspace" or None + If a bool, sets whether to use ``constrained_layout`` upon drawing. + If ``None``, use :rc:`figure.autolayout` instead. + If a dict, pass it as kwargs to `.set_constrained_layout`, + overriding the default paddings. + """ + if ( + layout is None and + tight_layout is None and + constrained_layout is None + ): + layout = self.get_layout() + + if layout is not None: + # these will store the state of any warnings we need to pop + layout_clash = False + bool_conflict = False + type_conflict = False + + if layout == 'constrained': + layoutstr = 'constrained_layout' + falselayoutstr = 'tight_layout' + layout_clash = tight_layout not in [False, None] + tight_layout = False + bool_conflict = ( + isinstance(constrained_layout, bool) and + not constrained_layout + ) + type_conflict = not isinstance(constrained_layout, + (dict, bool, type(None))) + if ( + bool_conflict or + type_conflict or + constrained_layout is None + ): + constrained_layout = True + + elif layout == 'tight': + layoutstr = 'tight_layout' + falselayoutstr = 'constrained_layout' + layout_clash = constrained_layout not in [False, None] + constrained_layout = False + bool_conflict = ( + isinstance(tight_layout, bool) and + not tight_layout + ) + type_conflict = not isinstance(tight_layout, + (dict, bool, type(None))) + if bool_conflict or type_conflict or tight_layout is None: + tight_layout = True + else: + _api.check_in_list(['constrained', 'tight'], layout=layout) + + if layout_clash: + _api.warn_external(f"Figure parameters " + f"'layout'=='{layout}' and " + f"'{falselayoutstr}'!=False cannot " + f"be used together. " + f"Please use 'layout' only.") + if bool_conflict: + _api.warn_external(f"Figure parameters " + f"'layout'=='{layout}' and " + f"'{layoutstr}'==False cannot be " + f"used together. " + f"Please use 'layout' only.") + if type_conflict: + _api.warn_external(f"Figure parameters " + f"'layout'=='{layout}' and " + f"'{layoutstr}' cannot be " + f"used together if '{layoutstr}' is " + f"not True or a dictionary of " + f"{layoutstr} arguments. " + f"Please use 'layout' only.") + else: + if all([tight_layout, constrained_layout]): + raise ValueError("Cannot set 'tight_layout' and " + "'constrained_layout' simultaneously.") + self._constrained = False + self.set_tight_layout(tight_layout) + self.set_constrained_layout(constrained_layout) + def get_tight_layout(self): """Return whether `.tight_layout` is called when drawing.""" return self._tight - def set_tight_layout(self, tight): + def set_tight_layout(self, tight=None): """ Set whether and how `.tight_layout` is called when drawing. @@ -2429,7 +2636,7 @@ def get_constrained_layout(self): """ return self._constrained - def set_constrained_layout(self, constrained): + def set_constrained_layout(self, constrained=None): """ Set whether ``constrained_layout`` is used upon drawing. If None, :rc:`figure.constrained_layout.use` value will be used. diff --git a/lib/matplotlib/tests/test_figure.py b/lib/matplotlib/tests/test_figure.py index cb8f63893aea..7dbd017cb668 100644 --- a/lib/matplotlib/tests/test_figure.py +++ b/lib/matplotlib/tests/test_figure.py @@ -10,12 +10,13 @@ import pytest from PIL import Image + import matplotlib as mpl from matplotlib import cbook, rcParams from matplotlib._api.deprecation import MatplotlibDeprecationWarning from matplotlib.testing.decorators import image_comparison, check_figures_equal from matplotlib.axes import Axes -from matplotlib.figure import Figure +from matplotlib.figure import Figure, SubplotParams from matplotlib.ticker import AutoMinorLocator, FixedFormatter, ScalarFormatter import matplotlib.pyplot as plt import matplotlib.dates as mdates @@ -350,6 +351,10 @@ def test_set_fig_size(): assert fig.get_figwidth() == 1 assert fig.get_figheight() == 3 + fig.set_figsize(2, 4) + assert fig.get_figsize()[0] == 2 + assert fig.get_figsize()[1] == 4 + def test_axes_remove(): fig, axs = plt.subplots(2, 2) @@ -539,30 +544,317 @@ def test_valid_layouts(): assert not fig.get_tight_layout() assert fig.get_constrained_layout() + fig = Figure(tight_layout={'pad': 1}) + assert fig.get_tight_layout() + assert not fig.get_constrained_layout() + + fig = Figure(tight_layout=True) + assert fig.get_tight_layout() + assert not fig.get_constrained_layout() + + fig = Figure(tight_layout=True, constrained_layout=False) + assert fig.get_tight_layout() + assert not fig.get_constrained_layout() + + fig = Figure(constrained_layout={'w_pad': 1}) + assert not fig.get_tight_layout() + assert fig.get_constrained_layout() + + fig = Figure(constrained_layout=True) + assert not fig.get_tight_layout() + assert fig.get_constrained_layout() + + fig = Figure(constrained_layout=True, tight_layout=False) + assert not fig.get_tight_layout() + assert fig.get_constrained_layout() + + fig = Figure(layout='tight', tight_layout={'pad': 1}) + assert fig.get_tight_layout() + assert not fig.get_constrained_layout() + + fig = Figure(layout='constrained', constrained_layout={'w_pad': 1}) + assert not fig.get_tight_layout() + assert fig.get_constrained_layout() + + fig = Figure(layout='tight', tight_layout={'pad': 1}, + constrained_layout=False) + assert fig.get_tight_layout() + assert not fig.get_constrained_layout() + + fig = Figure(layout='constrained', constrained_layout={'w_pad': 1}, + tight_layout=False) + assert not fig.get_tight_layout() + assert fig.get_constrained_layout() + fig = Figure(layout=None, constrained_layout=False, + tight_layout=False) + assert not fig.get_tight_layout() + assert not fig.get_constrained_layout() + def test_invalid_layouts(): + + def assert_is_tight(fig): + assert fig.get_tight_layout() + assert not fig.get_constrained_layout() + assert fig.layout == 'tight' + + def assert_is_constrained(fig): + assert not fig.get_tight_layout() + assert fig.get_constrained_layout() + assert fig.layout == 'constrained' + + def assert_neither(fig): + assert not fig.get_tight_layout() + assert not fig.get_constrained_layout() + assert fig.layout is None fig, ax = plt.subplots(constrained_layout=True) with pytest.warns(UserWarning): # this should warn, fig.subplots_adjust(top=0.8) - assert not(fig.get_constrained_layout()) - + assert_neither(fig) # Using layout + (tight|constrained)_layout warns, but the former takes # precedence. - with pytest.warns(UserWarning, match="Figure parameters 'layout' and " - "'tight_layout' cannot"): + + # check the set_layout function on figure construction: + with pytest.warns(UserWarning, match="Figure parameters 'layout'=='tight'" + " and 'tight_layout'==False cannot"): fig = Figure(layout='tight', tight_layout=False) - assert fig.get_tight_layout() - assert not fig.get_constrained_layout() - with pytest.warns(UserWarning, match="Figure parameters 'layout' and " - "'constrained_layout' cannot"): + assert_is_tight(fig) + + with pytest.warns(UserWarning, match="Figure parameters " + "'layout'=='constrained' and " + "'constrained_layout'==False cannot"): fig = Figure(layout='constrained', constrained_layout=False) - assert not fig.get_tight_layout() - assert fig.get_constrained_layout() + assert_is_constrained(fig) + + with pytest.warns(UserWarning, match="Figure parameters " + "'layout'=='tight' and " + "'constrained_layout'!=False cannot"): + fig = Figure(layout='tight', constrained_layout=True) + assert_is_tight(fig) + + with pytest.warns(UserWarning, match="Figure parameters " + "'layout'=='constrained' and " + "'tight_layout'!=False cannot"): + fig = Figure(layout='constrained', tight_layout=True) + assert_is_constrained(fig) + + with pytest.warns(UserWarning, match="Figure parameters " + "'layout'=='tight' and " + "'constrained_layout'!=False cannot"): + fig = Figure(layout='tight', tight_layout=True, + constrained_layout=True) + assert_is_tight(fig) + + with pytest.warns(UserWarning, match="Figure parameters " + "'layout'=='tight' and " + "'constrained_layout'!=False cannot"): + fig = Figure(layout='tight', tight_layout={'pad': 1}, + constrained_layout=True) + assert_is_tight(fig) + + with pytest.warns(UserWarning, match="Figure parameters " + "'layout'=='tight' and " + "'constrained_layout'!=False cannot"): + fig = Figure(layout='tight', tight_layout={'pad': 1}, + constrained_layout={'w_pad': 1}) + assert_is_tight(fig) + + with pytest.warns(UserWarning, match="Figure parameters " + "'layout'=='tight' and " + "'constrained_layout'!=False cannot"): + fig = Figure(layout='tight', tight_layout=True, + constrained_layout={'w_pad': 1}) + assert_is_tight(fig) + + with pytest.warns(UserWarning, match="Figure parameters " + "'layout'=='constrained' and " + "'tight_layout'!=False cannot"): + fig = Figure(layout='constrained', constrained_layout=True, + tight_layout=True) + assert_is_constrained(fig) + + with pytest.warns(UserWarning, match="Figure parameters " + "'layout'=='constrained' and " + "'tight_layout'!=False cannot"): + fig = Figure(layout='constrained', constrained_layout={'w_pad': 1}, + tight_layout=True) + assert_is_constrained(fig) + + with pytest.warns(UserWarning, match="Figure parameters " + "'layout'=='constrained' and " + "'tight_layout'!=False cannot"): + fig = Figure(layout='constrained', constrained_layout={'w_pad': 1}, + tight_layout={'pad': 1}) + assert_is_constrained(fig) + + with pytest.warns(UserWarning, match="Figure parameters " + "'layout'=='constrained' and " + "'tight_layout'!=False cannot"): + fig = Figure(layout='constrained', constrained_layout=True, + tight_layout={'pad': 1}) + assert_is_constrained(fig) + + with pytest.warns(Warning) as warninfo: + fig = Figure(layout='tight', + tight_layout=False, + constrained_layout=True) + warns = {(warn.category, warn.message.args[0]) for warn in warninfo} + expected = { + (UserWarning, "Figure parameters 'layout'=='tight' " + "and 'tight_layout'==False cannot be used together. " + "Please use 'layout' only."), + (UserWarning, "Figure parameters 'layout'=='tight' " + "and 'constrained_layout'!=False cannot be used together. " + "Please use 'layout' only.")} + assert_is_tight(fig) + assert warns == expected + with pytest.warns(Warning) as warninfo: + fig = Figure(layout='constrained', + tight_layout=True, + constrained_layout=False) + warns = {(warn.category, warn.message.args[0]) for warn in warninfo} + expected = { + (UserWarning, "Figure parameters 'layout'=='constrained' " + "and 'tight_layout'!=False cannot be used together. " + "Please use 'layout' only."), + (UserWarning, "Figure parameters 'layout'=='constrained' " + "and 'constrained_layout'==False cannot be used together. " + "Please use 'layout' only.")} + assert_is_constrained(fig) + assert warns == expected with pytest.raises(ValueError, match="'foobar' is not a valid value for layout"): Figure(layout='foobar') + # now check the set_layout function after figure_construction + + fig = Figure(layout='tight') + with pytest.warns(UserWarning, match="Figure parameters 'layout'=='tight' " + "and 'tight_layout'==False cannot"): + fig.set_layout(layout='tight', tight_layout=False) + assert_is_tight(fig) + + with pytest.warns(UserWarning, match="Figure parameters " + "'layout'=='constrained' and " + "'constrained_layout'==False cannot"): + fig.set_layout(layout='constrained', constrained_layout=False) + assert_is_constrained(fig) + + with pytest.warns(UserWarning, match="Figure parameters " + "'layout'=='tight' and " + "'constrained_layout'!=False cannot"): + fig.set_layout(layout='tight', constrained_layout=True) + assert_is_tight(fig) + + with pytest.warns(UserWarning, match="Figure parameters " + "'layout'=='constrained' and " + "'tight_layout'!=False cannot"): + fig.set_layout(layout='constrained', tight_layout=True) + assert_is_constrained(fig) + + with pytest.warns(UserWarning, match="Figure parameters " + "'layout'=='tight' and " + "'constrained_layout'!=False cannot"): + fig.set_layout(layout='tight', constrained_layout={'pad': 1}) + assert_is_tight(fig) + with pytest.warns(UserWarning, match="Figure parameters " + "'layout'=='constrained' and " + "'tight_layout'!=False cannot"): + fig.set_layout(layout='constrained', tight_layout={'pad': 1}) + assert_is_constrained(fig) + + with pytest.warns(Warning) as warninfo: + fig.set_layout(layout='tight', + tight_layout=False, + constrained_layout=True) + warns = {(warn.category, warn.message.args[0]) for warn in warninfo} + expected = { + (UserWarning, "Figure parameters 'layout'=='tight' " + "and 'tight_layout'==False cannot be used together. " + "Please use 'layout' only."), + (UserWarning, "Figure parameters 'layout'=='tight' " + "and 'constrained_layout'!=False cannot be used together. " + "Please use 'layout' only.")} + assert_is_tight(fig) + assert warns == expected + with pytest.warns(Warning) as warninfo: + fig.set_layout(layout='constrained', + tight_layout=True, + constrained_layout=False) + warns = {(warn.category, warn.message.args[0]) for warn in warninfo} + expected = { + (UserWarning, "Figure parameters 'layout'=='constrained' " + "and 'tight_layout'!=False cannot be used together. " + "Please use 'layout' only."), + (UserWarning, "Figure parameters 'layout'=='constrained' " + "and 'constrained_layout'==False cannot be used together. " + "Please use 'layout' only.")} + assert_is_constrained(fig) + assert warns == expected + + with pytest.raises(ValueError, + match="Cannot set 'tight_layout' and " + "'constrained_layout' simultaneously."): + fig = Figure(tight_layout={'w': 1}, constrained_layout={'w_pad': 1}) + with pytest.raises(ValueError, + match="Cannot set 'tight_layout' and " + "'constrained_layout' simultaneously."): + fig = Figure(tight_layout=True, constrained_layout={'w_pad': 1}) + with pytest.raises(ValueError, + match="Cannot set 'tight_layout' and " + "'constrained_layout' simultaneously."): + fig = Figure(tight_layout=True, constrained_layout=True) + + +def test_set_subplotpars(): + subplotparams_keys = ["left", "bottom", "right", "top", "wspace", "hspace"] + fig = plt.figure() + subplotparams = fig.get_subplotpars() + test_dict = {} + default_dict = {} + for key in subplotparams_keys: + attr = getattr(subplotparams, key) + assert attr == mpl.rcParams[f"figure.subplot.{key}"] + default_dict[key] = attr + test_dict[key] = attr * 2 + + subplotparams.update(left=test_dict['left']) + assert fig.get_subplotpars().left == test_dict['left'] + + fig.subplots_adjust(**default_dict) + assert fig.get_subplotpars().left == default_dict['left'] + + fig.set_subplotpars(test_dict) + for key, value in test_dict.items(): + assert getattr(fig.get_subplotpars(), key) == value + + test_subplotparams = SubplotParams() + fig.set_subplotpars(test_subplotparams) + for key, value in default_dict.items(): + assert getattr(fig.get_subplotpars(), key) == value + + fig.set_subplotpars(test_dict) + for key, value in test_dict.items(): + assert getattr(fig.get_subplotpars(), key) == value + + test_dict['foo'] = 'bar' + with pytest.warns(UserWarning, + match="'foo' is not a valid key for set_subplotpars;" + " this key was ignored"): + fig.set_subplotpars(test_dict) + + with pytest.raises(TypeError, + match="subplotpars must be a dictionary of " + "keyword-argument pairs or " + "an instance of SubplotParams()"): + fig.set_subplotpars(['foo']) + + fig.set_subplotpars({}) + with pytest.raises(AttributeError): # test_dict['foo'] = 'bar' + # but fig.get_subplotpars().foo should be invalid + for key, value in test_dict.items(): + assert getattr(fig.get_subplotpars(), key) == value @check_figures_equal(extensions=["png", "pdf"]) @@ -995,7 +1287,7 @@ def test_subfigure_tightbbox(): sub = fig.subfigures(1, 2) np.testing.assert_allclose( - fig.get_tightbbox(fig.canvas.get_renderer()).width, 0.1) + fig.get_tightbbox(fig.canvas.get_renderer()).width, 0.1) @image_comparison(['test_subfigure_ss.png'], style='mpl20', @@ -1078,11 +1370,11 @@ def test_subfigure_spanning(): w = 640 h = 480 - np.testing.assert_allclose(sub_figs[0].bbox.min, [0., h * 2/3]) + np.testing.assert_allclose(sub_figs[0].bbox.min, [0., h * 2 / 3]) np.testing.assert_allclose(sub_figs[0].bbox.max, [w / 3, h]) np.testing.assert_allclose(sub_figs[1].bbox.min, [w / 3, h / 3]) - np.testing.assert_allclose(sub_figs[1].bbox.max, [w * 2/3, h]) + np.testing.assert_allclose(sub_figs[1].bbox.max, [w * 2 / 3, h]) np.testing.assert_allclose(sub_figs[2].bbox.min, [w / 3, 0]) np.testing.assert_allclose(sub_figs[2].bbox.max, [w, h / 3]) @@ -1123,7 +1415,7 @@ def test_subfigure_ticks(): @image_comparison(['test_subfigure_scatter_size.png'], style='mpl20', - remove_text=True) + remove_text=True) def test_subfigure_scatter_size(): # markers in the left- and right-most subplots should be the same fig = plt.figure() @@ -1226,3 +1518,13 @@ def test_kwargs_pass(): assert fig.get_label() == 'whole Figure' assert sub_fig.get_label() == 'sub figure' + + +def test_fig_get_set(): + varnames = filter(lambda var: var not in ['self', 'kwargs', 'args'], + Figure.__init__.__code__.co_varnames) + fig = plt.figure() + for var in varnames: + # if getattr fails then the getter and setter does not exist + getfunc = getattr(fig, f"get_{var}") + setfunc = getattr(fig, f"set_{var}")