Skip to content

Commit da6c6b5

Browse files
committed
Merge pull request #2351 from tacaswell/annotation_refactor
Annotation refactor
2 parents d583f3b + f6e3edc commit da6c6b5

File tree

5 files changed

+186
-121
lines changed

5 files changed

+186
-121
lines changed

doc/api/api_changes.rst

+15
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,24 @@ original location:
5959
thus `colorbar.ColorbarBase.outline` is now a
6060
`matplotlib.patches.Polygon` object.
6161

62+
6263
* The rcParams `savefig.transparent` has been added to control
6364
default transparency when saving figures.
6465

66+
* Slightly refactored the `Annotation` family. The text location in
67+
`Annotation` is now handled entirely handled by the underlying `Text`
68+
object so `set_position` works as expected. The attributes `xytext` and
69+
`textcoords` have been deprecated in favor of `xyann` and `anncoords` so
70+
that `Annotation` and `AnnotaionBbox` can share a common sensibly named
71+
api for getting/setting the location of the text or box.
72+
73+
- `xyann` -> set the location of the annotation
74+
- `xy` -> set where the arrow points to
75+
- `anncoords` -> set the units of the annotation location
76+
- `xycoords` -> set the units of the point location
77+
- `set_position()` -> `Annotation` only set location of annotation
78+
79+
6580
.. _changes_in_1_3:
6681

6782

lib/matplotlib/offsetbox.py

+36-18
Original file line numberDiff line numberDiff line change
@@ -1265,6 +1265,10 @@ def __init__(self, offsetbox, xy,
12651265

12661266
self.set_fontsize(fontsize)
12671267

1268+
self.xybox = xybox
1269+
1270+
self.boxcoords = boxcoords
1271+
12681272
if arrowprops is not None:
12691273
self._arrow_relpos = self.arrowprops.pop("relpos", (0.5, 0.5))
12701274
self.arrow_patch = FancyArrowPatch((0, 0), (1, 1),
@@ -1274,8 +1278,8 @@ def __init__(self, offsetbox, xy,
12741278
self.arrow_patch = None
12751279

12761280
_AnnotationBase.__init__(self,
1277-
xy, xytext=xybox,
1278-
xycoords=xycoords, textcoords=boxcoords,
1281+
xy,
1282+
xycoords=xycoords,
12791283
annotation_clip=annotation_clip)
12801284

12811285
martist.Artist.__init__(self, **kwargs)
@@ -1295,6 +1299,22 @@ def __init__(self, offsetbox, xy,
12951299
self.patch.set(**bboxprops)
12961300
self._drawFrame = frameon
12971301

1302+
@property
1303+
def xyann(self):
1304+
return self.xybox
1305+
1306+
@xyann.setter
1307+
def xyann(self, xyann):
1308+
self.xybox = xyann
1309+
1310+
@property
1311+
def anncoords(self):
1312+
return self.boxcoords
1313+
1314+
@anncoords.setter
1315+
def anncoords(self, coords):
1316+
self.boxcoords = coords
1317+
12981318
def contains(self, event):
12991319
t, tinfo = self.offsetbox.contains(event)
13001320
#if self.arrow_patch is not None:
@@ -1352,14 +1372,14 @@ def _update_position_xybox(self, renderer, xy_pixel):
13521372
patch.
13531373
"""
13541374

1355-
x, y = self.xytext
1356-
if isinstance(self.textcoords, tuple):
1357-
xcoord, ycoord = self.textcoords
1375+
x, y = self.xybox
1376+
if isinstance(self.boxcoords, tuple):
1377+
xcoord, ycoord = self.boxcoords
13581378
x1, y1 = self._get_xy(renderer, x, y, xcoord)
13591379
x2, y2 = self._get_xy(renderer, x, y, ycoord)
13601380
ox0, oy0 = x1, y2
13611381
else:
1362-
ox0, oy0 = self._get_xy(renderer, x, y, self.textcoords)
1382+
ox0, oy0 = self._get_xy(renderer, x, y, self.boxcoords)
13631383

13641384
w, h, xd, yd = self.offsetbox.get_extent(renderer)
13651385

@@ -1579,32 +1599,30 @@ def __init__(self, annotation, use_blit=False):
15791599

15801600
def save_offset(self):
15811601
ann = self.annotation
1582-
x, y = ann.xytext
1583-
if isinstance(ann.textcoords, tuple):
1584-
xcoord, ycoord = ann.textcoords
1602+
x, y = ann.xyann
1603+
if isinstance(ann.anncoords, tuple):
1604+
xcoord, ycoord = ann.anncoords
15851605
x1, y1 = ann._get_xy(self.canvas.renderer, x, y, xcoord)
15861606
x2, y2 = ann._get_xy(self.canvas.renderer, x, y, ycoord)
15871607
ox0, oy0 = x1, y2
15881608
else:
1589-
ox0, oy0 = ann._get_xy(self.canvas.renderer, x, y, ann.textcoords)
1609+
ox0, oy0 = ann._get_xy(self.canvas.renderer, x, y, ann.anncoords)
15901610

15911611
self.ox, self.oy = ox0, oy0
1592-
self.annotation.textcoords = "figure pixels"
1612+
self.annotation.anncoords = "figure pixels"
15931613
self.update_offset(0, 0)
15941614

15951615
def update_offset(self, dx, dy):
15961616
ann = self.annotation
1597-
ann.xytext = self.ox + dx, self.oy + dy
1598-
x, y = ann.xytext
1599-
# xy is never used
1600-
xy = ann._get_xy(self.canvas.renderer, x, y, ann.textcoords)
1617+
ann.xyann = self.ox + dx, self.oy + dy
1618+
x, y = ann.xyann
16011619

16021620
def finalize_offset(self):
1603-
loc_in_canvas = self.annotation.xytext
1604-
self.annotation.textcoords = "axes fraction"
1621+
loc_in_canvas = self.annotation.xyann
1622+
self.annotation.anncoords = "axes fraction"
16051623
pos_axes_fraction = self.annotation.axes.transAxes.inverted()
16061624
pos_axes_fraction = pos_axes_fraction.transform_point(loc_in_canvas)
1607-
self.annotation.xytext = tuple(pos_axes_fraction)
1625+
self.annotation.xyann = tuple(pos_axes_fraction)
16081626

16091627

16101628
if __name__ == "__main__":

lib/matplotlib/tests/test_text.py

+78-46
Original file line numberDiff line numberDiff line change
@@ -21,60 +21,60 @@ def find_matplotlib_font(**kw):
2121
return FontProperties(fname=path)
2222

2323
from matplotlib.font_manager import FontProperties, findfont
24-
warnings.filterwarnings('ignore','findfont: Font family \[\'Foo\'\] '+ \
24+
warnings.filterwarnings('ignore', 'findfont: Font family \[\'Foo\'\] '+ \
2525
'not found. Falling back to .',
2626
UserWarning,
2727
module='matplotlib.font_manager')
2828
fig = plt.figure()
29-
ax = plt.subplot( 1, 1, 1 )
30-
31-
normalFont = find_matplotlib_font( family = "sans-serif",
32-
style = "normal",
33-
variant = "normal",
34-
size = 14,
35-
)
36-
ax.annotate( "Normal Font", (0.1, 0.1), xycoords='axes fraction',
37-
fontproperties = normalFont )
38-
39-
boldFont = find_matplotlib_font( family = "Foo",
40-
style = "normal",
41-
variant = "normal",
42-
weight = "bold",
43-
stretch = 500,
44-
size = 14,
29+
ax = plt.subplot(1, 1, 1)
30+
31+
normalFont = find_matplotlib_font(family="sans-serif",
32+
style="normal",
33+
variant="normal",
34+
size=14,
35+
)
36+
ax.annotate("Normal Font", (0.1, 0.1), xycoords='axes fraction',
37+
fontproperties=normalFont)
38+
39+
boldFont = find_matplotlib_font(family="Foo",
40+
style="normal",
41+
variant="normal",
42+
weight="bold",
43+
stretch=500,
44+
size=14,
4545
)
46-
ax.annotate( "Bold Font", (0.1, 0.2), xycoords='axes fraction',
47-
fontproperties = boldFont )
48-
49-
boldItemFont = find_matplotlib_font( family = "sans serif",
50-
style = "italic",
51-
variant = "normal",
52-
weight = 750,
53-
stretch = 500,
54-
size = 14,
46+
ax.annotate("Bold Font", (0.1, 0.2), xycoords='axes fraction',
47+
fontproperties=boldFont)
48+
49+
boldItemFont = find_matplotlib_font(family="sans serif",
50+
style="italic",
51+
variant="normal",
52+
weight=750,
53+
stretch=500,
54+
size=14,
5555
)
56-
ax.annotate( "Bold Italic Font", (0.1, 0.3), xycoords='axes fraction',
57-
fontproperties = boldItemFont )
58-
59-
lightFont = find_matplotlib_font( family = "sans-serif",
60-
style = "normal",
61-
variant = "normal",
62-
weight = 200,
63-
stretch = 500,
64-
size = 14,
56+
ax.annotate("Bold Italic Font", (0.1, 0.3), xycoords='axes fraction',
57+
fontproperties=boldItemFont)
58+
59+
lightFont = find_matplotlib_font(family="sans-serif",
60+
style="normal",
61+
variant="normal",
62+
weight=200,
63+
stretch=500,
64+
size=14,
6565
)
66-
ax.annotate( "Light Font", (0.1, 0.4), xycoords='axes fraction',
67-
fontproperties = lightFont )
68-
69-
condensedFont = find_matplotlib_font( family = "sans-serif",
70-
style = "normal",
71-
variant = "normal",
72-
weight = 500,
73-
stretch = 100,
74-
size = 14,
66+
ax.annotate("Light Font", (0.1, 0.4), xycoords='axes fraction',
67+
fontproperties=lightFont)
68+
69+
condensedFont = find_matplotlib_font(family="sans-serif",
70+
style="normal",
71+
variant="normal",
72+
weight=500,
73+
stretch=100,
74+
size=14,
7575
)
76-
ax.annotate( "Condensed Font", (0.1, 0.5), xycoords='axes fraction',
77-
fontproperties = condensedFont )
76+
ax.annotate("Condensed Font", (0.1, 0.5), xycoords='axes fraction',
77+
fontproperties=condensedFont)
7878

7979
ax.set_xticks([])
8080
ax.set_yticks([])
@@ -101,6 +101,7 @@ def test_multiline():
101101
ax.set_xticks([])
102102
ax.set_yticks([])
103103

104+
104105
@image_comparison(baseline_images=['antialiased'], extensions=['png'])
105106
def test_antialiasing():
106107
matplotlib.rcParams['text.antialiased'] = True
@@ -193,3 +194,34 @@ def test_alignment():
193194
ax.set_ylim([0, 1.5])
194195
ax.set_xticks([])
195196
ax.set_yticks([])
197+
198+
199+
@cleanup
200+
def test_set_position():
201+
fig, ax = plt.subplots()
202+
203+
# test set_position
204+
ann = ax.annotate('test', (0, 0), xytext=(0, 0), textcoords='figure pixels')
205+
plt.draw()
206+
207+
init_pos = ann.get_window_extent(fig.canvas.renderer)
208+
shift_val = 15
209+
ann.set_position((shift_val, shift_val))
210+
plt.draw()
211+
post_pos = ann.get_window_extent(fig.canvas.renderer)
212+
213+
for a, b in zip(init_pos.min, post_pos.min):
214+
assert a + shift_val == b
215+
216+
# test xyann
217+
ann = ax.annotate('test', (0, 0), xytext=(0, 0), textcoords='figure pixels')
218+
plt.draw()
219+
220+
init_pos = ann.get_window_extent(fig.canvas.renderer)
221+
shift_val = 15
222+
ann.xyann = (shift_val, shift_val)
223+
plt.draw()
224+
post_pos = ann.get_window_extent(fig.canvas.renderer)
225+
226+
for a, b in zip(init_pos.min, post_pos.min):
227+
assert a + shift_val == b

0 commit comments

Comments
 (0)