Skip to content

pyplot: minorticks_on() freezes the major tick positions, so labels vanish on zoom #493

Description

@MoAly98

Calling minorticks_on() turns the major ticks from an automatic axis into a fixed list of positions computed for the initial view. Zoom in interactively and the labels disappear once you get past roughly a factor of two, because the view no longer contains any of the frozen positions. Without minorticks_on() the labels rescale normally.

Reproducer

import xy.pyplot as plt


def x_axis(minor):
    fig, ax = plt.subplots(figsize=(6, 4))
    ax.plot([0, 10], [0, 10])
    if minor:
        ax.minorticks_on()
    ax.tick_params(which="major", direction="in", length=6,
                   bottom=True, top=True, left=True, right=True)
    payload = ax._build_chart(600, 400).figure().build_payload()
    spec = payload[0] if isinstance(payload, tuple) else payload
    return spec["x_axis"]


without, with_minor = x_axis(False), x_axis(True)
for key in ("tick_values", "tick_count", "minor_tick_values"):
    print(f"{key}")
    print(f"  without minorticks_on(): {str(without.get(key))[:60]}")
    print(f"  with    minorticks_on(): {str(with_minor.get(key))[:60]}")

On main (84cb0e1), Python 3.14, macOS:

tick_values
  without minorticks_on(): None
  with    minorticks_on(): [0.0, 2.0, 4.0, 6.0, 8.0, 10.0]
tick_count
  without minorticks_on(): 9
  with    minorticks_on(): 6
minor_tick_values
  without minorticks_on(): None
  with    minorticks_on(): [-0.5, 0.5, 1.0, 1.5, 2.5, 3.0, 3.5, 4.5, 5.0, 5.5

Without minor ticks the axis ships no authored major positions, so the client generates ticks for whatever view it is showing. With minor ticks the majors become six fixed values. The client then filters that fixed list to the visible window, so zooming into leaves nothing to label.

To see the visible effect, render the two figures in a notebook and zoom: the one with minorticks_on() loses its labels and keeps its tick marks.

The problem

_apply_tickers returns early when nothing is registered:

if locator is None and formatter is None and minor_locator is None and not is_log:
    return

minorticks_on() registers a minor_locator, so the guard is not triggered and execution reaches props["tick_values"] = ..., which writes the majors as an authored list.

That write makes sense when the caller sets a major locator or formatter, since those define the positions. But, by registering a minor locator my major ticks shouldn't get pinned. Internally I would expect a redivision of the ticks for the minors, similar to matplotlib's AutoLocator.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions