22 unicode_literals )
33
44import six
5+ import warnings
56
67import numpy as np
8+ from numpy .testing import assert_almost_equal
9+ from nose .tools import eq_
10+
11+ from matplotlib .transforms import Bbox
712import matplotlib
8- from matplotlib .testing .decorators import image_comparison , knownfailureif , cleanup
913import matplotlib .pyplot as plt
10- import warnings
11- from nose .tools import with_setup
14+ from matplotlib .testing .decorators import image_comparison , cleanup
15+ from matplotlib .figure import Figure
16+ from matplotlib .text import Annotation , Text
17+ from matplotlib .backends .backend_agg import RendererAgg
1218
1319
1420@image_comparison (baseline_images = ['font_styles' ])
@@ -22,79 +28,98 @@ def find_matplotlib_font(**kw):
2228 return FontProperties (fname = path )
2329
2430 from matplotlib .font_manager import FontProperties , findfont
25- warnings .filterwarnings ('ignore' , ('findfont: Font family \[u?\' Foo\' \] ' +
26- 'not found. Falling back to .' ),
27- UserWarning ,
28- module = 'matplotlib.font_manager' )
29- fig = plt .figure ()
31+ warnings .filterwarnings (
32+ 'ignore' ,
33+ ('findfont: Font family \[u?\' Foo\' \] not found. Falling back to .' ),
34+ UserWarning ,
35+ module = 'matplotlib.font_manager' )
36+
37+ plt .figure ()
3038 ax = plt .subplot (1 , 1 , 1 )
3139
32- normalFont = find_matplotlib_font (family = "sans-serif" ,
33- style = "normal" ,
34- variant = "normal" ,
35- size = 14 ,
36- )
37- ax .annotate ("Normal Font" , (0.1 , 0.1 ), xycoords = 'axes fraction' ,
38- fontproperties = normalFont )
39-
40- boldFont = find_matplotlib_font (family = "Foo" ,
41- style = "normal" ,
42- variant = "normal" ,
43- weight = "bold" ,
44- stretch = 500 ,
45- size = 14 ,
46- )
47- ax .annotate ("Bold Font" , (0.1 , 0.2 ), xycoords = 'axes fraction' ,
48- fontproperties = boldFont )
49-
50- boldItemFont = find_matplotlib_font (family = "sans serif" ,
51- style = "italic" ,
52- variant = "normal" ,
53- weight = 750 ,
54- stretch = 500 ,
55- size = 14 ,
56- )
57- ax .annotate ("Bold Italic Font" , (0.1 , 0.3 ), xycoords = 'axes fraction' ,
58- fontproperties = boldItemFont )
59-
60- lightFont = find_matplotlib_font (family = "sans-serif" ,
61- style = "normal" ,
62- variant = "normal" ,
63- weight = 200 ,
64- stretch = 500 ,
65- size = 14 ,
66- )
67- ax .annotate ("Light Font" , (0.1 , 0.4 ), xycoords = 'axes fraction' ,
68- fontproperties = lightFont )
69-
70- condensedFont = find_matplotlib_font (family = "sans-serif" ,
71- style = "normal" ,
72- variant = "normal" ,
73- weight = 500 ,
74- stretch = 100 ,
75- size = 14 ,
76- )
77- ax .annotate ("Condensed Font" , (0.1 , 0.5 ), xycoords = 'axes fraction' ,
78- fontproperties = condensedFont )
40+ normalFont = find_matplotlib_font (
41+ family = "sans-serif" ,
42+ style = "normal" ,
43+ variant = "normal" ,
44+ size = 14 )
45+ ax .annotate (
46+ "Normal Font" ,
47+ (0.1 , 0.1 ),
48+ xycoords = 'axes fraction' ,
49+ fontproperties = normalFont )
50+
51+ boldFont = find_matplotlib_font (
52+ family = "Foo" ,
53+ style = "normal" ,
54+ variant = "normal" ,
55+ weight = "bold" ,
56+ stretch = 500 ,
57+ size = 14 )
58+ ax .annotate (
59+ "Bold Font" ,
60+ (0.1 , 0.2 ),
61+ xycoords = 'axes fraction' ,
62+ fontproperties = boldFont )
63+
64+ boldItemFont = find_matplotlib_font (
65+ family = "sans serif" ,
66+ style = "italic" ,
67+ variant = "normal" ,
68+ weight = 750 ,
69+ stretch = 500 ,
70+ size = 14 )
71+ ax .annotate (
72+ "Bold Italic Font" ,
73+ (0.1 , 0.3 ),
74+ xycoords = 'axes fraction' ,
75+ fontproperties = boldItemFont )
76+
77+ lightFont = find_matplotlib_font (
78+ family = "sans-serif" ,
79+ style = "normal" ,
80+ variant = "normal" ,
81+ weight = 200 ,
82+ stretch = 500 ,
83+ size = 14 )
84+ ax .annotate (
85+ "Light Font" ,
86+ (0.1 , 0.4 ),
87+ xycoords = 'axes fraction' ,
88+ fontproperties = lightFont )
89+
90+ condensedFont = find_matplotlib_font (
91+ family = "sans-serif" ,
92+ style = "normal" ,
93+ variant = "normal" ,
94+ weight = 500 ,
95+ stretch = 100 ,
96+ size = 14 )
97+ ax .annotate (
98+ "Condensed Font" ,
99+ (0.1 , 0.5 ),
100+ xycoords = 'axes fraction' ,
101+ fontproperties = condensedFont )
79102
80103 ax .set_xticks ([])
81104 ax .set_yticks ([])
82105
83106
84107@image_comparison (baseline_images = ['multiline' ])
85108def test_multiline ():
86- fig = plt .figure ()
109+ plt .figure ()
87110 ax = plt .subplot (1 , 1 , 1 )
88111 ax .set_title ("multiline\n text alignment" )
89112
90- plt .text (0.2 , 0.5 , "TpTpTp \n $M$ \n TpTpTp" , size = 20 ,
91- ha = "center" , va = "top" )
113+ plt .text (
114+ 0.2 , 0.5 , "TpTpTp \n $M$ \n TpTpTp" , size = 20 , ha = "center" , va = "top" )
92115
93- plt .text (0.5 , 0.5 , "TpTpTp\n $M^{M^{M^{M}}}$\n TpTpTp" , size = 20 ,
94- ha = "center" , va = "top" )
116+ plt .text (
117+ 0.5 , 0.5 , "TpTpTp\n $M^{M^{M^{M}}}$\n TpTpTp" , size = 20 ,
118+ ha = "center" , va = "top" )
95119
96- plt .text (0.8 , 0.5 , "TpTpTp\n $M_{q_{q_{q}}}$\n TpTpTp" , size = 20 ,
97- ha = "center" , va = "top" )
120+ plt .text (
121+ 0.8 , 0.5 , "TpTpTp\n $M_{q_{q_{q}}}$\n TpTpTp" , size = 20 ,
122+ ha = "center" , va = "top" )
98123
99124 plt .xlim (0 , 1 )
100125 plt .ylim (0 , 0.8 )
@@ -135,15 +160,15 @@ def test_contains():
135160 fig = plt .figure ()
136161 ax = plt .axes ()
137162
138- mevent = mbackend .MouseEvent ('button_press_event' , fig . canvas , 0.5 ,
139- 0.5 , 1 , None )
163+ mevent = mbackend .MouseEvent (
164+ 'button_press_event' , fig . canvas , 0.5 , 0.5 , 1 , None )
140165
141166 xs = np .linspace (0.25 , 0.75 , 30 )
142167 ys = np .linspace (0.25 , 0.75 , 30 )
143168 xs , ys = np .meshgrid (xs , ys )
144169
145- txt = plt .text (0.48 , 0.52 , 'hello world' , ha = 'center' , fontsize = 30 ,
146- rotation = 30 )
170+ txt = plt .text (
171+ 0.48 , 0.52 , 'hello world' , ha = 'center' , fontsize = 30 , rotation = 30 )
147172 # uncomment to draw the text's bounding box
148173 # txt.set_bbox(dict(edgecolor='black', facecolor='none'))
149174
@@ -153,9 +178,7 @@ def test_contains():
153178
154179 for x , y in zip (xs .flat , ys .flat ):
155180 mevent .x , mevent .y = plt .gca ().transAxes .transform_point ([x , y ])
156-
157181 contains , _ = txt .contains (mevent )
158-
159182 color = 'yellow' if contains else 'red'
160183
161184 # capture the viewLim, plot a point, and reset the viewLim
@@ -167,7 +190,7 @@ def test_contains():
167190@image_comparison (baseline_images = ['titles' ])
168191def test_titles ():
169192 # left and right side titles
170- fig = plt .figure ()
193+ plt .figure ()
171194 ax = plt .subplot (1 , 1 , 1 )
172195 ax .set_title ("left title" , loc = "left" )
173196 ax .set_title ("right title" , loc = "right" )
@@ -177,15 +200,17 @@ def test_titles():
177200
178201@image_comparison (baseline_images = ['text_alignment' ])
179202def test_alignment ():
180- fig = plt .figure ()
203+ plt .figure ()
181204 ax = plt .subplot (1 , 1 , 1 )
182205
183206 x = 0.1
184207 for rotation in (0 , 30 ):
185208 for alignment in ('top' , 'bottom' , 'baseline' , 'center' ):
186- ax .text (x , 0.5 , alignment + " Tj" , va = alignment , rotation = rotation ,
187- bbox = dict (boxstyle = 'round' , facecolor = 'wheat' , alpha = 0.5 ))
188- ax .text (x , 1.0 , r'$\sum_{i=0}^{j}$' , va = alignment , rotation = rotation )
209+ ax .text (
210+ x , 0.5 , alignment + " Tj" , va = alignment , rotation = rotation ,
211+ bbox = dict (boxstyle = 'round' , facecolor = 'wheat' , alpha = 0.5 ))
212+ ax .text (
213+ x , 1.0 , r'$\sum_{i=0}^{j}$' , va = alignment , rotation = rotation )
189214 x += 0.1
190215
191216 ax .plot ([0 , 1 ], [0.5 , 0.5 ])
@@ -200,8 +225,8 @@ def test_alignment():
200225@image_comparison (baseline_images = ['axes_titles' ], extensions = ['png' ])
201226def test_axes_titles ():
202227 # Related to issue #3327
203- fig = plt .figure ()
204- ax = plt .subplot (1 ,1 , 1 )
228+ plt .figure ()
229+ ax = plt .subplot (1 , 1 , 1 )
205230 ax .set_title ('center' , loc = 'center' , fontsize = 20 , fontweight = 700 )
206231 ax .set_title ('left' , loc = 'left' , fontsize = 12 , fontweight = 400 )
207232 ax .set_title ('right' , loc = 'right' , fontsize = 12 , fontweight = 400 )
@@ -212,7 +237,8 @@ def test_set_position():
212237 fig , ax = plt .subplots ()
213238
214239 # test set_position
215- ann = ax .annotate ('test' , (0 , 0 ), xytext = (0 , 0 ), textcoords = 'figure pixels' )
240+ ann = ax .annotate (
241+ 'test' , (0 , 0 ), xytext = (0 , 0 ), textcoords = 'figure pixels' )
216242 plt .draw ()
217243
218244 init_pos = ann .get_window_extent (fig .canvas .renderer )
@@ -225,7 +251,8 @@ def test_set_position():
225251 assert a + shift_val == b
226252
227253 # test xyann
228- ann = ax .annotate ('test' , (0 , 0 ), xytext = (0 , 0 ), textcoords = 'figure pixels' )
254+ ann = ax .annotate (
255+ 'test' , (0 , 0 ), xytext = (0 , 0 ), textcoords = 'figure pixels' )
229256 plt .draw ()
230257
231258 init_pos = ann .get_window_extent (fig .canvas .renderer )
@@ -260,3 +287,116 @@ def test_annotation_negative_coords():
260287 ax .annotate ("-fpx" , (- 45 , - 30 ), xycoords = "figure pixels" )
261288 ax .annotate ("-apt" , (- 35 , - 20 ), xycoords = "axes points" )
262289 ax .annotate ("-apx" , (- 25 , - 10 ), xycoords = "axes pixels" )
290+
291+
292+ @cleanup
293+ def test_text_annotation_get_window_extent ():
294+ figure = Figure (dpi = 100 )
295+ renderer = RendererAgg (200 , 200 , 100 )
296+
297+ # Only text annotation
298+ annotation = Annotation ('test' , xy = (0 , 0 ))
299+ annotation .set_figure (figure )
300+
301+ text = Text (text = 'test' , x = 0 , y = 0 )
302+ text .set_figure (figure )
303+
304+ bbox = annotation .get_window_extent (renderer = renderer )
305+
306+ text_bbox = text .get_window_extent (renderer = renderer )
307+ eq_ (bbox .width , text_bbox .width )
308+ eq_ (bbox .height , text_bbox .height )
309+
310+ _ , _ , d = renderer .get_text_width_height_descent (
311+ 'text' , annotation ._fontproperties , ismath = False )
312+ _ , _ , lp_d = renderer .get_text_width_height_descent (
313+ 'lp' , annotation ._fontproperties , ismath = False )
314+ below_line = max (d , lp_d )
315+
316+ # These numbers are specific to the current implementation of Text
317+ points = bbox .get_points ()
318+ eq_ (points [0 , 0 ], 0.0 )
319+ eq_ (points [1 , 0 ], text_bbox .width )
320+ eq_ (points [0 , 1 ], - below_line )
321+ eq_ (points [1 , 1 ], text_bbox .height - below_line )
322+
323+
324+ @cleanup
325+ def test_text_with_arrow_annotation_get_window_extent ():
326+ headwidth = 21
327+ fig , ax = plt .subplots (dpi = 100 )
328+ txt = ax .text (s = 'test' , x = 0 , y = 0 )
329+ ann = ax .annotate ('test' ,
330+ xy = (0.0 , 50.0 ),
331+ xytext = (50.0 , 50.0 ), xycoords = 'figure pixels' ,
332+ arrowprops = {
333+ 'facecolor' : 'black' , 'width' : 2 ,
334+ 'headwidth' : headwidth , 'shrink' : 0.0 })
335+
336+ plt .draw ()
337+ renderer = fig .canvas .renderer
338+ # bounding box of text
339+ text_bbox = txt .get_window_extent (renderer = renderer )
340+ # bounding box of annotation (text + arrow)
341+ bbox = ann .get_window_extent (renderer = renderer )
342+ # bounding box of arrow
343+ arrow_bbox = ann .arrow .get_window_extent (renderer )
344+ # bounding box of annotation text
345+ ann_txt_bbox = Text .get_window_extent (ann )
346+
347+ # make sure annotation with in 50 px wider than
348+ # just the text
349+ eq_ (bbox .width , text_bbox .width + 50.0 )
350+ # make sure the annotation text bounding box is same size
351+ # as the bounding box of the same string as a Text object
352+ eq_ (ann_txt_bbox .height , text_bbox .height )
353+ eq_ (ann_txt_bbox .width , text_bbox .width )
354+ # compute the expected bounding box of arrow + text
355+ expected_bbox = Bbox .union ([ann_txt_bbox , arrow_bbox ])
356+ assert_almost_equal (bbox .height , expected_bbox .height )
357+
358+
359+ @cleanup
360+ def test_arrow_annotation_get_window_extent ():
361+ figure = Figure (dpi = 100 )
362+ figure .set_figwidth (2.0 )
363+ figure .set_figheight (2.0 )
364+ renderer = RendererAgg (200 , 200 , 100 )
365+
366+ # Text annotation with arrow
367+ annotation = Annotation (
368+ '' , xy = (0.0 , 50.0 ), xytext = (50.0 , 50.0 ), xycoords = 'figure pixels' ,
369+ arrowprops = {
370+ 'facecolor' : 'black' , 'width' : 8 , 'headwidth' : 10 , 'shrink' : 0.0 })
371+ annotation .set_figure (figure )
372+ annotation .draw (renderer )
373+
374+ bbox = annotation .get_window_extent ()
375+ points = bbox .get_points ()
376+
377+ eq_ (bbox .width , 50.0 )
378+ assert_almost_equal (bbox .height , 10.0 / 0.72 )
379+ eq_ (points [0 , 0 ], 0.0 )
380+ eq_ (points [0 , 1 ], 50.0 - 5 / 0.72 )
381+
382+
383+ @cleanup
384+ def test_empty_annotation_get_window_extent ():
385+ figure = Figure (dpi = 100 )
386+ figure .set_figwidth (2.0 )
387+ figure .set_figheight (2.0 )
388+ renderer = RendererAgg (200 , 200 , 100 )
389+
390+ # Text annotation with arrow
391+ annotation = Annotation (
392+ '' , xy = (0.0 , 50.0 ), xytext = (0.0 , 50.0 ), xycoords = 'figure pixels' )
393+ annotation .set_figure (figure )
394+ annotation .draw (renderer )
395+
396+ bbox = annotation .get_window_extent ()
397+ points = bbox .get_points ()
398+
399+ eq_ (points [0 , 0 ], 0.0 )
400+ eq_ (points [1 , 0 ], 0.0 )
401+ eq_ (points [1 , 1 ], 50.0 )
402+ eq_ (points [0 , 1 ], 50.0 )
0 commit comments