Skip to content

Support standard tickdir control (in/out/inout) in axisartist. #30369

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
14 changes: 14 additions & 0 deletions doc/api/next_api_changes/deprecations/30369-AL.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
:mod:`.axisartist` now uses more standard tick direction controls
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Previously, the position of :mod:`.axisartist` ticks (inside or outside
the axes) were set using ``set_tick_out(bool)``. They are now set
using ``set_tick_direction("in")`` (or "out", or "inout"), and respect
:rc:`xtick.direction` and :rc:`ytick.direction`. In particular, they default
to pointing outwards, consistently with the rest of the library.

The *tick_out* parameter of `.Ticks` has been deprecated (use *tick_direction*
instead). The ``Ticks.get_tick_out`` method is deprecated (use
`.Ticks.get_tick_direction` instead).

The unused ``locs_angles_labels`` attribute of `.Ticks` and `.LabelBase` has
also been deprecated.
28 changes: 19 additions & 9 deletions galleries/examples/axisartist/simple_axis_pad.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,23 +81,33 @@ def ann(ax1, d):
va="top", ha="center")


ax1 = setup_axes(fig, rect=141)
ax1 = setup_axes(fig, rect=231)
axis = add_floating_axis1(ax1)
ann(ax1, r"default")
ann(ax1, "default")

ax1 = setup_axes(fig, rect=142)
ax1 = setup_axes(fig, rect=232)
axis = add_floating_axis1(ax1)
axis.major_ticklabels.set_pad(10)
ann(ax1, r"ticklabels.set_pad(10)")
ann(ax1, "ticklabels.set_pad(10)")

ax1 = setup_axes(fig, rect=143)
ax1 = setup_axes(fig, rect=233)
axis = add_floating_axis1(ax1)
axis.label.set_pad(20)
ann(ax1, r"label.set_pad(20)")
ann(ax1, "label.set_pad(20)")

ax1 = setup_axes(fig, rect=144)
ax1 = setup_axes(fig, rect=234)
axis = add_floating_axis1(ax1)
axis.major_ticks.set_tick_out(True)
ann(ax1, "ticks.set_tick_out(True)")
axis.major_ticks.set_tick_direction("in")
ann(ax1, 'ticks.set_tick_direction("in")')

ax1 = setup_axes(fig, rect=235)
axis = add_floating_axis1(ax1)
axis.major_ticks.set_tick_direction("out")
ann(ax1, 'ticks.set_tick_direction("out")')

ax1 = setup_axes(fig, rect=236)
axis = add_floating_axis1(ax1)
axis.major_ticks.set_tick_direction("inout")
ann(ax1, 'ticks.set_tick_direction("inout")')

plt.show()
50 changes: 25 additions & 25 deletions galleries/users_explain/toolkits/axisartist.rst
Original file line number Diff line number Diff line change
Expand Up @@ -287,16 +287,16 @@ HowTo

ax.axis[:].major_ticklabels.set_color("r")

4. To change the tick size (length), you need to use
axis.major_ticks.set_ticksize method. To change the direction of
the ticks (ticks are in opposite direction of ticklabels by
default), use axis.major_ticks.set_tick_out method.
4. To change the tick size (length), use ``axis.major_ticks.set_ticksize``.

To change the direction of the ticks, use
``axis.major_ticks.set_tick_direction``.

To change the pad between ticks and ticklabels, use
axis.major_ticklabels.set_pad method.
``axis.major_ticklabels.set_pad``.

To change the pad between ticklabels and axis label,
axis.label.set_pad method.
To change the pad between ticklabels and axis label, use
``axis.label.set_pad``.

Rotation and alignment of TickLabels
====================================
Expand Down Expand Up @@ -398,15 +398,14 @@ axis_direction of ticks, ticklabels, and axis-label does not affect
them.

If you want to make ticks outward and ticklabels inside the axes,
use invert_ticklabel_direction method. ::
use `.AxisArtist.invert_ticklabel_direction`::

ax.axis[:].invert_ticklabel_direction()

A related method is "set_tick_out". It makes ticks outward (as a
matter of fact, it makes ticks toward the opposite direction of the
default direction). ::
A related method is `.Ticks.set_tick_direction`. It can make ticks point "in",
"out", or "inout" (crossing the axis halfway)::

ax.axis[:].major_ticks.set_tick_out(True)
ax.axis[:].major_ticks.set_tick_direction("inout")

.. figure:: /gallery/axisartist/images/sphx_glr_simple_axis_direction03_001.png
:target: /gallery/axisartist/simple_axis_direction03.html
Expand All @@ -416,27 +415,28 @@ So, in summary,

* AxisArtist's methods

- set_axis_direction: "left", "right", "bottom", or "top"
- set_ticklabel_direction: "+" or "-"
- set_axislabel_direction: "+" or "-"
- invert_ticklabel_direction
- `~.AxisArtist.set_axis_direction`: "left", "right", "bottom", or "top"
- `~.AxisArtist.set_ticklabel_direction`: "+" or "-"
- `~.AxisArtist.set_axislabel_direction`: "+" or "-"
- `~.AxisArtist.invert_ticklabel_direction`

* Ticks' methods (major_ticks and minor_ticks)

- set_tick_out: True or False
- set_ticksize: size in points
- `~.Ticks.set_tick_direction`: "in", "out", or "inout"
- `~.Ticks.set_ticksize`: size in points

* TickLabels' methods (major_ticklabels and minor_ticklabels)

- set_axis_direction: "left", "right", "bottom", or "top"
- set_rotation: angle with respect to the reference direction
- set_ha and set_va: see below
- `~.TickLabels.set_axis_direction`: "left", "right", "bottom", or "top"
- `~.Text.set_rotation`: angle with respect to the reference direction
- `~.Text.set_horizontalalignment` and `~.Text.set_verticalalignment`: see below

* AxisLabels' methods (label)
* AxisLabel' methods (label)

- set_axis_direction: "left", "right", "bottom", or "top"
- set_rotation: angle with respect to the reference direction
- set_ha and set_va
- `~.AxisLabel.set_axis_direction`: "left", "right", "bottom", or "top"
- `~.Text.set_rotation`: angle with respect to the reference direction
- `~.Text.set_horizontalalignment` and
`~.Text.set_verticalalignment`

Adjusting ticklabels alignment
------------------------------
Expand Down
99 changes: 63 additions & 36 deletions lib/mpl_toolkits/axisartist/axis_artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,14 +55,14 @@
axislabel ha right center right center
=================== ====== ======== ====== ========

Ticks are by default direct opposite side of the ticklabels. To make ticks to
the same side of the ticklabels, ::
Tick orientation is controlled by :rc:`xtick.direction` and
:rc:`ytick.direction`; they can be manually adjusted using ::

ax.axis["bottom"].major_ticks.set_tick_out(True)
ax.axis["bottom"].major_ticks.set_tick_direction("in") # or "out", "inout"

The following attributes can be customized (use the ``set_xxx`` methods):

* `Ticks`: ticksize, tick_out
* `Ticks`: ticksize, tick_direction
* `TickLabels`: pad
* `AxisLabel`: pad
"""
Expand Down Expand Up @@ -109,16 +109,16 @@ class Ticks(AttributeCopier, Line2D):
Ticks are derived from `.Line2D`, and note that ticks themselves
are markers. Thus, you should use set_mec, set_mew, etc.

To change the tick size (length), you need to use
`set_ticksize`. To change the direction of the ticks (ticks are
in opposite direction of ticklabels by default), use
``set_tick_out(False)``
To change the tick size (length), use `set_ticksize`.
To change the direction of the ticks, use ``set_tick_direction("in")`` (or
"out", or "inout").
"""

locs_angles_labels = _api.deprecated("3.11")(property(lambda self: []))

@_api.delete_parameter("3.11", "tick_out", alternative="tick_direction")
def __init__(self, ticksize, tick_out=False, *, axis=None, **kwargs):
self._ticksize = ticksize
self.locs_angles_labels = []

self.set_tick_out(tick_out)

self._axis = axis
Expand Down Expand Up @@ -152,13 +152,33 @@ def get_markeredgecolor(self):
def get_markeredgewidth(self):
return self.get_attribute_from_ref_artist("markeredgewidth")

def set_tick_direction(self, direction):
_api.check_in_list(["in", "out", "inout"], direction=direction)
self._tick_dir = direction

def get_tick_direction(self):
return self._tick_dir

def set_tick_out(self, b):
Copy link
Member

Choose a reason for hiding this comment

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

missing deprecation?

Suggested change
def set_tick_out(self, b):
@_api.deprecated("3.11", alternative="set_tick_direction")
def set_tick_out(self, b):

If so, please also add to the change note.

Copy link
Contributor Author

@anntzer anntzer Jul 30, 2025

Choose a reason for hiding this comment

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

Intended to be kept, at least for now. It doesn't really hurt anything, and I'd rather not make users fiddle too much with a rather obscure part of the library for now (I'm just trying to slowly nudge axisartist towards more standard usability, but that motion is really slow and thus I'd rather not annoy too much end users, as it's not as if they could do all the fixes for all the future improvements at once).

"""Set whether ticks are drawn inside or outside the axes."""
self._tick_out = b
"""
Set whether ticks are drawn inside or outside the axes.

.. admonition:: Discouraged
Consider using the more general method `.set_tick_direction` instead.
"""
self._tick_dir = "out" if b else "in"

@_api.deprecated("3.11", alternative="get_tick_direction")
def get_tick_out(self):
"""Return whether ticks are drawn inside or outside the axes."""
return self._tick_out
if self._tick_dir == "in":
return False
elif self._tick_dir == "out":
return True
else:
raise ValueError(
f"Tick direction ({self._tick_dir!r}) not supported by get_tick_out, "
f"use get_tick_direction instead")

def set_ticksize(self, ticksize):
"""Set length of the ticks in points."""
Expand All @@ -171,8 +191,6 @@ def get_ticksize(self):
def set_locs_angles(self, locs_angles):
self.locs_angles = locs_angles

_tickvert_path = Path([[0., 0.], [1., 0.]])

def draw(self, renderer):
if not self.get_visible():
return
Expand All @@ -182,18 +200,20 @@ def draw(self, renderer):
gc.set_linewidth(self.get_markeredgewidth())
gc.set_alpha(self._alpha)

tickvert_path = (
Path([[0, 0], [1, 0]]) if self._tick_dir == "in" else
Path([[-1, 0], [0, 0]]) if self._tick_dir == "out" else
Path([[-.5, 0.], [.5, 0]])) # if self._tick_dir == "inout"
path_trans = self.get_transform()
marker_transform = (Affine2D()
.scale(renderer.points_to_pixels(self._ticksize)))
if self.get_tick_out():
marker_transform.rotate_deg(180)

for loc, angle in self.locs_angles:
locs = path_trans.transform_non_affine(np.array([loc]))
if self.axes and not self.axes.viewLim.contains(*locs[0]):
continue
renderer.draw_markers(
gc, self._tickvert_path,
gc, tickvert_path,
marker_transform + Affine2D().rotate_deg(angle),
Path(locs), path_trans.get_affine())

Expand All @@ -207,8 +227,9 @@ class LabelBase(mtext.Text):
text_ref_angle, and offset_radius attributes.
"""

locs_angles_labels = _api.deprecated("3.11")(property(lambda self: []))

def __init__(self, *args, **kwargs):
self.locs_angles_labels = []
self._ref_angle = 0
self._offset_radius = 0.

Expand Down Expand Up @@ -866,14 +887,16 @@ def _init_ticks(self, **kwargs):
+ self.offset_transform)

self.major_ticks = Ticks(
kwargs.get(
ticksize=kwargs.get(
"major_tick_size",
mpl.rcParams[f"{axis_name}tick.major.size"]),
tick_direction=mpl.rcParams[f"{axis_name}tick.direction"],
axis=self.axis, transform=trans)
self.minor_ticks = Ticks(
kwargs.get(
ticksize=kwargs.get(
"minor_tick_size",
mpl.rcParams[f"{axis_name}tick.minor.size"]),
tick_direction=mpl.rcParams[f"{axis_name}tick.direction"],
axis=self.axis, transform=trans)

size = mpl.rcParams[f"{axis_name}tick.labelsize"]
Expand Down Expand Up @@ -925,14 +948,13 @@ def _update_ticks(self, renderer=None):
if renderer is None:
renderer = self.get_figure(root=True)._get_renderer()

dpi_cor = renderer.points_to_pixels(1.)
if self.major_ticks.get_visible() and self.major_ticks.get_tick_out():
ticklabel_pad = self.major_ticks._ticksize * dpi_cor
self.major_ticklabels._external_pad = ticklabel_pad
self.minor_ticklabels._external_pad = ticklabel_pad
else:
self.major_ticklabels._external_pad = 0
self.minor_ticklabels._external_pad = 0
self.major_ticklabels._external_pad = \
self.minor_ticklabels._external_pad = (
renderer.points_to_pixels(self.major_ticks._ticksize)
* {"in": 0, "inout": 1/2, "out": 1}[
self.major_ticks.get_tick_direction()]
* self.major_ticks.get_visible() # 0 if invisible.
)

majortick_iter, minortick_iter = \
self._axis_artist_helper.get_tick_iterators(self.axes)
Expand Down Expand Up @@ -1007,13 +1029,18 @@ def _update_label(self, renderer):
return

if self._ticklabel_add_angle != self._axislabel_add_angle:
if ((self.major_ticks.get_visible()
and not self.major_ticks.get_tick_out())
or (self.minor_ticks.get_visible()
and not self.major_ticks.get_tick_out())):
axislabel_pad = self.major_ticks._ticksize
else:
axislabel_pad = 0
axislabel_pad = max(
# major pad:
self.major_ticks._ticksize
* {"in": 1, "inout": .5, "out": 0}[
self.major_ticks.get_tick_direction()]
* self.major_ticks.get_visible(), # 0 if invisible.
# minor pad:
self.minor_ticks._ticksize
* {"in": 1, "inout": .5, "out": 0}[
self.minor_ticks.get_tick_direction()]
* self.minor_ticks.get_visible(), # 0 if invisible.
)
else:
axislabel_pad = max(self.major_ticklabels._axislabel_pad,
self.minor_ticklabels._axislabel_pad)
Expand Down
4 changes: 2 additions & 2 deletions lib/mpl_toolkits/axisartist/tests/test_axis_artist.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ def test_ticks():
ticks_in.set_locs_angles(locs_angles)
ax.add_artist(ticks_in)

ticks_out = Ticks(ticksize=10, tick_out=True, color='C3', axis=ax.xaxis)
ticks_out = Ticks(ticksize=10, tick_direction="out", color='C3', axis=ax.xaxis)
ticks_out.set_locs_angles(locs_angles)
ax.add_artist(ticks_out)

Expand Down Expand Up @@ -89,11 +89,11 @@ def test_axis_artist():
for loc in ('left', 'right', 'bottom'):
helper = AxisArtistHelperRectlinear.Fixed(ax, loc=loc)
axisline = AxisArtist(ax, helper, offset=None, axis_direction=loc)
axisline.major_ticks.set_tick_direction("in")
ax.add_artist(axisline)

# Settings for bottom AxisArtist.
axisline.set_label("TTT")
axisline.major_ticks.set_tick_out(False)
axisline.label.set_pad(5)

ax.set_ylabel("Test")
Loading
Loading
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy