diff --git a/doc/users/whats_new/style_changes.rst b/doc/users/whats_new/style_changes.rst index e5a9a11e110a..8bf16062abfb 100644 --- a/doc/users/whats_new/style_changes.rst +++ b/doc/users/whats_new/style_changes.rst @@ -72,6 +72,11 @@ Plot layout - Ticks now point outward by default. To have ticks pointing inward, use the ``rcParams`` ``xtick.direction`` and ``ytick.direction``. +- Ticks and grids are now plotted above solid elements such as + filled contours, but below lines. To return to the previous + behavior of plotting ticks and grids above lines, set + ``rcParams['axes.axisbelow'] = False``. + - By default, caps on the ends of errorbars are not present. Use the rcParam ``errorbar.capsize`` to control this. diff --git a/examples/api/custom_scale_example.py b/examples/api/custom_scale_example.py index df8fe46d2737..7a25c415860c 100644 --- a/examples/api/custom_scale_example.py +++ b/examples/api/custom_scale_example.py @@ -5,6 +5,11 @@ from matplotlib import scale as mscale from matplotlib import transforms as mtransforms from matplotlib.ticker import Formatter, FixedLocator +from matplotlib import rcParams + + +# BUG: this example fails with any other setting of axisbelow +rcParams['axes.axisbelow'] = False class MercatorLatitudeScale(mscale.ScaleBase): diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index 5d14e3db1d04..f77690d970f4 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -36,6 +36,7 @@ from matplotlib.artist import allow_rasterization from matplotlib.rcsetup import cycler +from matplotlib.rcsetup import validate_axisbelow rcParams = matplotlib.rcParams @@ -441,8 +442,9 @@ def __init__(self, fig, rect, *aspect* [ 'auto' | 'equal' | aspect_ratio ] *autoscale_on* [ *True* | *False* ] whether or not to autoscale the *viewlim* - *axisbelow* draw the grids and ticks below the other - artists + *axisbelow* [ *True* | *False* | 'line'] draw the grids + and ticks below or above most other artists, + or below lines but above patches *cursor_props* a (*float*, *color*) tuple *figure* a :class:`~matplotlib.figure.Figure` instance @@ -2308,12 +2310,16 @@ def draw(self, renderer=None, inframe=False): artists.remove(spine) if self.axison and not inframe: - if self._axisbelow: + if self._axisbelow is True: self.xaxis.set_zorder(0.5) self.yaxis.set_zorder(0.5) - else: + elif self._axisbelow is False: self.xaxis.set_zorder(2.5) self.yaxis.set_zorder(2.5) + else: + # 'line': above patches, below lines + self.xaxis.set_zorder(1.5) + self.yaxis.set_zorder(1.5) else: for _axis in self._get_axis_list(): artists.remove(_axis) @@ -2413,9 +2419,9 @@ def set_axisbelow(self, b): Set whether the axis ticks and gridlines are above or below most artists - ACCEPTS: [ *True* | *False* ] + ACCEPTS: [ *True* | *False* | 'line' ] """ - self._axisbelow = b + self._axisbelow = validate_axisbelow(b) self.stale = True @docstring.dedent_interpd diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index e33a015a66d1..8c968a3ff407 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -175,6 +175,17 @@ def validate_string_or_None(s): except ValueError: raise ValueError('Could not convert "%s" to string' % s) +def validate_axisbelow(s): + try: + return validate_bool(s) + except ValueError: + if isinstance(s, six.string_types): + s = s.lower() + if s.startswith('line'): + return 'line' + raise ValueError('%s cannot be interpreted as' + ' True, False, or "line"' % s) + def validate_dpi(s): """confirm s is string 'figure' or convert s to float or raise""" @@ -1003,7 +1014,7 @@ def validate_animation_writer_path(p): 'errorbar.capsize': [0, validate_float], # axes props - 'axes.axisbelow': [False, validate_bool], + 'axes.axisbelow': ['line', validate_axisbelow], 'axes.hold': [True, validate_bool], 'axes.facecolor': ['w', validate_color], # background color; white 'axes.edgecolor': ['k', validate_color], # edge color; black diff --git a/lib/matplotlib/tests/baseline_images/test_artist/default_edges.png b/lib/matplotlib/tests/baseline_images/test_artist/default_edges.png index 7bc95ec5d3ff..5d86a12d9bcb 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_artist/default_edges.png and b/lib/matplotlib/tests/baseline_images/test_artist/default_edges.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/auto_numticks.png b/lib/matplotlib/tests/baseline_images/test_axes/auto_numticks.png index f86e21e1cfc1..c1e13657d81c 100644 Binary files a/lib/matplotlib/tests/baseline_images/test_axes/auto_numticks.png and b/lib/matplotlib/tests/baseline_images/test_axes/auto_numticks.png differ diff --git a/lib/matplotlib/tests/baseline_images/test_axes/axisbelow.png b/lib/matplotlib/tests/baseline_images/test_axes/axisbelow.png new file mode 100644 index 000000000000..d95e259e05c3 Binary files /dev/null and b/lib/matplotlib/tests/baseline_images/test_axes/axisbelow.png differ diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 45c2bb679db2..f1f0dfe8c007 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -25,6 +25,7 @@ from matplotlib.testing.noseclasses import KnownFailureTest import matplotlib.pyplot as plt import matplotlib.markers as mmarkers +import matplotlib.patches as mpatches from numpy.testing import assert_allclose, assert_array_equal import warnings from matplotlib.cbook import IgnoredKeywordWarning @@ -4417,6 +4418,27 @@ def test_date_timezone_x_and_y(): plt.plot_date(time_index, time_index, tz='US/Eastern', ydate=True) +@image_comparison(baseline_images=['axisbelow'], + extensions=['png'], remove_text=True) +def test_axisbelow(): + # Test 'line' setting added in 6287. + # Show only grids, not frame or ticks, to make this test + # independent of future change to drawing order of those elements. + fig, axs = plt.subplots(ncols=3, sharex=True, sharey=True) + settings = (False, 'line', True) + + for ax, setting in zip(axs, settings): + ax.plot((0, 10), (0, 10), lw=10, color='m') + circ = mpatches.Circle((3, 3), color='r') + ax.add_patch(circ) + ax.grid(color='c', linestyle='-', linewidth=3) + ax.tick_params(top=False, bottom=False, + left=False, right=False) + for spine in ax.spines.values(): + spine.set_visible(False) + ax.set_axisbelow(setting) + + if __name__ == '__main__': import nose import sys diff --git a/lib/mpl_toolkits/tests/baseline_images/test_axes_grid1/inset_locator.png b/lib/mpl_toolkits/tests/baseline_images/test_axes_grid1/inset_locator.png index d8ee37675416..c4b62f28c811 100644 Binary files a/lib/mpl_toolkits/tests/baseline_images/test_axes_grid1/inset_locator.png and b/lib/mpl_toolkits/tests/baseline_images/test_axes_grid1/inset_locator.png differ diff --git a/matplotlibrc.template b/matplotlibrc.template index f7aa486f0914..33a3c5b5eecd 100644 --- a/matplotlibrc.template +++ b/matplotlibrc.template @@ -301,8 +301,9 @@ backend : $TEMPLATE_BACKEND #axes.labelpad : 5.0 # space between label and axis #axes.labelweight : normal # weight of the x and y labels #axes.labelcolor : black -#axes.axisbelow : False # whether axis gridlines and ticks are below - # the axes elements (lines, text, etc) +#axes.axisbelow : 'line' # draw axis gridlines and ticks below + # patches (True); above patches but below + # lines ('line'); or above all (False) #axes.formatter.limits : -7, 7 # use scientific notation if log10 # of the axis range is smaller than the