diff --git a/lib/matplotlib/rcsetup.py b/lib/matplotlib/rcsetup.py index ad807d72a89f..3a188feaad38 100644 --- a/lib/matplotlib/rcsetup.py +++ b/lib/matplotlib/rcsetup.py @@ -775,6 +775,21 @@ def validate_hatch(s): validate_dashlist = _listify_validator(validate_floatlist) +def validate_minor_tick_ndivs(n): + """ + Validate the given ndiv parameter for minor ticks. + ndiv can be either the string 'auto' or a non-negative integer. + """ + if isinstance(n, int): + if (0 <= n): + return n + else: + raise RuntimeError(f'Value must be >=0; got {n}') + elif (n == 'auto'): + return n + else: + raise ValueError("Value must be a non-negative int or 'auto'") + _prop_validators = { 'color': _listify_validator(validate_color_for_prop_cycle, allow_stringlist=True), @@ -1287,6 +1302,8 @@ def _convert_validator_spec(key, conv): "xtick.minor.bottom": validate_bool, # draw bottom minor xticks "xtick.major.top": validate_bool, # draw top major xticks "xtick.major.bottom": validate_bool, # draw bottom major xticks + "xtick.minor.ndivs": ['auto', validate_minor_tick_ndivs], + # default number of minor ticks to display between each pair of major ticks "xtick.labelsize": validate_fontsize, # fontsize of xtick labels "xtick.direction": validate_string, # direction of xticks "xtick.alignment": ["center", "right", "left"], @@ -1310,6 +1327,8 @@ def _convert_validator_spec(key, conv): "ytick.major.left": validate_bool, # draw left major yticks "ytick.major.right": validate_bool, # draw right major yticks "ytick.labelsize": validate_fontsize, # fontsize of ytick labels + "ytick.minor.ndivs": ['auto', validate_minor_tick_ndivs], + # default number of minor ticks to display between each pair of major ticks "ytick.direction": validate_string, # direction of yticks "ytick.alignment": [ "center", "top", "bottom", "baseline", "center_baseline"], diff --git a/lib/matplotlib/tests/test_ticker.py b/lib/matplotlib/tests/test_ticker.py index 9c75678e14d3..84cf3821945c 100644 --- a/lib/matplotlib/tests/test_ticker.py +++ b/lib/matplotlib/tests/test_ticker.py @@ -191,6 +191,70 @@ def test_additional(self, lim, ref): assert_almost_equal(ax.yaxis.get_ticklocs(minor=True), ref) + def test_ndivs_rcParams_auto(self): + # when no parameter is given rcParams should be used + # auto + mpl.rc_context(rc={'xtick.minor.ndivs': 'auto'}) + mpl.rc_context(rc={'ytick.minor.ndivs': 'auto'}) + fig, ax = plt.subplots() + ax.set_xlim(0, 1.39) + ax.set_ylim(0, 1.39) + ax.xaxis.set_minor_locator(mticker.AutoMinorLocator()) + ax.yaxis.set_minor_locator(mticker.AutoMinorLocator()) + + test_value = np.array([0.05, 0.1, 0.15, 0.25, 0.3, 0.35, 0.45, + 0.5, 0.55, 0.65, 0.7, 0.75, 0.85, 0.9, + 0.95, 1.05, 1.1, 1.15, 1.25, 1.3, 1.35]) + assert_almost_equal(ax.xaxis.get_ticklocs(minor=True), test_value) + assert_almost_equal(ax.yaxis.get_ticklocs(minor=True), test_value) + + def test_ndivs_rcParams_int(self): + # when no parameter is given rcParams should be used + # non-neg int + mpl.rc_context(rc={'xtick.minor.ndivs': 2}) + mpl.rc_context(rc={'ytick.minor.ndivs': 2}) + fig, ax = plt.subplots() + ax.set_xlim(0, 4) + ax.xaxis.set_major_locator(mticker.MultipleLocator(1)) + ax.xaxis.set_minor_locator(mticker.AutoMinorLocator()) + + ax.set_ylim(0, 4) + ax.yaxis.set_major_locator(mticker.MultipleLocator(1)) + ax.yaxis.set_minor_locator(mticker.AutoMinorLocator()) + + test_value = np.array([0.5, 1.5, 2.5, 3.5]) + assert_almost_equal(ax.xaxis.get_ticklocs(minor=True), test_value) + assert_almost_equal(ax.yaxis.get_ticklocs(minor=True), test_value) + + def test_ndivs_auto(self): + # when auto is given set ndivs to either 4 or 5 + fig, ax = plt.subplots() + ax.set_xlim(0, 1.39) + ax.set_ylim(0, 1.39) + ax.xaxis.set_minor_locator(mticker.AutoMinorLocator('auto')) + ax.yaxis.set_minor_locator(mticker.AutoMinorLocator('auto')) + + test_value = np.array([0.05, 0.1, 0.15, 0.25, 0.3, 0.35, 0.45, + 0.5, 0.55, 0.65, 0.7, 0.75, 0.85, 0.9, + 0.95, 1.05, 1.1, 1.15, 1.25, 1.3, 1.35]) + assert_almost_equal(ax.xaxis.get_ticklocs(minor=True), test_value) + assert_almost_equal(ax.yaxis.get_ticklocs(minor=True), test_value) + + def test_ndivs_int(self): + # when an integer is given, use that integer + fig, ax = plt.subplots() + ax.set_xlim(0, 4) + ax.xaxis.set_major_locator(mticker.MultipleLocator(1)) + ax.xaxis.set_minor_locator(mticker.AutoMinorLocator(2)) + + ax.set_ylim(0, 4) + ax.yaxis.set_major_locator(mticker.MultipleLocator(1)) + ax.yaxis.set_minor_locator(mticker.AutoMinorLocator(2)) + + test_value = np.array([0.5, 1.5, 2.5, 3.5]) + assert_almost_equal(ax.xaxis.get_ticklocs(minor=True), test_value) + assert_almost_equal(ax.yaxis.get_ticklocs(minor=True), test_value) + class TestLogLocator: def test_basic(self): diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index f3d97d95093c..35dd846dc979 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -2911,7 +2911,8 @@ def __init__(self, n=None): major ticks; e.g., n=2 will place a single minor tick midway between major ticks. - If *n* is omitted or None, it will be set to 5 or 4. + If *n* is omitted or None, the value stored in rcParams will be used. + If *n* is 'auto', it will be set to 5 or 4. """ self.ndivs = n @@ -2933,6 +2934,12 @@ def __call__(self): return [] if self.ndivs is None: + if self.axis.__name__ == 'xaxis': + self.ndivs = mpl.rcParams['xtick.minor.ndivs'] + else: + self.ndivs = mpl.rcParams['ytick.minor.ndivs'] + + if self.ndivs == 'auto': majorstep_no_exponent = 10 ** (np.log10(majorstep) % 1) diff --git a/matplotlibrc.template b/matplotlibrc.template index 87c7927ad3f8..2e3ba3dd34d1 100644 --- a/matplotlibrc.template +++ b/matplotlibrc.template @@ -477,6 +477,7 @@ #xtick.minor.top: True # draw x axis top minor ticks #xtick.minor.bottom: True # draw x axis bottom minor ticks #xtick.alignment: center # alignment of xticks +#xtick.minor.ndivs: auto # default number of minor ticks to display between each pair of major ticks #ytick.left: True # draw ticks on the left side #ytick.right: False # draw ticks on the right side @@ -498,6 +499,7 @@ #ytick.minor.left: True # draw y axis left minor ticks #ytick.minor.right: True # draw y axis right minor ticks #ytick.alignment: center_baseline # alignment of yticks +#ytick.minor.ndivs: auto # default number of minor ticks to display between each pair of major ticks ## ***************************************************************************