Skip to content
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
33 changes: 33 additions & 0 deletions lib/matplotlib/axis.py
Original file line number Diff line number Diff line change
Expand Up @@ -2668,6 +2668,39 @@ def contains(self, mouseevent):
t < y < t + self._pickradius)
return inaxis, {}

def _get_ticklabel_bboxes(self, ticks, renderer):
# Top alignment normally preserves the padding from the Axes, but can
# give plain, horizontal labels different baselines when their measured
# ascents differ. Only normalize groups that actually differ so that
# uniformly sized labels keep their existing position.
for labels in ([tick.label1 for tick in ticks],
[tick.label2 for tick in ticks]):
layouts = []
for label in labels:
label._baseline_ascent = None
if (not label.get_visible()
or not label.get_in_layout()
or not label.get_text()
or label.get_verticalalignment() != "top"
or label.get_rotation() != 0
or label.get_rotation_mode() == "anchor"):
continue
_, line_layouts, _ = label._get_layout(renderer)
if len(line_layouts) != 1:
continue
line, (_, ascent, _), _ = line_layouts[0]
_, ismath = label._preprocess_math(line)
if ismath is False:
layouts.append((label, ascent))

ascents = [ascent for _, ascent in layouts]
if ascents and not np.allclose(ascents, ascents[0]):
baseline_ascent = min(ascents)
for label, _ in layouts:
label._baseline_ascent = baseline_ascent

return super()._get_ticklabel_bboxes(ticks, renderer)

def set_label_position(self, position):
"""
Set the label position (top or bottom)
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
31 changes: 31 additions & 0 deletions lib/matplotlib/tests/test_axis.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import numpy as np

import pytest

import matplotlib.pyplot as plt
from matplotlib.axis import XTick
from matplotlib.testing.decorators import check_figures_equal
Expand All @@ -11,6 +13,35 @@ def test_tick_labelcolor_array():
XTick(ax, 0, labelcolor=np.array([1, 0, 0, 1]))


def test_xtick_labels_share_baseline():
fig, ax = plt.subplots(figsize=(.5, .5), layout="constrained")
ax.set_xlim(-2, 3)
ax.set_xticks([0, 1])
ax.xaxis.set_major_formatter(
lambda x, pos: "pol." if x == 0 else str(int(x)))

fig.canvas.draw()
bboxes = [label.get_window_extent(fig.canvas.get_renderer())
for label in ax.get_xticklabels()]

assert bboxes[0].y0 == pytest.approx(bboxes[1].y0)


def test_xtick_labels_with_matching_ascents_keep_top_alignment():
fig, axs = plt.subplots(2)
for ax, label in zip(axs, ["1", "foo"]):
ax.set_xticks([0, 1], [label, label])

fig.canvas.draw()
renderer = fig.canvas.get_renderer()
label_pads = [
ax.bbox.y0 - ax.get_xticklabels()[0].get_window_extent(renderer).y1
for ax in axs
]

assert label_pads[0] == pytest.approx(label_pads[1])


def test_axis_not_in_layout():
fig1, (ax1_left, ax1_right) = plt.subplots(ncols=2, layout='constrained')
fig2, (ax2_left, ax2_right) = plt.subplots(ncols=2, layout='constrained')
Expand Down
19 changes: 12 additions & 7 deletions lib/matplotlib/text.py
Original file line number Diff line number Diff line change
Expand Up @@ -563,13 +563,18 @@ def _get_layout(self, renderer):
xmax if halign == "right" else
(xmin + xmax) / 2 # halign == "center"
)
offsety = (
ymin if valign == "bottom" else
ymax if valign == "top" else
(ymin + ymax) / 2 if valign == "center" else
ymin + descent if valign == "baseline" else
ymin + height_rot - baseline / 2 # valign == "center_baseline"
)
baseline_ascent = getattr(self, "_baseline_ascent", None)
if (valign == "top" and len(lines) == 1 and angle == 0
and baseline_ascent is not None):
offsety = ymax + baseline_ascent - baseline
else:
offsety = (
ymin if valign == "bottom" else
ymax if valign == "top" else
(ymin + ymax) / 2 if valign == "center" else
ymin + descent if valign == "baseline" else
ymin + height_rot - baseline / 2 # valign == "center_baseline"
)
else:
xmin1, ymin1 = corners_horiz[0]
xmax1, ymax1 = corners_horiz[2]
Expand Down
Loading