From 4b5284f79a7dbff456cd1ba9a8485aef2d00f9bb Mon Sep 17 00:00:00 2001 From: Tim Hoffmann <2836374+timhoffm@users.noreply.github.com> Date: Fri, 25 Oct 2019 00:27:21 +0200 Subject: [PATCH] Use FixedFormatter only with FixedLocator --- lib/matplotlib/axes/_base.py | 30 ++++++++++++++++++++--------- lib/matplotlib/axis.py | 29 ++++++++++++++++------------ lib/matplotlib/tests/test_axes.py | 8 ++++++-- lib/matplotlib/tests/test_figure.py | 7 ++++++- lib/matplotlib/ticker.py | 5 +++++ 5 files changed, 55 insertions(+), 24 deletions(-) diff --git a/lib/matplotlib/axes/_base.py b/lib/matplotlib/axes/_base.py index dc15642c9216..45b7182af5de 100644 --- a/lib/matplotlib/axes/_base.py +++ b/lib/matplotlib/axes/_base.py @@ -3412,10 +3412,15 @@ def set_xticklabels(self, labels, fontdict=None, minor=False, **kwargs): """ Set the x-tick labels with list of string labels. + .. warning:: + This method should only be used after fixing the tick positions + using `~.axes.Axes.set_xticks`. Otherwise, the labels may end up + in unexpected positions. + Parameters ---------- - labels : List[str] - List of string labels. + labels : list of str + The label texts. fontdict : dict, optional A dictionary controlling the appearance of the ticklabels. @@ -3426,12 +3431,13 @@ def set_xticklabels(self, labels, fontdict=None, minor=False, **kwargs): 'verticalalignment': 'baseline', 'horizontalalignment': loc} - minor : bool, optional + minor : bool, default: False Whether to set the minor ticklabels rather than the major ones. Returns ------- - A list of `~.text.Text` instances. + labels : list of `~.Text` + The labels. Other Parameters ----------------- @@ -3792,12 +3798,17 @@ def get_yticklabels(self, minor=False, which=None): def set_yticklabels(self, labels, fontdict=None, minor=False, **kwargs): """ - Set the y-tick labels with list of strings labels. + Set the y-tick labels with list of string labels. + + .. warning:: + This method should only be used after fixing the tick positions + using `~.axes.Axes.set_yticks`. Otherwise, the labels may end up + in unexpected positions. Parameters ---------- - labels : List[str] - list of string labels + labels : list of str + The label texts. fontdict : dict, optional A dictionary controlling the appearance of the ticklabels. @@ -3808,12 +3819,13 @@ def set_yticklabels(self, labels, fontdict=None, minor=False, **kwargs): 'verticalalignment': 'baseline', 'horizontalalignment': loc} - minor : bool, optional + minor : bool, default: False Whether to set the minor ticklabels rather than the major ones. Returns ------- - A list of `~.text.Text` instances. + labels + A list of `~.text.Text` instances. Other Parameters ---------------- diff --git a/lib/matplotlib/axis.py b/lib/matplotlib/axis.py index 2101d802264c..13363f7de362 100644 --- a/lib/matplotlib/axis.py +++ b/lib/matplotlib/axis.py @@ -1632,6 +1632,11 @@ def set_major_formatter(self, formatter): formatter : `~matplotlib.ticker.Formatter` """ cbook._check_isinstance(mticker.Formatter, formatter=formatter) + if (isinstance(formatter, mticker.FixedFormatter) + and len(formatter.seq) > 0 + and not isinstance(self.major.locator, mticker.FixedLocator)): + cbook._warn_external('FixedFormatter should only be used together ' + 'with FixedLocator') self.isDefault_majfmt = False self.major.formatter = formatter formatter.set_axis(self) @@ -1646,6 +1651,11 @@ def set_minor_formatter(self, formatter): formatter : `~matplotlib.ticker.Formatter` """ cbook._check_isinstance(mticker.Formatter, formatter=formatter) + if (isinstance(formatter, mticker.FixedFormatter) + and len(formatter.seq) > 0 + and not isinstance(self.minor.locator, mticker.FixedLocator)): + cbook._warn_external('FixedFormatter should only be used together ' + 'with FixedLocator') self.isDefault_minfmt = False self.minor.formatter = formatter formatter.set_axis(self) @@ -1697,6 +1707,11 @@ def set_ticklabels(self, ticklabels, *args, 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. + Parameters ---------- ticklabels : sequence of str or of `Text`\s @@ -1718,18 +1733,8 @@ def set_ticklabels(self, ticklabels, *args, minor=False, **kwargs): "3.1", message="Additional positional arguments to " "set_ticklabels are ignored, and deprecated since Matplotlib " "3.1; passing them will raise a TypeError in Matplotlib 3.3.") - get_labels = [] - for t in ticklabels: - # try calling get_text() to check whether it is Text object - # if it is Text, get label content - try: - get_labels.append(t.get_text()) - # otherwise add the label to the list directly - except AttributeError: - get_labels.append(t) - # replace the ticklabels list with the processed one - ticklabels = get_labels - + ticklabels = [t.get_text() if hasattr(t, 'get_text') else t + for t in ticklabels] if minor: self.set_minor_formatter(mticker.FixedFormatter(ticklabels)) ticks = self.get_minor_ticks() diff --git a/lib/matplotlib/tests/test_axes.py b/lib/matplotlib/tests/test_axes.py index 16f495c04375..377b109f1ec8 100644 --- a/lib/matplotlib/tests/test_axes.py +++ b/lib/matplotlib/tests/test_axes.py @@ -5064,11 +5064,15 @@ def test_set_get_ticklabels(): ax[1].set_title(ha[1]) # set ticklabel to 1 plot in normal way - ax[0].set_xticklabels(('a', 'b', 'c', 'd')) - ax[0].set_yticklabels(('11', '12', '13', '14')) + ax[0].set_xticks(range(10)) + ax[0].set_yticks(range(10)) + ax[0].set_xticklabels(['a', 'b', 'c', 'd']) + ax[0].set_yticklabels(['11', '12', '13', '14']) # set ticklabel to the other plot, expect the 2 plots have same label # setting pass get_ticklabels return value as ticklabels argument + ax[1].set_xticks(ax[0].get_xticks()) + ax[1].set_yticks(ax[0].get_yticks()) ax[1].set_xticklabels(ax[0].get_xticklabels()) ax[1].set_yticklabels(ax[0].get_yticklabels()) diff --git a/lib/matplotlib/tests/test_figure.py b/lib/matplotlib/tests/test_figure.py index 52befc23f14a..665a23bc2a6e 100644 --- a/lib/matplotlib/tests/test_figure.py +++ b/lib/matplotlib/tests/test_figure.py @@ -1,6 +1,7 @@ from datetime import datetime from pathlib import Path import platform +import warnings from matplotlib import rcParams from matplotlib.testing.decorators import image_comparison, check_figures_equal @@ -317,7 +318,11 @@ def test_autofmt_xdate(which): ax.xaxis_date() ax.xaxis.set_minor_locator(AutoMinorLocator(2)) - ax.xaxis.set_minor_formatter(FixedFormatter(minors)) + with warnings.catch_warnings(): + warnings.filterwarnings( + 'ignore', + 'FixedFormatter should only be used together with FixedLocator') + ax.xaxis.set_minor_formatter(FixedFormatter(minors)) fig.autofmt_xdate(0.2, angle, 'right', which) diff --git a/lib/matplotlib/ticker.py b/lib/matplotlib/ticker.py index 5551542a2cb4..f50305301ab8 100644 --- a/lib/matplotlib/ticker.py +++ b/lib/matplotlib/ticker.py @@ -345,6 +345,11 @@ def __call__(self, x, pos=None): class FixedFormatter(Formatter): """ Return fixed strings for tick labels based only on position, not value. + + .. note:: + `.FixedFormatter` should only be used together with `.FixedLocator`. + Otherwise, the labels may end up in unexpected positions. + """ def __init__(self, seq): """