Getting the size of Text objects

Hi all, Is there a way to get the size of a text object? I

    > can't seem to find a method that does that. The
    > functionality must be in there somewhere, or having
    > different reference points wouldn't work.

The size of a text object is tricky. Do you mean the width and height
in points? Or in data coords? The x,y location of text is in one
coordinate system (axes, figure or data), but the width and height are
not. To convert between coordinate systems (eg points or display
versus data) the way mpl does it is to one transform coord system to
display and use the other coordinate system to inverse transform.
This would enable you to get, for example, a text bounding box in data
coords, but could be screwed up by a figure resize.

If you tell me more precisely what you are trying to achieve, I might
be able to help you or think about design changes to accommodate it.
FYI, this is an issue that crops up a lot and is vexing. What one
would like to be able to do is use a layout engine and say, place
object one above and to the right of object 2 with a pad of 2 points.

The text instance can give you its bounding box in display if you pass
it the backend renderer -- this is required because the width and
height can be backend dependent. I suppose you are using OO agg based
on your previous posts. One problem with the current design that is
that the agg canvas doesn't generate it's renderer until draw time
(with caching), but you need access to the renderer before draw time
for layout that depends on text. If we move this logic to a
get_renderer method, you can use it at draw time. I'll attach a
replacement backend_agg.FigureCanvasAgg class to support this below

    from matplotlib.figure import Figure
    from matplotlib.backends.backend_agg import FigureCanvasAgg

    fig = Figure()
    canvas = FigureCanvasAgg(fig)
    renderer = canvas.get_renderer()
    ax = fig.add_subplot(111)
    ax.plot([1,2,3])
    t = ax.text(1,2,'hi mom')
    bbox = t.get_window_extent(renderer)
    print 'display', bbox.get_bounds() #l,b,w,h

    # get the axes data coords bbox of this display bounding box
    from matplotlib.transforms import inverse_transform_bbox
    axbox = inverse_transform_bbox(ax.transData, bbox)

    print 'data coords', axbox.get_bounds()

    fig.savefig('test')

In backend_agg.py FigureCanvasAgg, replace the draw method with the following 2
methods

    def draw(self):
        """
        Draw the figure using the renderer
        """
        if __debug__: verbose.report('FigureCanvasAgg.draw', 'debug-annoying')

        renderer = self.get_renderer()
        self.figure.draw(renderer)

    def get_renderer(self):
        l,b,w,h = self.figure.bbox.get_bounds()
        key = w, h, self.figure.dpi.get()
        try: self._lastKey, self.renderer
        except AttributeError: need_new_renderer = True
        else: need_new_renderer = (self._lastKey != key)

        if need_new_renderer:
            self.renderer = RendererAgg(w, h, self.figure.dpi)
            self._lastKey = key
        return self.renderer

Hope this gets you started -- if you provide more details we maybe
able to improve from here.

JDG

John,

Thanks for the reply. Sorry for the delay, I've been off line for a few days.

John Hunter wrote:

"Chris" == Chris Barker <Chris.Barker@...259...> writes:

    > Hi all, Is there a way to get the size of a text object?

Do you mean the width and height
in points?

At this point, points. The other option is figure coordinates. The problem at hand is that we're generating a figure that has some fairly large labels on the Y axis, so we're having to do the layout of subplots ourselves. We've got it looking OK with trial and error, but if we change a label, it might not layout right anymore, so I thought it would be helpful to be able to check the sizes of things to do it automagically.

Besides, aren't there methods to convert between the various coord. systems anyway?

but could be screwed up by a figure resize.

Yeah, it would , but right now I'm working with the AGG backend, so I can stick with one figure size.

FYI, this is an issue that crops up a lot and is vexing. What one
would like to be able to do is use a layout engine and say, place
object one above and to the right of object 2 with a pad of 2 points.

Yes, that would be nice. In fact, I've been planning to do exactly that for my wxFloatCanvas. It's easier there because I've only got wx as a back end, but still a bit tricky. One thing I've done to help is simply declare that I'm always assuming 72dpi, rather than trying to use the system's idea of the display resolution. This has helped keep results consistent across systems. I have had to re-scale text, however.

The text instance can give you its bounding box in display if you pass
it the backend renderer -- this is required because the width and
height can be backend dependent. I suppose you are using OO agg based
on your previous posts.

Mostly, it's a mix as I'm working with someone else and most of the examples are in the pylab interface.

One problem with the current design that is
that the agg canvas doesn't generate it's renderer until draw time
(with caching), but you need access to the renderer before draw time
for layout that depends on text.

Right. So, do any of the default layout's take text into account (subplot, for example?)

> If we move this logic to a

get_renderer method, you can use it at draw time. I'll attach a
replacement backend_agg.FigureCanvasAgg class to support this below

    from matplotlib.figure import Figure
    from matplotlib.backends.backend_agg import FigureCanvasAgg

    fig = Figure()
    canvas = FigureCanvasAgg(fig)

Didn't you add canvas as a Figure() attribute to support the new Figure.savefig() method? (added for me, such service!)

    renderer = canvas.get_renderer()
    ax = fig.add_subplot(111)
    ax.plot([1,2,3])
    t = ax.text(1,2,'hi mom')
    bbox = t.get_window_extent(renderer)
    print 'display', bbox.get_bounds() #l,b,w,h

    # get the axes data coords bbox of this display bounding box
    from matplotlib.transforms import inverse_transform_bbox
    axbox = inverse_transform_bbox(ax.transData, bbox)

    print 'data coords', axbox.get_bounds()

    fig.savefig('test')

Thanks for this example. I hope I"ll get a chance to play with this soon. My schedule is a nightmare for a while.

thanks,

-Chris

···

--
Christopher Barker, Ph.D.
Oceanographer
                                         
NOAA/OR&R/HAZMAT (206) 526-6959 voice
7600 Sand Point Way NE (206) 526-6329 fax
Seattle, WA 98115 (206) 526-6317 main reception

Chris.Barker@...259...