Skip to content

Commit 15bfe84

Browse files
committed
Support standard tickdir control (in/out/inout) in axisartist.
... and also respect rcParams, which makes axisartist ticks default to pointing outwards, similarly to standard ticks. Rework the tick direction machinery to also support "inout" (similarly to standard ticks). One may wonder whether external ticks should really be drawn in the local direction of the gridlines, though, or whether drawing them orthogonal to the axis spine would look better. This PR keeps the old behavior of `set_ticks_out(True)`, but we could later add something like ``set_tick_direction("out_ortho")`` (name up to bikeshedding). While at it, also deprecate the entirely unused `Ticks.locs_angles_labels` and `LabelBase.locs_angles_labels`.
1 parent b2e8b93 commit 15bfe84

File tree

8 files changed

+128
-56
lines changed

8 files changed

+128
-56
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
:mod:`.axisartist` now uses more standard tick direction controls
2+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3+
Previously, the position of :mod:`.axisartist` ticks (inside or outside
4+
the axes) were set using ``set_tick_out(bool)``. They are now set
5+
using ``set_tick_direction("in")`` (or "out", or "inout"), and respect
6+
:rc:`xtick.direction` and :rc:`ytick.direction`. In particular, they default
7+
to pointing outwards, consistently with the rest of the library.
8+
9+
The *tick_out* parameter of `.Ticks` has been deprecated (use *tick_direction*
10+
instead). The ``Ticks.get_tick_out`` method is deprecated (use
11+
`.Ticks.get_tick_direction` instead).
12+
13+
The unused ``locs_angles_labels`` attribute of `.Ticks` and `.LabelBase` has
14+
also been deprecated.

galleries/examples/axisartist/simple_axis_pad.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -81,23 +81,33 @@ def ann(ax1, d):
8181
va="top", ha="center")
8282

8383

84-
ax1 = setup_axes(fig, rect=141)
84+
ax1 = setup_axes(fig, rect=231)
8585
axis = add_floating_axis1(ax1)
86-
ann(ax1, r"default")
86+
ann(ax1, "default")
8787

88-
ax1 = setup_axes(fig, rect=142)
88+
ax1 = setup_axes(fig, rect=232)
8989
axis = add_floating_axis1(ax1)
9090
axis.major_ticklabels.set_pad(10)
91-
ann(ax1, r"ticklabels.set_pad(10)")
91+
ann(ax1, "ticklabels.set_pad(10)")
9292

93-
ax1 = setup_axes(fig, rect=143)
93+
ax1 = setup_axes(fig, rect=233)
9494
axis = add_floating_axis1(ax1)
9595
axis.label.set_pad(20)
96-
ann(ax1, r"label.set_pad(20)")
96+
ann(ax1, "label.set_pad(20)")
9797

98-
ax1 = setup_axes(fig, rect=144)
98+
ax1 = setup_axes(fig, rect=234)
9999
axis = add_floating_axis1(ax1)
100-
axis.major_ticks.set_tick_out(True)
101-
ann(ax1, "ticks.set_tick_out(True)")
100+
axis.major_ticks.set_tick_direction("in")
101+
ann(ax1, 'ticks.set_tick_direction("in")')
102+
103+
ax1 = setup_axes(fig, rect=235)
104+
axis = add_floating_axis1(ax1)
105+
axis.major_ticks.set_tick_direction("out")
106+
ann(ax1, 'ticks.set_tick_direction("out")')
107+
108+
ax1 = setup_axes(fig, rect=236)
109+
axis = add_floating_axis1(ax1)
110+
axis.major_ticks.set_tick_direction("inout")
111+
ann(ax1, 'ticks.set_tick_direction("inout")')
102112

103113
plt.show()

galleries/users_explain/toolkits/axisartist.rst

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -287,16 +287,16 @@ HowTo
287287

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

290-
4. To change the tick size (length), you need to use
291-
axis.major_ticks.set_ticksize method. To change the direction of
292-
the ticks (ticks are in opposite direction of ticklabels by
293-
default), use axis.major_ticks.set_tick_out method.
290+
4. To change the tick size (length), use ``axis.major_ticks.set_ticksize``.
291+
292+
To change the direction of the ticks, use
293+
``axis.major_ticks.set_tick_direction``.
294294

295295
To change the pad between ticks and ticklabels, use
296-
axis.major_ticklabels.set_pad method.
296+
``axis.major_ticklabels.set_pad``.
297297

298-
To change the pad between ticklabels and axis label,
299-
axis.label.set_pad method.
298+
To change the pad between ticklabels and axis label, use
299+
``axis.label.set_pad method``.
300300

301301
Rotation and alignment of TickLabels
302302
====================================
@@ -398,15 +398,14 @@ axis_direction of ticks, ticklabels, and axis-label does not affect
398398
them.
399399

400400
If you want to make ticks outward and ticklabels inside the axes,
401-
use invert_ticklabel_direction method. ::
401+
use invert_ticklabel_direction method::
402402

403403
ax.axis[:].invert_ticklabel_direction()
404404

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

409-
ax.axis[:].major_ticks.set_tick_out(True)
408+
ax.axis[:].major_ticks.set_tick_direction("inout")
410409

411410
.. figure:: /gallery/axisartist/images/sphx_glr_simple_axis_direction03_001.png
412411
:target: /gallery/axisartist/simple_axis_direction03.html
@@ -423,7 +422,7 @@ So, in summary,
423422

424423
* Ticks' methods (major_ticks and minor_ticks)
425424

426-
- set_tick_out: True or False
425+
- set_tick_direction: "in", "out", or "inout"
427426
- set_ticksize: size in points
428427

429428
* TickLabels' methods (major_ticklabels and minor_ticklabels)

lib/mpl_toolkits/axisartist/axis_artist.py

Lines changed: 61 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,14 @@
5555
axislabel ha right center right center
5656
=================== ====== ======== ====== ========
5757
58-
Ticks are by default direct opposite side of the ticklabels. To make ticks to
59-
the same side of the ticklabels, ::
58+
Tick orientation is controlled by ``rcParams["xtick.direction"]`` and
59+
``rcParams["ytick.direction"]``; they can be manually adjusted using ::
6060
61-
ax.axis["bottom"].major_ticks.set_tick_out(True)
61+
ax.axis["bottom"].major_ticks.set_tick_direction("in") # or "out", "inout"
6262
6363
The following attributes can be customized (use the ``set_xxx`` methods):
6464
65-
* `Ticks`: ticksize, tick_out
65+
* `Ticks`: ticksize, tick_direction
6666
* `TickLabels`: pad
6767
* `AxisLabel`: pad
6868
"""
@@ -109,16 +109,16 @@ class Ticks(AttributeCopier, Line2D):
109109
Ticks are derived from `.Line2D`, and note that ticks themselves
110110
are markers. Thus, you should use set_mec, set_mew, etc.
111111
112-
To change the tick size (length), you need to use
113-
`set_ticksize`. To change the direction of the ticks (ticks are
114-
in opposite direction of ticklabels by default), use
115-
``set_tick_out(False)``
112+
To change the tick size (length), use `set_ticksize`.
113+
To change the direction of the ticks, use ``set_tick_direction("in")`` (or
114+
"out", or "inout").
116115
"""
117116

117+
locs_angles_labels = _api.deprecated("3.11")(property(lambda self: []))
118+
119+
@_api.delete_parameter("3.11", "tick_out", alternative="tick_direction")
118120
def __init__(self, ticksize, tick_out=False, *, axis=None, **kwargs):
119121
self._ticksize = ticksize
120-
self.locs_angles_labels = []
121-
122122
self.set_tick_out(tick_out)
123123

124124
self._axis = axis
@@ -152,13 +152,28 @@ def get_markeredgecolor(self):
152152
def get_markeredgewidth(self):
153153
return self.get_attribute_from_ref_artist("markeredgewidth")
154154

155+
def set_tick_direction(self, direction):
156+
_api.check_in_list(["in", "out", "inout"], direction=direction)
157+
self._tick_dir = direction
158+
159+
def get_tick_direction(self):
160+
return self._tick_dir
161+
155162
def set_tick_out(self, b):
156163
"""Set whether ticks are drawn inside or outside the axes."""
157-
self._tick_out = b
164+
self._tick_dir = "out" if b else "in"
158165

166+
@_api.deprecated("3.11", alternative="get_tick_direction")
159167
def get_tick_out(self):
160168
"""Return whether ticks are drawn inside or outside the axes."""
161-
return self._tick_out
169+
if self._tick_dir == "in":
170+
return False
171+
elif self._tick_dir == "out":
172+
return True
173+
else:
174+
raise ValueError(
175+
f"Tick direction ({self._tick_dir!r}) not supported by get_tick_dir, "
176+
f"use get_tick_direction instead")
162177

163178
def set_ticksize(self, ticksize):
164179
"""Set length of the ticks in points."""
@@ -171,8 +186,6 @@ def get_ticksize(self):
171186
def set_locs_angles(self, locs_angles):
172187
self.locs_angles = locs_angles
173188

174-
_tickvert_path = Path([[0., 0.], [1., 0.]])
175-
176189
def draw(self, renderer):
177190
if not self.get_visible():
178191
return
@@ -182,18 +195,20 @@ def draw(self, renderer):
182195
gc.set_linewidth(self.get_markeredgewidth())
183196
gc.set_alpha(self._alpha)
184197

198+
tickvert_path = (
199+
Path([[0, 0], [1, 0]]) if self._tick_dir == "in" else
200+
Path([[-1, 0], [0, 0]]) if self._tick_dir == "out" else
201+
Path([[-.5, 0.], [.5, 0]])) # if self._tick_dir == "inout"
185202
path_trans = self.get_transform()
186203
marker_transform = (Affine2D()
187204
.scale(renderer.points_to_pixels(self._ticksize)))
188-
if self.get_tick_out():
189-
marker_transform.rotate_deg(180)
190205

191206
for loc, angle in self.locs_angles:
192207
locs = path_trans.transform_non_affine(np.array([loc]))
193208
if self.axes and not self.axes.viewLim.contains(*locs[0]):
194209
continue
195210
renderer.draw_markers(
196-
gc, self._tickvert_path,
211+
gc, tickvert_path,
197212
marker_transform + Affine2D().rotate_deg(angle),
198213
Path(locs), path_trans.get_affine())
199214

@@ -207,8 +222,9 @@ class LabelBase(mtext.Text):
207222
text_ref_angle, and offset_radius attributes.
208223
"""
209224

225+
locs_angles_labels = _api.deprecated("3.11")(property(lambda self: []))
226+
210227
def __init__(self, *args, **kwargs):
211-
self.locs_angles_labels = []
212228
self._ref_angle = 0
213229
self._offset_radius = 0.
214230

@@ -866,14 +882,16 @@ def _init_ticks(self, **kwargs):
866882
+ self.offset_transform)
867883

868884
self.major_ticks = Ticks(
869-
kwargs.get(
885+
ticksize=kwargs.get(
870886
"major_tick_size",
871887
mpl.rcParams[f"{axis_name}tick.major.size"]),
888+
tick_direction=mpl.rcParams[f"{axis_name}tick.direction"],
872889
axis=self.axis, transform=trans)
873890
self.minor_ticks = Ticks(
874-
kwargs.get(
891+
ticksize=kwargs.get(
875892
"minor_tick_size",
876893
mpl.rcParams[f"{axis_name}tick.minor.size"]),
894+
tick_direction=mpl.rcParams[f"{axis_name}tick.direction"],
877895
axis=self.axis, transform=trans)
878896

879897
size = mpl.rcParams[f"{axis_name}tick.labelsize"]
@@ -926,10 +944,17 @@ def _update_ticks(self, renderer=None):
926944
renderer = self.get_figure(root=True)._get_renderer()
927945

928946
dpi_cor = renderer.points_to_pixels(1.)
929-
if self.major_ticks.get_visible() and self.major_ticks.get_tick_out():
947+
if self.major_ticks.get_visible():
930948
ticklabel_pad = self.major_ticks._ticksize * dpi_cor
931-
self.major_ticklabels._external_pad = ticklabel_pad
932-
self.minor_ticklabels._external_pad = ticklabel_pad
949+
if self.major_ticks.get_tick_direction() == "in":
950+
self.major_ticklabels._external_pad = 0
951+
self.minor_ticklabels._external_pad = 0
952+
elif self.major_ticks.get_tick_direction() == "out":
953+
self.major_ticklabels._external_pad = ticklabel_pad
954+
self.minor_ticklabels._external_pad = ticklabel_pad
955+
else: # inout
956+
self.major_ticklabels._external_pad = ticklabel_pad / 2
957+
self.minor_ticklabels._external_pad = ticklabel_pad / 2
933958
else:
934959
self.major_ticklabels._external_pad = 0
935960
self.minor_ticklabels._external_pad = 0
@@ -1007,13 +1032,19 @@ def _update_label(self, renderer):
10071032
return
10081033

10091034
if self._ticklabel_add_angle != self._axislabel_add_angle:
1010-
if ((self.major_ticks.get_visible()
1011-
and not self.major_ticks.get_tick_out())
1012-
or (self.minor_ticks.get_visible()
1013-
and not self.major_ticks.get_tick_out())):
1014-
axislabel_pad = self.major_ticks._ticksize
1015-
else:
1016-
axislabel_pad = 0
1035+
axislabel_pad = 0
1036+
if self.major_ticks.get_visible():
1037+
axislabel_pad = max(
1038+
axislabel_pad,
1039+
{"in": 1, "inout": .5, "out": 0}[
1040+
self.major_ticks.get_tick_direction()]
1041+
* self.major_ticks._ticksize)
1042+
if self.minor_ticks.get_visible():
1043+
axislabel_pad = max(
1044+
axislabel_pad,
1045+
{"in": 1, "inout": .5, "out": 0}[
1046+
self.minor_ticks.get_tick_direction()]
1047+
* self.major_ticks._ticksize)
10171048
else:
10181049
axislabel_pad = max(self.major_ticklabels._axislabel_pad,
10191050
self.minor_ticklabels._axislabel_pad)

lib/mpl_toolkits/axisartist/tests/test_axis_artist.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def test_ticks():
1919
ticks_in.set_locs_angles(locs_angles)
2020
ax.add_artist(ticks_in)
2121

22-
ticks_out = Ticks(ticksize=10, tick_out=True, color='C3', axis=ax.xaxis)
22+
ticks_out = Ticks(ticksize=10, tick_direction="out", color='C3', axis=ax.xaxis)
2323
ticks_out.set_locs_angles(locs_angles)
2424
ax.add_artist(ticks_out)
2525

@@ -89,11 +89,11 @@ def test_axis_artist():
8989
for loc in ('left', 'right', 'bottom'):
9090
helper = AxisArtistHelperRectlinear.Fixed(ax, loc=loc)
9191
axisline = AxisArtist(ax, helper, offset=None, axis_direction=loc)
92+
axisline.major_ticks.set_tick_direction("in")
9293
ax.add_artist(axisline)
9394

9495
# Settings for bottom AxisArtist.
9596
axisline.set_label("TTT")
96-
axisline.major_ticks.set_tick_out(False)
9797
axisline.label.set_pad(5)
9898

9999
ax.set_ylabel("Test")

lib/mpl_toolkits/axisartist/tests/test_axislines.py

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
@image_comparison(['SubplotZero.png'], style='default')
1111
def test_SubplotZero():
1212
# Remove this line when this test image is regenerated.
13-
plt.rcParams['text.kerning_factor'] = 6
13+
plt.rcParams.update({
14+
"text.kerning_factor": 6, "xtick.direction": "in", "ytick.direction": "in"})
1415

1516
fig = plt.figure()
1617

@@ -31,7 +32,8 @@ def test_SubplotZero():
3132
@image_comparison(['Subplot.png'], style='default')
3233
def test_Subplot():
3334
# Remove this line when this test image is regenerated.
34-
plt.rcParams['text.kerning_factor'] = 6
35+
plt.rcParams.update({
36+
"text.kerning_factor": 6, "xtick.direction": "in", "ytick.direction": "in"})
3537

3638
fig = plt.figure()
3739

@@ -60,6 +62,8 @@ def test_Axes():
6062
@image_comparison(['ParasiteAxesAuxTrans_meshplot.png'],
6163
remove_text=True, style='default', tol=0.075)
6264
def test_ParasiteAxesAuxTrans():
65+
# Remove this line when this test image is regenerated.
66+
plt.rcParams.update({"xtick.direction": "in", "ytick.direction": "in"})
6367
data = np.ones((6, 6))
6468
data[2, 2] = 2
6569
data[0, :] = 0
@@ -91,6 +95,8 @@ def test_ParasiteAxesAuxTrans():
9195

9296
@image_comparison(['axisline_style.png'], remove_text=True, style='mpl20')
9397
def test_axisline_style():
98+
# Remove this line when this test image is regenerated.
99+
plt.rcParams.update({"xtick.direction": "in", "ytick.direction": "in"})
94100
fig = plt.figure(figsize=(2, 2))
95101
ax = fig.add_subplot(axes_class=AxesZero)
96102
ax.axis["xzero"].set_axisline_style("-|>")
@@ -105,6 +111,8 @@ def test_axisline_style():
105111
@image_comparison(['axisline_style_size_color.png'], remove_text=True,
106112
style='mpl20')
107113
def test_axisline_style_size_color():
114+
# Remove this line when this test image is regenerated.
115+
plt.rcParams.update({"xtick.direction": "in", "ytick.direction": "in"})
108116
fig = plt.figure(figsize=(2, 2))
109117
ax = fig.add_subplot(axes_class=AxesZero)
110118
ax.axis["xzero"].set_axisline_style("-|>", size=2.0, facecolor='r')
@@ -119,6 +127,8 @@ def test_axisline_style_size_color():
119127
@image_comparison(['axisline_style_tight.png'], remove_text=True,
120128
style='mpl20')
121129
def test_axisline_style_tight():
130+
# Remove this line when this test image is regenerated.
131+
plt.rcParams.update({"xtick.direction": "in", "ytick.direction": "in"})
122132
fig = plt.figure(figsize=(2, 2), layout='tight')
123133
ax = fig.add_subplot(axes_class=AxesZero)
124134
ax.axis["xzero"].set_axisline_style("-|>", size=5, facecolor='g')
@@ -132,6 +142,8 @@ def test_axisline_style_tight():
132142

133143
@image_comparison(['subplotzero_ylabel.png'], style='mpl20')
134144
def test_subplotzero_ylabel():
145+
# Remove this line when this test image is regenerated.
146+
plt.rcParams.update({"xtick.direction": "in", "ytick.direction": "in"})
135147
fig = plt.figure()
136148
ax = fig.add_subplot(111, axes_class=SubplotZero)
137149

lib/mpl_toolkits/axisartist/tests/test_floating_axes.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def test_subplot():
2323
# remove when image is regenerated.
2424
@image_comparison(['curvelinear3.png'], style='default', tol=5)
2525
def test_curvelinear3():
26+
plt.rcParams.update({"xtick.direction": "in", "ytick.direction": "in"})
2627
fig = plt.figure(figsize=(5, 5))
2728

2829
tr = (mtransforms.Affine2D().scale(np.pi / 180, 1) +
@@ -71,6 +72,7 @@ def test_curvelinear3():
7172
def test_curvelinear4():
7273
# Remove this line when this test image is regenerated.
7374
plt.rcParams['text.kerning_factor'] = 6
75+
plt.rcParams.update({"xtick.direction": "in", "ytick.direction": "in"})
7476

7577
fig = plt.figure(figsize=(5, 5))
7678

0 commit comments

Comments
 (0)
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