From 9e5b1d6589043c8f530ce64c67274d314b742f22 Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Thu, 22 Apr 2021 23:46:13 +0200 Subject: [PATCH] Add labels parameter to set_ticks() --- doc/users/next_whats_new/set_ticks_labels.rst | 16 +++++ lib/matplotlib/axes/_secondary_axes.py | 16 ++--- lib/matplotlib/axis.py | 67 ++++++++++++------- lib/matplotlib/tests/test_axes.py | 22 ++++++ 4 files changed, 85 insertions(+), 36 deletions(-) create mode 100644 doc/users/next_whats_new/set_ticks_labels.rst diff --git a/doc/users/next_whats_new/set_ticks_labels.rst b/doc/users/next_whats_new/set_ticks_labels.rst new file mode 100644 index 000000000000..7cb6692d5260 --- /dev/null +++ b/doc/users/next_whats_new/set_ticks_labels.rst @@ -0,0 +1,16 @@ +Settings tick positions and labels simultaneously in ``set_ticks`` +------------------------------------------------------------------ +`.Axis.set_ticks` (and the corresponding `.Axes.set_xticks` / +`.Axes.set_yticks`) got a new parameter *labels* allowing to set tick positions +and labels simultaneously. + +Previously, setting tick labels was done using `.Axis.set_ticklabels` (or +the corresponding `.Axes.set_xticklabels` / `.Axes.set_yticklabels`). This +usually only makes sense if you previously fix the position with +`~.Axis.set_ticks`. Both functionality is now available in `~.Axis.set_ticks`. +The use of `.Axis.set_ticklabels` is discouraged, but it will stay available +for backward compatibility. + +Note: This addition makes the API of `~.Axis.set_ticks` also more similar to +`.pyplot.xticks` / `.pyplot.yticks`, which already had the additional *labels* +parameter. diff --git a/lib/matplotlib/axes/_secondary_axes.py b/lib/matplotlib/axes/_secondary_axes.py index 98a612013a16..874a36c9d60f 100644 --- a/lib/matplotlib/axes/_secondary_axes.py +++ b/lib/matplotlib/axes/_secondary_axes.py @@ -4,6 +4,7 @@ import matplotlib.docstring as docstring import matplotlib.ticker as mticker from matplotlib.axes._base import _AxesBase, _TransformedBoundsLocator +from matplotlib.axis import Axis class SecondaryAxis(_AxesBase): @@ -123,18 +124,9 @@ def apply_aspect(self, position=None): self._set_lims() super().apply_aspect(position) - def set_ticks(self, ticks, *, minor=False): - """ - Set the x ticks with list of *ticks* - - Parameters - ---------- - ticks : list - List of x-axis tick locations. - minor : bool, default: False - If ``False`` sets major ticks, if ``True`` sets minor ticks. - """ - ret = self._axis.set_ticks(ticks, minor=minor) + @docstring.copy(Axis.set_ticks) + def set_ticks(self, ticks, labels=None, *, minor=False, **kwargs): + ret = self._axis.set_ticks(ticks, labels, minor=minor, **kwargs) self.stale = True self._ticks_set = True return ret diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 39fa5d40fb97..9a7dffe1bf56 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -1661,10 +1661,16 @@ def set_ticklabels(self, ticklabels, *, minor=False, **kwargs): r""" Set the text values of the tick labels. - .. warning:: - This method should only be used after fixing the tick positions - using `.Axis.set_ticks`. Otherwise, the labels may end up in - unexpected positions. + .. admonition:: Discouraged + + The use of this method is discouraged, because of the dependency + on tick positions. In most cases, you'll want to use + ``set_[x/y]ticks(positions, labels)`` instead. + + If you are using this method, you should always fix the tick + positions before, e.g. by using `.Axis.set_ticks` or by explicitly + setting a `~.ticker.FixedLocator`. Otherwise, ticks are free to + move and the labels may end up in unexpected positions. Parameters ---------- @@ -1772,27 +1778,9 @@ def _set_ticklabels(self, labels, fontdict=None, minor=False, **kwargs): kwargs.update(fontdict) return self.set_ticklabels(labels, minor=minor, **kwargs) - def set_ticks(self, ticks, *, minor=False): - """ - Set this Axis' tick locations. - - If necessary, the view limits of the Axis are expanded so that all - given ticks are visible. + def _set_tick_locations(self, ticks, *, minor=False): + # see docstring of set_ticks - Parameters - ---------- - ticks : list of floats - List of tick locations. - minor : bool, default: False - If ``False``, set the major ticks; if ``True``, the minor ticks. - - Notes - ----- - The mandatory expansion of the view limits is an intentional design - choice to prevent the surprise of a non-visible tick. If you need - other limits, you should set the limits explicitly after setting the - ticks. - """ # XXX if the user changes units, the information will be lost here ticks = self.convert_units(ticks) if self is self.axes.xaxis: @@ -1827,6 +1815,37 @@ def set_ticks(self, ticks, *, minor=False): self.set_major_locator(mticker.FixedLocator(ticks)) return self.get_major_ticks(len(ticks)) + def set_ticks(self, ticks, labels=None, *, minor=False, **kwargs): + """ + Set this Axis' tick locations and optionally labels. + + If necessary, the view limits of the Axis are expanded so that all + given ticks are visible. + + Parameters + ---------- + ticks : list of floats + List of tick locations. + labels : list of str, optional + List of tick labels. If not set, the labels show the data value. + minor : bool, default: False + If ``False``, set the major ticks; if ``True``, the minor ticks. + **kwargs + `.Text` properties for the labels. These take effect only if you + pass *labels*. In other cases, please use `~.Axes.tick_params`. + + Notes + ----- + The mandatory expansion of the view limits is an intentional design + choice to prevent the surprise of a non-visible tick. If you need + other limits, you should set the limits explicitly after setting the + ticks. + """ + result = self._set_tick_locations(ticks, minor=minor) + if labels is not None: + self.set_ticklabels(labels, minor=minor, **kwargs) + return result + def _get_tick_boxes_siblings(self, renderer): """ Get the bounding boxes for this `.axis` and its siblings diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index b8685ba790d0..ee28041a1f4e 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -5107,6 +5107,28 @@ def test_set_get_ticklabels(): ax[1].set_yticklabels(ax[0].get_yticklabels()) +@check_figures_equal(extensions=["png"]) +def test_set_ticks_with_labels(fig_test, fig_ref): + """ + Test that these two are identical:: + + set_xticks(ticks); set_xticklabels(labels, **kwargs) + set_xticks(ticks, labels, **kwargs) + + """ + ax = fig_ref.subplots() + ax.set_xticks([1, 2, 4, 6]) + ax.set_xticklabels(['a', 'b', 'c', 'd'], fontweight='bold') + ax.set_yticks([1, 3, 5]) + ax.set_yticks([2, 4], minor=True) + ax.set_yticklabels(['A', 'B'], minor=True) + + ax = fig_test.subplots() + ax.set_xticks([1, 2, 4, 6], ['a', 'b', 'c', 'd'], fontweight='bold') + ax.set_yticks([1, 3, 5]) + ax.set_yticks([2, 4], ['A', 'B'], minor=True) + + def test_subsampled_ticklabels(): # test issue 11937 fig, ax = plt.subplots()