[Matplotlib-users] text size From: Michael P. Mossey - 2006-03-17 00:41 ```Is there a way to find out in terms of pixels, points, or fraction of figure, how large a text string is? In other words if I generate "Some label" as the x-axis label, can I find out how tall and wide it is? Mike ```
 Re: [Matplotlib-users] text size From: John Hunter - 2006-03-18 03:53 ```>>>>> "Michael" == Michael P Mossey writes: Michael> Is there a way to find out in terms of pixels, points, or Michael> fraction of figure, how large a text string is? In other Michael> words if I generate "Some label" as the x-axis label, can Michael> I find out how tall and wide it is? It can be done -- it's not terribly elegant. The basic problem is that text size depends on the backend and configuration settings. Since we have arbitrary fonts, multiline text with arbitrary rotation, possible using TeX to render, with multiple output targets that might handle text differently, you can see how this seemingly simple question is actually tough. All text instances have a method called "get_window_extent" which returns a matplotlib.transforms.BBox that bounds the text instance. This bbox is in pixel coords. bbox = sometext.get_window_extent() l,b,w,h = bbox.get_bounds() # left, bottom, width, height Here's the rub: we can't realistically know the text size until we have a renderer, because the same text may be different in different renderers. matplotlib sets the figure renderer at draw time, and so by default we don't know the text size until draw size. This is sometimes cumbersome. One solution is to do something like plot(something) sometext = text(x,y,s) draw() # force a draw to set the renderer l,b,w,h = sometext.get_window_extent().get_bounds() ..adjust the postion of some objects based on this info ...draw again not terribly elegant because we must redraw. If you did not first force the draw command before calling get_window_extent, you would get an exception about needing to first set the renderer. Another approach if you want to avoid the duplicate drawing is to create a proxy renderer and pass that off to the text instance. l,b,w,h = fig.bbox.get_bounds() renderer = RendererAgg(w, h, fig.dpi) l,b,w,h = sometext.get_window_extent(renderer).get_bounds() This is not ideal either because you have a duplicate renderer. If you are using a *Agg backend and have a figure instance, you can do the following, which requires no duplicate drawing and no duplicate renderer (the get_renderer method below caches the return value so repeated calls do not create duplicate renderers) Here is a complete example from matplotlib.patches import Rectangle from matplotlib.transforms import identity_transform from pylab import figure, show fig = figure() ax = fig.add_subplot(111) ax.plot([1,2,3,4], [1,2,3,4]) t = ax.text(2,2,'Look Ma!\nNo hands', fontsize=40, rotation=-45) renderer = fig.canvas.get_renderer() bbox = t.get_window_extent(renderer) l,b,w,h = bbox.get_bounds() # no transformation necessary, already in pixel coords r = Rectangle((l,b),w,h, transform=identity_transform()) ax.add_patch(r) show() If we made the get_renderer method standard across backends, we could probably hide the get_renderer call from the user and make this a little more friendly, but the above should suffice. Note if you have some data in pixel coords, you can transform it into another coordinate system (eg data coords) using the inverse transform methods. The l,b,w,h bounding box of the text bbox in "data" coordinates (ie, the coords of the [1,2,3,4] plot) can be obtained with from matplotlib.transforms import inverse_transform_bbox databbox = inverse_transform_bbox(ax.transData, bbox) print databbox.get_bounds() In general, different coordinate systems in matplotlib communicate with one another by transforming a data point to pixel space and then inverse transforming that point into a different own space. There are two inverse methods to help with these tasks. The first is a transform method to handle single points: # apply the inverse transformation to tuple xy xi, yi = trans.inverse_xy_tup(xy) and the second is a stand-alone function defined in matplotlib.transforms to handle bboxes # apply the inverse transformation of a bbox bboxi = inverse_transform_bbox(trans, bbox) You might be thinking: why aren't we simply using an affine transformation matrix with a standard matrix inverse? All I can offer in response is that this architecture supports nonlinear transformations, ie, an affine plus a nonlinear transformation, for polar, log, etc.... There is probably a better way, but this is what we've got. JDH ```
 Re: [Matplotlib-users] text size From: Ted Drain - 2006-03-19 18:46 ```John, It might be nice if we could add a "what if" type function to all the backends for text sizing. That way we could write much better auto-layout algorithms for the axis and legend code. A very simple improvement to the auto-date labeller would then work like this: - Find min, max values of the time axis - Convert to the label format using the date formatter - Ask the backend what the size of the min/max labels would be - Take the max of that as a reasonable guess for the date label size - Divide up the total interval (in pixels) by the label size + pad - Pick nice ticks based on the total time interval and number of ticks you can fit This would be really handy for us in that we do a lot of date plotting with widely varying time ranges and it would be nice to have an "auto-ticker" that insures the labels don't overlap for a plot of a given size and range. On a scale of 1-10, how difficult do you think this type of thing would be to do? Ted At 07:52 PM 3/17/2006, John Hunter wrote: > >>>>> "Michael" == Michael P Mossey writes: > > Michael> Is there a way to find out in terms of pixels, points, or > Michael> fraction of figure, how large a text string is? In other > Michael> words if I generate "Some label" as the x-axis label, can > Michael> I find out how tall and wide it is? > >It can be done -- it's not terribly elegant. The basic problem is >that text size depends on the backend and configuration settings. >Since we have arbitrary fonts, multiline text with arbitrary rotation, >possible using TeX to render, with multiple output targets that might >handle text differently, you can see how this seemingly simple >question is actually tough. > >All text instances have a method called "get_window_extent" which >returns a matplotlib.transforms.BBox that bounds the text instance. >This bbox is in pixel coords. > > bbox = sometext.get_window_extent() > l,b,w,h = bbox.get_bounds() # left, bottom, width, height > > >Here's the rub: we can't realistically know the text size until we >have a renderer, because the same text may be different in different >renderers. matplotlib sets the figure renderer at draw time, and so >by default we don't know the text size until draw size. This is >sometimes cumbersome. > >One solution is to do something like > > plot(something) > sometext = text(x,y,s) > draw() # force a draw to set the renderer > l,b,w,h = sometext.get_window_extent().get_bounds() > ..adjust the postion of some objects based on this info > ...draw again > >not terribly elegant because we must redraw. If you did not first >force the draw command before calling get_window_extent, you would get >an exception about needing to first set the renderer. > >Another approach if you want to avoid the duplicate drawing is to >create a proxy renderer and pass that off to the text instance. > > l,b,w,h = fig.bbox.get_bounds() > renderer = RendererAgg(w, h, fig.dpi) > l,b,w,h = sometext.get_window_extent(renderer).get_bounds() > >This is not ideal either because you have a duplicate renderer. > >If you are using a *Agg backend and have a figure instance, you can do >the following, which requires no duplicate drawing and no duplicate >renderer (the get_renderer method below caches the return value so >repeated calls do not create duplicate renderers) > >Here is a complete example > > from matplotlib.patches import Rectangle > from matplotlib.transforms import identity_transform > from pylab import figure, show > > fig = figure() > ax = fig.add_subplot(111) > ax.plot([1,2,3,4], [1,2,3,4]) > t = ax.text(2,2,'Look Ma!\nNo hands', fontsize=40, rotation=-45) > renderer = fig.canvas.get_renderer() > bbox = t.get_window_extent(renderer) > l,b,w,h = bbox.get_bounds() > > # no transformation necessary, already in pixel coords > r = Rectangle((l,b),w,h, transform=identity_transform()) > ax.add_patch(r) > show() > >If we made the get_renderer method standard across backends, we could >probably hide the get_renderer call from the user and make this a >little more friendly, but the above should suffice. Note if you have >some data in pixel coords, you can transform it into another >coordinate system (eg data coords) using the inverse transform >methods. The l,b,w,h bounding box of the text bbox in "data" >coordinates (ie, the coords of the [1,2,3,4] plot) can be obtained >with > > from matplotlib.transforms import inverse_transform_bbox > databbox = inverse_transform_bbox(ax.transData, bbox) > print databbox.get_bounds() > >In general, different coordinate systems in matplotlib communicate >with one another by transforming a data point to pixel space and then >inverse transforming that point into a different own space. There are >two inverse methods to help with these tasks. The first is a >transform method to handle single points: > > # apply the inverse transformation to tuple xy > xi, yi = trans.inverse_xy_tup(xy) > >and the second is a stand-alone function defined in >matplotlib.transforms to handle bboxes > > # apply the inverse transformation of a bbox > bboxi = inverse_transform_bbox(trans, bbox) > > >You might be thinking: why aren't we simply using an affine >transformation matrix with a standard matrix inverse? All I can offer >in response is that this architecture supports nonlinear >transformations, ie, an affine plus a nonlinear transformation, for >polar, log, etc.... There is probably a better way, but this is what >we've got. > >JDH > > > > >------------------------------------------------------- >This SF.Net email is sponsored by xPML, a groundbreaking scripting language >that extends applications into web and mobile media. Attend the live webcast >and join the prime developer group breaking into this new coding territory! >http://sel.as-us.falkag.net/sel?cmd=lnk&kid=110944&bid=241720&dat=121642 >_______________________________________________ >Matplotlib-users mailing list >Matplotlib-users@... >https://lists.sourceforge.net/lists/listinfo/matplotlib-users ```