17

I'm attempting to wrap text using wrap=True but it doesn't seem to be working for me. Running the example from matplotlib below:

import matplotlib.pyplot as plt

fig = plt.figure()
plt.axis([0, 10, 0, 10])
t = "This is a really long string that I'd rather have wrapped so that it"\
    " doesn't go outside of the figure, but if it's long enough it will go"\
    " off the top or bottom!"
plt.text(4, 1, t, ha='left', rotation=15, wrap=True)
plt.text(6, 5, t, ha='left', rotation=15, wrap=True)
plt.text(5, 5, t, ha='right', rotation=-15, wrap=True)
plt.text(5, 10, t, fontsize=18, style='oblique', ha='center',
         va='top', wrap=True)
plt.text(3, 4, t, family='serif', style='italic', ha='right', wrap=True)
plt.text(-1, 0, t, ha='left', rotation=-15, wrap=True)

plt.show()

gets me this:

Text wrapping gone wrong

enter image description here

Any ideas on what's the issue?

3
  • Works for me on Python 3.4 and matplotlib 2.1.1. What version of Python and matplotlib are you using? Commented Jan 3, 2018 at 14:47
  • 3
    Hello, it is an issue of Anaconda #705 since 15 Mar 2016. The issue is still open. I just posted a comment there, that the problem is still actual. Commented Jan 3, 2018 at 16:30
  • Running off Anaconda/python 3.6.3 with matplotlib 2.1.0 Commented Jan 3, 2018 at 16:47

1 Answer 1

12

Matplotlib is hardwired to use the figure box as the wrapping width. To get around this, you have to override the _get_wrap_line_width method, which returns how long the line can be in screen pixels. For example:

text = ('Lorem ipsum dolor sit amet, consectetur adipiscing elit, '
        'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ')
txt = ax.text(.2, .8, text, ha='left', va='top', wrap=True,
              bbox=dict(boxstyle='square', fc='w', ec='r'))
txt._get_wrap_line_width = lambda : 600.  #  wrap to 600 screen pixels

The lambda function is just an easy way to create a function/method without having to write a named one using def.

Obviously using a private method comes with risks, such as it being removed in future versions. And I haven't tested how well this works with rotations. If you want to make something more sophisticated, such as using data coordinates, you would have to subclass the Text class, and override the _get_wrap_line_width method explicitly.

import matplotlib.pyplot as plt
import matplotlib.text as mtext

class WrapText(mtext.Text):
    def __init__(self,
                 x=0, y=0, text='',
                 width=0,
                 **kwargs):
        mtext.Text.__init__(self,
                 x=x, y=y, text=text,
                 wrap=True,
                 **kwargs)
        self.width = width  # in screen pixels. You could do scaling first

    def _get_wrap_line_width(self):
        return self.width

fig = plt.figure(1, clear=True)
ax = fig.add_subplot(111)

text = ('Lorem ipsum dolor sit amet, consectetur adipiscing elit, '
        'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ')

# Create artist object. Note clip_on is True by default
# The axes doesn't have this method, so the object is created separately
# and added afterwards.
wtxt = WrapText(.8, .4, text, width=500, va='top', clip_on=False,
                bbox=dict(boxstyle='square', fc='w', ec='b'))
# Add artist to the axes
ax.add_artist(wtxt)

plt.show()
Sign up to request clarification or add additional context in comments.

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.