Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit39c1d50

Browse files
authored
Merge pull request#28968 from kyracho/positioning_for_rotated_labels
Implement xtick and ytick rotation modes
2 parents5714a18 +98778bb commit39c1d50

File tree

9 files changed

+131
-10
lines changed

9 files changed

+131
-10
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
``xtick`` and ``ytick`` rotation modes
2+
--------------------------------------
3+
4+
A new feature has been added for handling rotation of xtick and ytick
5+
labels more intuitively. The new `rotation modes <matplotlib.text.Text.set_rotation_mode>`
6+
"xtick" and "ytick" automatically adjust the alignment of rotated tick labels,
7+
so that the text points towards their anchor point, i.e. ticks. This works for
8+
all four sides of the plot (bottom, top, left, right), reducing the need for
9+
manual adjustments when rotating labels.
10+
11+
..plot::
12+
:include-source: true
13+
:alt: Example of rotated xtick and ytick labels.
14+
15+
import matplotlib.pyplot as plt
16+
17+
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(7, 3.5), layout='constrained')
18+
19+
pos = range(5)
20+
labels = ['label'] * 5
21+
ax1.set_xticks(pos, labels, rotation=-45, rotation_mode='xtick')
22+
ax1.set_yticks(pos, labels, rotation=45, rotation_mode='ytick')
23+
ax2.xaxis.tick_top()
24+
ax2.set_xticks(pos, labels, rotation=-45, rotation_mode='xtick')
25+
ax2.yaxis.tick_right()
26+
ax2.set_yticks(pos, labels, rotation=45, rotation_mode='ytick')
27+
28+
plt.show()

‎galleries/examples/images_contours_and_fields/image_annotated_heatmap.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464

6565
# Show all ticks and label them with the respective list entries
6666
ax.set_xticks(range(len(farmers)),labels=farmers,
67-
rotation=45,ha="right",rotation_mode="anchor")
67+
rotation=45,rotation_mode="xtick")
6868
ax.set_yticks(range(len(vegetables)),labels=vegetables)
6969

7070
# Loop over data dimensions and create text annotations.
@@ -135,7 +135,7 @@ def heatmap(data, row_labels, col_labels, ax=None,
135135

136136
# Show all ticks and label them with the respective list entries.
137137
ax.set_xticks(range(data.shape[1]),labels=col_labels,
138-
rotation=-30,ha="right",rotation_mode="anchor")
138+
rotation=-30,rotation_mode="xtick")
139139
ax.set_yticks(range(data.shape[0]),labels=row_labels)
140140

141141
# Let the horizontal axes labeling appear on top.

‎galleries/examples/subplots_axes_and_figures/align_labels_demo.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
ax.plot(np.arange(1.,0.,-0.1)*2000.,np.arange(1.,0.,-0.1))
2727
ax.set_title('Title0 1')
2828
ax.xaxis.tick_top()
29-
ax.tick_params(axis='x',rotation=55)
29+
ax.set_xticks(ax.get_xticks())
30+
ax.tick_params(axis='x',rotation=55,rotation_mode='xtick')
3031

3132

3233
foriinrange(2):
@@ -35,7 +36,8 @@
3536
ax.set_ylabel('YLabel1 %d'%i)
3637
ax.set_xlabel('XLabel1 %d'%i)
3738
ifi==0:
38-
ax.tick_params(axis='x',rotation=55)
39+
ax.set_xticks(ax.get_xticks())
40+
ax.tick_params(axis='x',rotation=55,rotation_mode='xtick')
3941

4042
fig.align_labels()# same as fig.align_xlabels(); fig.align_ylabels()
4143
fig.align_titles()

‎lib/matplotlib/axis.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ def __init__(
134134
# grid(color=(1, 1, 1, 0.5), alpha=rcParams['grid.alpha'])
135135
# so the that the rcParams default would override color alpha.
136136
grid_alpha=mpl.rcParams["grid.alpha"]
137-
grid_kw= {k[5:]:vfork,vinkwargs.items()}
137+
grid_kw= {k[5:]:vfork,vinkwargs.items()ifk!="rotation_mode"}
138138

139139
self.tick1line=mlines.Line2D(
140140
[], [],
@@ -1050,7 +1050,7 @@ def _translate_tick_params(cls, kw, reverse=False):
10501050
'tick1On','tick2On','label1On','label2On',
10511051
'length','direction','left','bottom','right','top',
10521052
'labelleft','labelbottom','labelright','labeltop',
1053-
'labelrotation',
1053+
'labelrotation','rotation_mode',
10541054
*_gridline_param_names]
10551055

10561056
keymap= {

‎lib/matplotlib/tests/test_text.py

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1135,3 +1135,58 @@ def test_font_wrap():
11351135
plt.text(3,4,t,family='monospace',ha='right',wrap=True)
11361136
plt.text(-1,0,t,fontsize=14,style='italic',ha='left',rotation=-15,
11371137
wrap=True)
1138+
1139+
1140+
deftest_ha_for_angle():
1141+
text_instance=Text()
1142+
angles=np.arange(0,360.1,0.1)
1143+
forangleinangles:
1144+
alignment=text_instance._ha_for_angle(angle)
1145+
assertalignmentin ['center','left','right']
1146+
1147+
1148+
deftest_va_for_angle():
1149+
text_instance=Text()
1150+
angles=np.arange(0,360.1,0.1)
1151+
forangleinangles:
1152+
alignment=text_instance._va_for_angle(angle)
1153+
assertalignmentin ['center','top','baseline']
1154+
1155+
1156+
@image_comparison(baseline_images=['xtick_rotation_mode'],
1157+
remove_text=False,extensions=['png'],style='mpl20')
1158+
deftest_xtick_rotation_mode():
1159+
fig,ax=plt.subplots(figsize=(12,1))
1160+
ax.set_yticks([])
1161+
ax2=ax.twiny()
1162+
1163+
ax.set_xticks(range(37), ['foo']*37,rotation_mode="xtick")
1164+
ax2.set_xticks(range(37), ['foo']*37,rotation_mode="xtick")
1165+
1166+
angles=np.linspace(0,360,37)
1167+
1168+
fortick,angleinzip(ax.get_xticklabels(),angles):
1169+
tick.set_rotation(angle)
1170+
fortick,angleinzip(ax2.get_xticklabels(),angles):
1171+
tick.set_rotation(angle)
1172+
1173+
plt.subplots_adjust(left=0.01,right=0.99,top=.6,bottom=.4)
1174+
1175+
1176+
@image_comparison(baseline_images=['ytick_rotation_mode'],
1177+
remove_text=False,extensions=['png'],style='mpl20')
1178+
deftest_ytick_rotation_mode():
1179+
fig,ax=plt.subplots(figsize=(1,12))
1180+
ax.set_xticks([])
1181+
ax2=ax.twinx()
1182+
1183+
ax.set_yticks(range(37), ['foo']*37,rotation_mode="ytick")
1184+
ax2.set_yticks(range(37), ['foo']*37,rotation_mode='ytick')
1185+
1186+
angles=np.linspace(0,360,37)
1187+
fortick,angleinzip(ax.get_yticklabels(),angles):
1188+
tick.set_rotation(angle)
1189+
fortick,angleinzip(ax2.get_yticklabels(),angles):
1190+
tick.set_rotation(angle)
1191+
1192+
plt.subplots_adjust(left=0.4,right=0.6,top=.99,bottom=.01)

‎lib/matplotlib/text.py

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -300,16 +300,19 @@ def set_rotation_mode(self, m):
300300
301301
Parameters
302302
----------
303-
m : {None, 'default', 'anchor'}
303+
m : {None, 'default', 'anchor', 'xtick', 'ytick'}
304304
If ``"default"``, the text will be first rotated, then aligned according
305305
to their horizontal and vertical alignments. If ``"anchor"``, then
306-
alignment occurs before rotation. Passing ``None`` will set the rotation
307-
mode to ``"default"``.
306+
alignment occurs before rotation. "xtick" and "ytick" adjust the
307+
horizontal/vertical alignment so that the text is visually pointing
308+
towards its anchor point. This is primarily used for rotated tick
309+
labels and positions them nicely towards their ticks. Passing
310+
``None`` will set the rotation mode to ``"default"``.
308311
"""
309312
ifmisNone:
310313
m="default"
311314
else:
312-
_api.check_in_list(("anchor","default"),rotation_mode=m)
315+
_api.check_in_list(("anchor","default","xtick","ytick"),rotation_mode=m)
313316
self._rotation_mode=m
314317
self.stale=True
315318

@@ -453,6 +456,11 @@ def _get_layout(self, renderer):
453456

454457
rotation_mode=self.get_rotation_mode()
455458
ifrotation_mode!="anchor":
459+
angle=self.get_rotation()
460+
ifrotation_mode=='xtick':
461+
halign=self._ha_for_angle(angle)
462+
elifrotation_mode=='ytick':
463+
valign=self._va_for_angle(angle)
456464
# compute the text location in display coords and the offsets
457465
# necessary to align the bbox with that location
458466
ifhalign=='center':
@@ -1376,6 +1384,32 @@ def set_fontname(self, fontname):
13761384
"""
13771385
self.set_fontfamily(fontname)
13781386

1387+
def_ha_for_angle(self,angle):
1388+
"""
1389+
Determines horizontal alignment ('ha') for rotation_mode "xtick" based on
1390+
the angle of rotation in degrees and the vertical alignment.
1391+
"""
1392+
anchor_at_bottom=self.get_verticalalignment()=='bottom'
1393+
if (angle<=10or85<=angle<=95or350<=angleor
1394+
170<=angle<=190or265<=angle<=275):
1395+
return'center'
1396+
elif10<angle<85or190<angle<265:
1397+
return'left'ifanchor_at_bottomelse'right'
1398+
return'right'ifanchor_at_bottomelse'left'
1399+
1400+
def_va_for_angle(self,angle):
1401+
"""
1402+
Determines vertical alignment ('va') for rotation_mode "ytick" based on
1403+
the angle of rotation in degrees and the horizontal alignment.
1404+
"""
1405+
anchor_at_left=self.get_horizontalalignment()=='left'
1406+
if (angle<=10or350<=angleor170<=angle<=190
1407+
or80<=angle<=100or260<=angle<=280):
1408+
return'center'
1409+
elif190<angle<260or10<angle<80:
1410+
return'baseline'ifanchor_at_leftelse'top'
1411+
return'top'ifanchor_at_leftelse'baseline'
1412+
13791413

13801414
classOffsetFrom:
13811415
"""Callable helper class for working with `Annotation`."""

‎lib/matplotlib/text.pyi

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,8 @@ class Text(Artist):
106106
defset_fontname(self,fontname:str|Iterable[str])->None: ...
107107
defget_antialiased(self)->bool: ...
108108
defset_antialiased(self,antialiased:bool)->None: ...
109+
def_ha_for_angle(self,angle:Any)->Literal['center','right','left']|None: ...
110+
def_va_for_angle(self,angle:Any)->Literal['center','top','baseline']|None: ...
109111

110112
classOffsetFrom:
111113
def__init__(

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp