Skip to content

[WIP] Fix annotation get window extent #4023

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
287 changes: 207 additions & 80 deletions lib/matplotlib/tests/test_text.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
from __future__ import (absolute_import, division, print_function,
unicode_literals)
from __future__ import (
absolute_import, division, print_function, unicode_literals)

import six
import warnings

import numpy as np
from numpy.testing import assert_almost_equal
from nose import SkipTest
from nose.tools import assert_raises, eq_

import matplotlib
from matplotlib.testing.decorators import image_comparison, knownfailureif, cleanup
import matplotlib.pyplot as plt
import warnings
from nose import SkipTest
from nose.tools import with_setup, assert_raises
from matplotlib.testing.decorators import image_comparison, cleanup
from matplotlib.figure import Figure
from matplotlib.text import Annotation, Text
from matplotlib.backends.backend_agg import RendererAgg


@image_comparison(baseline_images=['font_styles'])
Expand All @@ -24,79 +28,98 @@ def find_matplotlib_font(**kw):
return FontProperties(fname=path)

from matplotlib.font_manager import FontProperties, findfont
warnings.filterwarnings('ignore', ('findfont: Font family \[u?\'Foo\'\] '+
'not found. Falling back to .'),
UserWarning,
module='matplotlib.font_manager')
fig = plt.figure()
warnings.filterwarnings(
'ignore',
('findfont: Font family \[u?\'Foo\'\] not found. Falling back to .'),
UserWarning,
module='matplotlib.font_manager')

plt.figure()
ax = plt.subplot(1, 1, 1)

normalFont = find_matplotlib_font(family="sans-serif",
style="normal",
variant="normal",
size=14,
)
ax.annotate("Normal Font", (0.1, 0.1), xycoords='axes fraction',
fontproperties=normalFont)

boldFont = find_matplotlib_font(family="Foo",
style="normal",
variant="normal",
weight="bold",
stretch=500,
size=14,
)
ax.annotate("Bold Font", (0.1, 0.2), xycoords='axes fraction',
fontproperties=boldFont)

boldItemFont = find_matplotlib_font(family="sans serif",
style="italic",
variant="normal",
weight=750,
stretch=500,
size=14,
)
ax.annotate("Bold Italic Font", (0.1, 0.3), xycoords='axes fraction',
fontproperties=boldItemFont)

lightFont = find_matplotlib_font(family="sans-serif",
style="normal",
variant="normal",
weight=200,
stretch=500,
size=14,
)
ax.annotate("Light Font", (0.1, 0.4), xycoords='axes fraction',
fontproperties=lightFont)

condensedFont = find_matplotlib_font(family="sans-serif",
style="normal",
variant="normal",
weight=500,
stretch=100,
size=14,
)
ax.annotate("Condensed Font", (0.1, 0.5), xycoords='axes fraction',
fontproperties=condensedFont)
normalFont = find_matplotlib_font(
family="sans-serif",
style="normal",
variant="normal",
size=14)
ax.annotate(
"Normal Font",
(0.1, 0.1),
xycoords='axes fraction',
fontproperties=normalFont)

boldFont = find_matplotlib_font(
family="Foo",
style="normal",
variant="normal",
weight="bold",
stretch=500,
size=14)
ax.annotate(
"Bold Font",
(0.1, 0.2),
xycoords='axes fraction',
fontproperties=boldFont)

boldItemFont = find_matplotlib_font(
family="sans serif",
style="italic",
variant="normal",
weight=750,
stretch=500,
size=14)
ax.annotate(
"Bold Italic Font",
(0.1, 0.3),
xycoords='axes fraction',
fontproperties=boldItemFont)

lightFont = find_matplotlib_font(
family="sans-serif",
style="normal",
variant="normal",
weight=200,
stretch=500,
size=14)
ax.annotate(
"Light Font",
(0.1, 0.4),
xycoords='axes fraction',
fontproperties=lightFont)

condensedFont = find_matplotlib_font(
family="sans-serif",
style="normal",
variant="normal",
weight=500,
stretch=100,
size=14)
ax.annotate(
"Condensed Font",
(0.1, 0.5),
xycoords='axes fraction',
fontproperties=condensedFont)

ax.set_xticks([])
ax.set_yticks([])


@image_comparison(baseline_images=['multiline'])
def test_multiline():
fig = plt.figure()
plt.figure()
ax = plt.subplot(1, 1, 1)
ax.set_title("multiline\ntext alignment")

plt.text(0.2, 0.5, "TpTpTp\n$M$\nTpTpTp", size=20,
ha="center", va="top")
plt.text(
0.2, 0.5, "TpTpTp\n$M$\nTpTpTp", size=20, ha="center", va="top")

plt.text(0.5, 0.5, "TpTpTp\n$M^{M^{M^{M}}}$\nTpTpTp", size=20,
ha="center", va="top")
plt.text(
0.5, 0.5, "TpTpTp\n$M^{M^{M^{M}}}$\nTpTpTp", size=20,
ha="center", va="top")

plt.text(0.8, 0.5, "TpTpTp\n$M_{q_{q_{q}}}$\nTpTpTp", size=20,
ha="center", va="top")
plt.text(
0.8, 0.5, "TpTpTp\n$M_{q_{q_{q}}}$\nTpTpTp", size=20,
ha="center", va="top")

plt.xlim(0, 1)
plt.ylim(0, 0.8)
Expand Down Expand Up @@ -137,15 +160,15 @@ def test_contains():
fig = plt.figure()
ax = plt.axes()

mevent = mbackend.MouseEvent('button_press_event', fig.canvas, 0.5,
0.5, 1, None)
mevent = mbackend.MouseEvent(
'button_press_event', fig.canvas, 0.5, 0.5, 1, None)

xs = np.linspace(0.25, 0.75, 30)
ys = np.linspace(0.25, 0.75, 30)
xs, ys = np.meshgrid(xs, ys)

txt = plt.text(0.48, 0.52, 'hello world', ha='center', fontsize=30,
rotation=30)
txt = plt.text(
0.48, 0.52, 'hello world', ha='center', fontsize=30, rotation=30)
# uncomment to draw the text's bounding box
# txt.set_bbox(dict(edgecolor='black', facecolor='none'))

Expand All @@ -155,9 +178,7 @@ def test_contains():

for x, y in zip(xs.flat, ys.flat):
mevent.x, mevent.y = plt.gca().transAxes.transform_point([x, y])

contains, _ = txt.contains(mevent)

color = 'yellow' if contains else 'red'

# capture the viewLim, plot a point, and reset the viewLim
Expand All @@ -169,7 +190,7 @@ def test_contains():
@image_comparison(baseline_images=['titles'])
def test_titles():
# left and right side titles
fig = plt.figure()
plt.figure()
ax = plt.subplot(1, 1, 1)
ax.set_title("left title", loc="left")
ax.set_title("right title", loc="right")
Expand All @@ -179,15 +200,17 @@ def test_titles():

@image_comparison(baseline_images=['text_alignment'])
def test_alignment():
fig = plt.figure()
plt.figure()
ax = plt.subplot(1, 1, 1)

x = 0.1
for rotation in (0, 30):
for alignment in ('top', 'bottom', 'baseline', 'center'):
ax.text(x, 0.5, alignment + " Tj", va=alignment, rotation=rotation,
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))
ax.text(x, 1.0, r'$\sum_{i=0}^{j}$', va=alignment, rotation=rotation)
ax.text(
x, 0.5, alignment + " Tj", va=alignment, rotation=rotation,
bbox=dict(boxstyle='round', facecolor='wheat', alpha=0.5))
ax.text(
x, 1.0, r'$\sum_{i=0}^{j}$', va=alignment, rotation=rotation)
x += 0.1

ax.plot([0, 1], [0.5, 0.5])
Expand All @@ -202,8 +225,8 @@ def test_alignment():
@image_comparison(baseline_images=['axes_titles'], extensions=['png'])
def test_axes_titles():
# Related to issue #3327
fig = plt.figure()
ax = plt.subplot(1,1,1)
plt.figure()
ax = plt.subplot(1, 1, 1)
ax.set_title('center', loc='center', fontsize=20, fontweight=700)
ax.set_title('left', loc='left', fontsize=12, fontweight=400)
ax.set_title('right', loc='right', fontsize=12, fontweight=400)
Expand All @@ -214,7 +237,8 @@ def test_set_position():
fig, ax = plt.subplots()

# test set_position
ann = ax.annotate('test', (0, 0), xytext=(0, 0), textcoords='figure pixels')
ann = ax.annotate(
'test', (0, 0), xytext=(0, 0), textcoords='figure pixels')
plt.draw()

init_pos = ann.get_window_extent(fig.canvas.renderer)
Expand All @@ -227,7 +251,8 @@ def test_set_position():
assert a + shift_val == b

# test xyann
ann = ax.annotate('test', (0, 0), xytext=(0, 0), textcoords='figure pixels')
ann = ax.annotate(
'test', (0, 0), xytext=(0, 0), textcoords='figure pixels')
plt.draw()

init_pos = ann.get_window_extent(fig.canvas.renderer)
Expand Down Expand Up @@ -279,8 +304,110 @@ def test_get_rotation_mod360():
for i, j in zip([360., 377., 720+177.2], [0., 17., 177.2]):
assert_almost_equal(text.get_rotation(i), j)


@image_comparison(baseline_images=['text_bboxclip'])
def test_bbox_clipping():
plt.text(0.9, 0.2, 'Is bbox clipped?', backgroundcolor='r', clip_on=True)
t = plt.text(0.9, 0.5, 'Is fancy bbox clipped?', clip_on=True)
t.set_bbox({"boxstyle": "round, pad=0.1"})


def test_text_annotation_get_window_extent():
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is easier to add the @cleanup decorator use pyplot to make your figure/axes objects.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe, I now see why you are doing it this way.

figure = Figure(dpi=100)
renderer = RendererAgg(200, 200, 100)

# Only text annotation
annotation = Annotation('test', xy=(0, 0))
annotation.set_figure(figure)

text = Text(text='test', x=0, y=0)
text.set_figure(figure)

bbox = annotation.get_window_extent(renderer=renderer)

text_bbox = text.get_window_extent(renderer=renderer)
eq_(bbox.width, text_bbox.width)
eq_(bbox.height, text_bbox.height)

_, _, d = renderer.get_text_width_height_descent(
'text', annotation._fontproperties, ismath=False)
_, _, lp_d = renderer.get_text_width_height_descent(
'lp', annotation._fontproperties, ismath=False)
below_line = max(d, lp_d)

# These numbers are specific to the current implementation of Text
points = bbox.get_points()
eq_(points[0, 0], 0.0)
eq_(points[1, 0], text_bbox.width)
eq_(points[0, 1], -below_line)
eq_(points[1, 1], text_bbox.height - below_line)


def test_text_with_arrow_annotation_get_window_extent():
figure = Figure(dpi=100)
renderer = RendererAgg(600, 600, 100)
headwidth = 21

text = Text(text='test', x=0, y=0)
text.set_figure(figure)
text_bbox = text.get_window_extent(renderer=renderer)

# Text annotation with arrow
annotation = Annotation(
'test',
xy=(0.0, 50.0 + (headwidth / 0.72) * 0.5),
xytext=(50.0, 50.0), xycoords='figure pixels',
arrowprops={
'facecolor': 'black', 'width': 2,
'headwidth': headwidth, 'shrink': 0.0})
annotation.set_figure(figure)
annotation.draw(renderer)

bbox = annotation.get_window_extent(renderer=renderer)
eq_(bbox.width, text_bbox.width + 50.0)
expected_height = max(text_bbox.height, headwidth / 0.72)
assert_almost_equal(bbox.height, expected_height)


def test_arrow_annotation_get_window_extent():
figure = Figure(dpi=100)
figure.set_figwidth(2.0)
figure.set_figheight(2.0)
renderer = RendererAgg(200, 200, 100)

# Text annotation with arrow
annotation = Annotation(
'', xy=(0.0, 50.0), xytext=(50.0, 50.0), xycoords='figure pixels',
arrowprops={
'facecolor': 'black', 'width': 8, 'headwidth': 10, 'shrink': 0.0})
annotation.set_figure(figure)
annotation.draw(renderer)

bbox = annotation.get_window_extent()
points = bbox.get_points()

eq_(bbox.width, 50.0)
assert_almost_equal(bbox.height, 10.0 / 0.72)
eq_(points[0, 0], 0.0)
eq_(points[0, 1], 50.0 - 5 / 0.72)


def test_empty_annotation_get_window_extent():
figure = Figure(dpi=100)
figure.set_figwidth(2.0)
figure.set_figheight(2.0)
renderer = RendererAgg(200, 200, 100)

# Text annotation with arrow
annotation = Annotation(
'', xy=(0.0, 50.0), xytext=(0.0, 50.0), xycoords='figure pixels')
annotation.set_figure(figure)
annotation.draw(renderer)

bbox = annotation.get_window_extent()
points = bbox.get_points()

eq_(points[0, 0], 0.0)
eq_(points[1, 0], 0.0)
eq_(points[1, 1], 50.0)
eq_(points[0, 1], 50.0)
Loading