8000 Improve consistency in LogLocator and LogFormatter API by oscargus · Pull Request #23014 · matplotlib/matplotlib · GitHub
[go: up one dir, main page]

Skip to content

Improve consistency in LogLocator and LogFormatter API #23014

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 21, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions doc/api/next_api_changes/deprecations/23014-OG.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
Methods to set parameters in ``LogLocator`` and ``LogFormatter*``
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

In `~.LogFormatter` and derived subclasses, the methods ``base`` and
``label_minor`` for setting the respective parameter are deprecated and
replaced by ``set_base`` and ``set_label_minor``, respectively.

In `~.LogLocator`, the methods ``base`` and ``subs`` for setting the
respective parameter are deprecated. Instead, use
``set_params(base=..., subs=...)``.
40 changes: 39 additions & 1 deletion lib/matplotlib/tests/test_ticker.py
Original file line number Diff line number Diff line change
Expand Up @@ -585,6 +585,8 @@ class TestScalarFormatter:

use_offset_data = [True, False]

useMathText_data = [True, False]

# (sci_type, scilimits, lim, orderOfMag, fewticks)
scilimits_data = [
(False, (0, 0), (10.0, 20.0), 0, False),
Expand Down Expand Up @@ -643,6 +645,19 @@ def test_use_offset(self, use_offset):
with mpl.rc_context({'axes.formatter.useoffset': use_offset}):
tmp_form = mticker.ScalarFormatter()
assert use_offset == tmp_form.get_useOffset()
assert tmp_form.offset == 0

@pytest.mark.parametrize('use_math_text', useMathText_data)
def test_useMathText(self, use_math_text):
with mpl.rc_context({'axes.formatter.use_mathtext': use_math_text}):
tmp_form = mticker.ScalarFormatter()
assert use_math_text == tmp_form.get_useMathText()

def test_set_use_offset_float(self):
tmp_form = mticker.ScalarFormatter()
tmp_form.set_useOffset(0.5)
assert not tmp_form.get_useOffset()
assert tmp_form.offset == 0.5

def test_use_locale(self):
conv = locale.localeconv()
Expand Down Expand Up @@ -695,6 +710,8 @@ def test_cursor_dummy_axis(self, data, expected):
sf.axis.set_view_interval(0, 10)
fmt = sf.format_data_short
assert fmt(data) == expected
assert sf.axis.get_tick_space() == 9
8000 assert sf.axis.get_minpos() == 0

def test_mathtext_ticks(self):
mpl.rcParams.update({
Expand All @@ -708,6 +725,11 @@ def test_mathtext_ticks(self):
ax.set_xticks([-1, 0, 1])
fig.canvas.draw()

def test_empty_locs(self):
sf = mticker.ScalarFormatter()
sf.set_locs([])
assert sf(0.5) == ''


class FakeAxis:
"""Allow Formatter to be called without having a "full" plot set up."""
Expand Down Expand Up @@ -1485,7 +1507,7 @@ def test_remove_overlap(remove_overlapping_locs, expected_num):
def test_bad_locator_subs(sub):
ll = mticker.LogLocator()
with pytest.raises(ValueError):
ll.subs(sub)
ll.set_params(subs=sub)


@pytest.mark.parametrize('numticks', [1, 2, 3, 9])
Expand All @@ -1496,3 +1518,19 @@ def test_small_range_loglocator(numticks):
for top in [5, 7, 9, 11, 15, 50, 100, 1000]:
ticks = ll.tick_values(.5, top)
assert (np.diff(np.log10(ll.tick_values(6, 150))) == 1).all()


def test_NullFormatter():
formatter = mticker.NullFormatter()
assert formatter(1.0) == ''
assert formatter.format_data(1.0) == ''
assert formatter.format_data_short(1.0) == ''


@pytest.mark.parametrize('formatter', (
mticker.FuncFormatter(lambda a: f'val: {a}'),
mticker.FixedFormatter(('foo', 'bar'))))
def test_set_offset_string(formatter):
assert formatter.get_offset() == ''
formatter.set_offset_string('mpl')
assert formatter.get_offset() == 'mpl'
90 changes: 57 additions & 33 deletions lib/matplotlib/ticker.py
Original file line number Diff line number Diff line change
Expand Up @@ -446,33 +446,12 @@ def __init__(self, useOffset=None, useMathText=None, useLocale=None):
mpl.rcParams['axes.formatter.offset_threshold']
self.set_useOffset(useOffset)
self._usetex = mpl.rcParams['text.usetex']
if useMathText is None:
useMathText = mpl.rcParams['axes.formatter.use_mathtext']
if useMathText is False:
try:
from matplotlib import font_manager
ufont = font_manager.findfont(
font_manager.FontProperties(
mpl.rcParams["font.family"]
),
fallback_to_default=False,
)
except ValueError:
ufont = None

if ufont == str(cbook._get_data_path("fonts/ttf/cmr10.ttf")):
_api.warn_external(
"cmr10 font should ideally be used with "
"mathtext, set axes.formatter.use_mathtext to True"
)
self.set_useMathText(useMathText)
self.orderOfMagnitude = 0
self.format = ''
self._scientific = True
self._powerlimits = mpl.rcParams['axes.formatter.limits']
if useLocale is None:
useLocale = mpl.rcParams['axes.formatter.use_locale']
self._useLocale = useLocale
self.set_useLocale(useLocale)

def get_useOffset(self):
"""
Expand Down Expand Up @@ -579,6 +558,23 @@ def set_useMathText(self, val):
"""
if val is None:
self._useMathText = mpl.rcParams['axes.formatter.use_mathtext']
if self._useMathText is False:
try:
from matplotlib import font_manager
ufont = font_manager.findfont(
font_manager.FontProperties(
mpl.rcParams["font.family"]
),
fallback_to_default=False,
)
except ValueError:
ufont = None

if ufont == str(cbook._get_data_path("fonts/ttf/cmr10.ttf")):
_api.warn_external(
"cmr10 font should ideally be used with "
"mathtext, set axes.formatter.use_mathtext to True"
)
else:
self._useMathText = val

Expand Down Expand Up @@ -890,8 +886,8 @@ def __init__(self, base=10.0, labelOnlyBase=False,
minor_thresholds=None,
linthresh=None):

self._base = float(base)
self.labelOnlyBase = labelOnlyBase
self.set_base(base)
self.set_label_minor(labelOnlyBase)
if minor_thresholds is None:
if mpl.rcParams['_internal.classic_mode']:
minor_thresholds = (0, 0)
Expand All @@ -901,19 +897,41 @@ def __init__(self, base=10.0, labelOnlyBase=False,
self._sublabels = None
self._linthresh = linthresh

@_api.deprecated("3.6", alternative='set_base()')
def base(self, base):
"""
Change the *base* for labeling.

.. warning::
Should always match the base used for :class:`LogLocator`
"""
self._base = base
self.set_base(base)

def set_base(self, base):
"""
Change the *base* for labeling.

.. warning::
Should always match the base used for :class:`LogLocator`
"""
self._base = float(base)

@_api.deprecated("3.6", alternative='set_label_minor()')
def label_minor(self, labelOnlyBase):
"""
Switch minor tick labeling on or off.

Parameters
----------
labelOnlyBase : bool
If True, label ticks only at integer powers of base.
"""
self.set_label_minor(labelOnlyBase)

def set_label_minor(self, labelOnlyBase):
"""
Switch minor tick labeling on or off.

Parameters
----------
labelOnlyBase : bool
Expand Down Expand Up @@ -2250,7 +2268,8 @@ def __init__(self, base=10.0, subs=(1.0,), numdecs=4, numticks=None):
Parameters
----------
base : float, default: 10.0
The base of the log used, so ticks are placed at ``base**n``.
The base of the log used, so major ticks are placed at
``base**n 67E6 ``, n integer.
subs : None or str or sequence of float, default: (1.0,)
Gives the multiples of integer powers of the base at which
to place ticks. The default places ticks only at
Expand All @@ -2273,30 +2292,35 @@ def __init__(self, base=10.0, subs=(1.0,), numdecs=4, numticks=None):
numticks = 15
else:
numticks = 'auto'
self.base(base)
self.subs(subs)
self._base = float(base)
self._set_subs(subs)
self.numdecs = numdecs
self.numticks = numticks

def set_params(self, base=None, subs=None, numdecs=None, numticks=None):
"""Set parameters within this locator."""
if base is not None:
self.base(base)
self._base = float(base)
if subs is not None:
self.subs(subs)
self._set_subs(subs)
if numdecs is not None:
self.numdecs = numdecs
if numticks is not None:
self.numticks = numticks

# FIXME: these base and subs functions are contrary to our
# usual and desired API.

@_api.deprecated("3.6", alternative='set_params(base=...)')
def base(self, base):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a reason to not have set_base and set_subs here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My impression is that locators only supply set_params and no methods for individual parameters. (As opposed to formatters where separate methods seems like the standard.)

"""Set the log base (major tick every ``base**i``, i integer)."""
self._base = float(base)

@_api.deprecated("3.6", alternative='set_params(subs=...)')
def subs(self, subs):
"""
Set the minor ticks for the log scaling every ``base**i*subs[j]``.
"""
self._set_subs(subs)

def _set_subs(self, subs):
"""
Set the minor ticks for the log scaling every ``base**i*subs[j]``.
"""
Expand Down
0