1

I am trying to draw a bounding box across text such as :

from matplotlib.pyplot import text
from matplotlib.patches import FancyBboxPatch
import matplotlib.transforms as mtransforms

import numpy as np
import matplotlib.pyplot as plt



def draw_bbox(ax, bb):
    # boxstyle=square with pad=0, i.e. bbox itself.
    p_bbox = FancyBboxPatch((bb.xmin, bb.ymin),
                            abs(bb.width), abs(bb.height),
                            boxstyle="round,pad=0.1, rounding_size=0.2",
                            ec="k", fc="none", zorder=10.,facecolor='pink')
    ax.add_patch(p_bbox)

np.random.seed(19680801)

fig, ax = plt.subplots()
x = 30*np.random.randn(10000)
mu = x.mean()
median = np.median(x)
sigma = x.std()
textstr = '\n'.join((
    r'$\mu=%.2f$' % (mu, ),
    r'$\mathrm{median}=%.2f$' % (median, ),
    r'$\sigma=%.2f$' % (sigma, )))

ax.hist(x, 50)

# these are matplotlib.patch.Patch properties
props = dict(boxstyle='square, pad=0.2', facecolor='wheat', alpha=0.5 )

# place a text box in upper left in axes coords
ax.text(0.05, 0.95, textstr, transform=ax.transAxes, fontsize=14,
        verticalalignment='top', bbox=props
       )

plt.show()

enter image description here

In the docs matplotlib.pyplot.text return an instance of Text which can take a kwarg 'bbox' . and this bbox argument accepts a dict() type object with properties for patches.FancyBboxPatch . The FancyBboxPatch has a position arg width which if I am trying to give,

props2 =FancyBboxPatch(xy=(0., 0.),width=1.,height=1.,boxstyle='Round, pad=0.2', facecolor='pink', alpha=0.5 )
ax.text(0.1, 0.95, textstr, transform=ax.transAxes, fontsize=14,
        verticalalignment='top', bbox=props2
       )

it gives the following error:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-59-0c8ad0d773a9> in <module>
     32 props2 =FancyBboxPatch((0., 0.),1.,1.,boxstyle='Round, pad=0.2', facecolor='pink', alpha=0.5 )
     33 ax.text(0.1, 0.95, textstr, transform=ax.transAxes, fontsize=14,
---> 34         verticalalignment='top', bbox=props2
     35        )
     36 

~\AppData\Local\Continuum\anaconda3\envs\automation_work\lib\site-packages\matplotlib\cbook\deprecation.py in wrapper(*args, **kwargs)
    367                 f"%(removal)s.  If any parameter follows {name!r}, they "
    368                 f"should be pass as keyword, not positionally.")
--> 369         return func(*args, **kwargs)
    370 
    371     return wrapper

~\AppData\Local\Continuum\anaconda3\envs\automation_work\lib\site-packages\matplotlib\axes\_axes.py in text(self, x, y, s, fontdict, withdash, **kwargs)
    781         else:
    782             t = mtext.Text(x, y, text=s)
--> 783         t.update(effective_kwargs)
    784 
    785         t.set_clip_path(self.patch)

~\AppData\Local\Continuum\anaconda3\envs\automation_work\lib\site-packages\matplotlib\text.py in update(self, kwargs)
    177         super().update(kwargs)
    178         if bbox is not sentinel:
--> 179             self.set_bbox(bbox)
    180 
    181     def __getstate__(self):

~\AppData\Local\Continuum\anaconda3\envs\automation_work\lib\site-packages\matplotlib\text.py in set_bbox(self, rectprops)
    443 
    444         if rectprops is not None:
--> 445             props = rectprops.copy()
    446             boxstyle = props.pop("boxstyle", None)
    447             pad = props.pop("pad", None)

AttributeError: 'FancyBboxPatch' object has no attribute 'copy'

This error doesn't make sense to me. I feel it's a bug in the FancyBboxPatch implementation? Can anyone help to find the issue?

Anu
  • 3,198
  • 5
  • 28
  • 49
  • You are passing the `FancyBboxPatch` itself. But the `bbox` argument needs a dictionary as input. – ImportanceOfBeingErnest Sep 19 '19 at 23:28
  • I tried passing a dictionary object, `props = dict(boxstyle='Round, pad=0.2', facecolor='wheat', alpha=0.5 )` works but, `props = dict(xy=(0., 0.), width= 1., height=1.,boxstyle='Round, pad=0.2', facecolor='wheat', alpha=0.5 )` doesn't ! it throws an error `TypeError: __init__() got multiple values for argument 'xy'` and if I use `props =dict((0., 0.),1., 1.,boxstyle='Round, pad=0.2', facecolor='wheat', alpha=0.5 )` then I get `TypeError: dict expected at most 1 arguments, got 3`. How I can use `width` in that `dict()` or `FancyBboxPatch()`. Any suggestions? – Anu Sep 20 '19 at 01:50
  • Oh, you can't. The width is determined by the width of the text. You cannot change that. What exactly do you want to achieve? – ImportanceOfBeingErnest Sep 20 '19 at 01:55
  • I want to adjust the width of the box so that I can overlay multiple boxes on the plot. – Anu Sep 23 '19 at 20:11
  • You can draw a "box" with a `plt.Rectangle()`. So there might be more requirements? – ImportanceOfBeingErnest Sep 23 '19 at 20:16
  • I am trying to make a box of fixed width & height and insert text into it as shown in the picture! I don't want my text's font to handle the size of the box. This will help me to plot 2 boxes on top of each other(not-overlapping) with any text of some considerable font_size into it. That's why I used an instance of FancyBboxPatch. – Anu Sep 23 '19 at 23:00
  • Mhh, this is hard. Because in matplotlib you cannot *insert* text into a box like in html, svg, or Word etc. Do you only care about the width or the height as well. In the former case I might have a solution. – ImportanceOfBeingErnest Sep 23 '19 at 23:32
  • Oh ok. I just care about the width for now. Can you post one as an answer? – Anu Sep 24 '19 at 00:31

1 Answers1

1

The following would add two texts below each other in the upper left corner of the axes. The width of the boxes is 200 pixels, independent of the size of the text.

import matplotlib.pyplot as plt
from matplotlib.offsetbox import AnchoredOffsetbox, HPacker, VPacker, TextArea, PaddedBox

fig, ax = plt.subplots()

text1 = TextArea("Short\nText")
of1 = HPacker(width=200, height=None, pad=2, sep=5, align="center", mode="expand", children=[text1])
pb1 = PaddedBox(of1, pad=4, draw_frame=True)

text2 = TextArea("Long text in one line")
of2 = HPacker(width=200, height=None, pad=2, sep=5, align="center", mode="expand",  children=[text2])
pb2 = PaddedBox(of2, pad=4, draw_frame=True)

oft = VPacker(width=200, height=None, pad=2, sep=5, align="baseline", children=[pb1, pb2])
t = AnchoredOffsetbox("upper left", pad=0.4, frameon=False, child=oft)

ax.add_artist(t)

plt.show()

enter image description here

ImportanceOfBeingErnest
  • 321,279
  • 53
  • 665
  • 712