18

What I want to do:

I want to get the position and dimensions of a text instance in matplotlib world units (not screen pixels), with the intention of calculating and preventing text overlaps.

I'm developing on Mac OSX 10.9.3, Python 2.7.5, matplotlib 1.3.1.

What I've tried:

Let t be a text instance.

  1. t.get_window_extent(renderer):

    This gets bounding box dimensions in pixels, and I need world coordinates (normalized between -1.0 and 1.0 in my case).

  2. t._get_bbox_patch():

    t = ax.text(x, y, text_string, prop_dict, bbox=dict(facecolor='red', alpha=0.5, boxstyle='square'))
    print t._get_bbox_patch()
    

    When I execute the above sequence, the output is FancyBboxPatchFancyBboxPatch(0,0;1x1). In the image I produce, the text instance is rendered properly with a red bounding box, so that output leads me to think that the FancyBbox is instantiated but not actually populated with real dimensions until render time.

So, how can I get the position and dimensions of the text instance's bounding box in the same coord system units that I used for the x and y parameters I passed to ax.text(...)?

2 Answers 2

19

This may help a bit.

import matplotlib.pyplot as plt

f = plt.figure()
ax = f.add_subplot(111)
ax.plot([0,10], [4,0])
t = ax.text(3.2, 2.1, "testing...")

# get the inverse of the transformation from data coordinates to pixels
transf = ax.transData.inverted()
bb = t.get_window_extent(renderer = f.canvas.renderer)
bb_datacoords = bb.transformed(transf)

# Bbox('array([[ 3.2       ,  2.1       ],\n       [ 4.21607125,  2.23034396]])')

This should give what you want. If you want to have the coordinates in terms of figure coordinates (0..1,0..1), then use the inverse of ax.transAxes.

However, there is a small catch in this solution. An excerpt from the matplotlib documentation:

Any Text instance can report its extent in window coordinates (a negative x coordinate is outside the window), but there is a rub.

The RendererBase instance, which is used to calculate the text size, is not known until the figure is drawn (draw()). After the window is drawn and the text instance knows its renderer, you can call get_window_extent().

So, before the figure is really drawn, there seems to be no way to find out the text size.

BTW, you may have noticed that the Bbox instances have method overlaps which may be used to find out whether the Bbox overlaps with another one (bb1.overlaps(bb2)). This may be useful in some cases, but it does not answer the question "how much".

If you have rotated texts, you will have hard time seeing if they overlap, but that you probably already know.

Sign up to request clarification or add additional context in comments.

2 Comments

That worked perfectly to me, the only gotcha is that I had to use f.canvas.get_renderer() instead of f.canvas.renderer.
I have noticed this solution will not work if your text is partially outside the axes bounds before drawing the text - for example: plt.plot([1,2,3], [2,3,4]), t = plt.text(3, 3.1, "my text", fontsize=18). (it will never seem to get the right bounds in proper data coordinates) - is there a way to estimate bounds of text such that you can always ensure they are within the x/ylims of the axes after drawing?
6

Little late, but here is other example, which shows how to get bounding box of a text object in data coordinates/units. It also draws the bounding box obtained around the text for its visual representation.

import matplotlib.pyplot as plt

# some example plot
plt.plot([1,2,3], [2,3,4])

t = plt.text(1.1, 3.1, "my text", fontsize=18)

# to get the text bounding box 
# we need to draw the plot
plt.gcf().canvas.draw()

# get bounding box of the text 
# in the units of the data
bbox = t.get_window_extent().transformed(plt.gca().transData.inverted())

print(bbox)
# prints: Bbox(x0=1.1, y0=3.0702380952380954, x1=1.5296875, y1=3.2130952380952382)

# plot the bounding box around the text
plt.plot([bbox.x0, bbox.x0, bbox.x1, bbox.x1, bbox.x0],
         [bbox.y0, bbox.y1, bbox.y1, bbox.y0, bbox.y0])

plt.show()

enter image description here

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.