Skip to content

[Bug]: Backgroundcolor of text is set in a too wide range #29874

Open

Description

@siegbert7

Bug summary

Backgroundcolor of text is set in a too wide range when setting it for the first time.
Problem: Border of first color remains when backgroundcolor is set again.

Image

Code for reproduction

''' Backgroundcolor of text is set in a too wide range when setting it for the first time.
    Problem: Border of first color remains when backgroundcolor is set again.'''

# windows 11
# Python version 3.13.2
# Matplotlib version 3.10.1

import matplotlib.pyplot as plt

fig, ax = plt.subplots(1, 1)
fig.set_dpi(100)

txt   = ax.text(0.1, 0.8, 'There should not be a red border around this text !')

##txt.set(backgroundcolor='none')     # Uncomment this line as workaround to avoid red border
txt.set(backgroundcolor='red')        # Set backgroundcolor for the first time
txt.set(backgroundcolor='none')       # Cannot reset backgroundcolor of text completely, red border remains

plt.show()

Actual outcome

Text with red border, see picture above.

Expected outcome

Text without red border.

Additional information

There is a workaround for the first, but not a fix, see Python program above.

Operating system

Windows

Matplotlib Version

3.10.1

Matplotlib Backend

tkagg

Python version

3.13.2

Jupyter version

Installation

pip

Activity

  1. timhoffm commented on Apr 6, 2025

    @timhoffm
    Member

    The background is realized through an internal FancyBboxPatch (Text._bbox_patch). It is initally None and only created if you want a background or border around the text.

    What happens here is that the patch is created with finite linewidth 1 and the edgecolor is the same as the facecolor (One can debate whether that behavior is reasonable, but it likely needs a closer look to handle all potential cases reasonably). To completely undo txt.set(backgroundcolor='red') you should remove the background patch via txt.set(bbox=None) instead of making the patch transparent.

  2. timhoffm commented on Apr 6, 2025

    @timhoffm
    Member

    Note: The behavior is due to

    self.set_bbox(dict(facecolor=color, edgecolor=color))

    There is a need to do something about the edge because by default rcParams["patch.linewidth"] is 1. The alternative to setting edgecolor to the same color as facecolor would be to set linewidth=0. That might be the slightly better approach. OTOH it will lead to subsequent txt.set_bbox(edgecolor=...) have on visual effect. So that's not ideal either. Overall, I believe it's not worth changing the behavior and the improved documentation in #29875 should suffice.

  3. siegbert7 commented on Apr 6, 2025

    @siegbert7
    Author

    Thanks for your reply.

    For an user of matplotlib like me it is hard to guess, that the problem of the red border results from the initialization of the text object and how to work around.
    At least you should point out in the manual, that "txt.set(backgroundcolor='none')" as a command before is a simple workaround. Or something like this.

    I got another problem, probably caused by the initialization of text object, which is more hard to understand for the user.
    It appears when you try to get the width of the text. Sometime it works, sometimes there is an exception:

    ----------------------------

    import matplotlib.pyplot as plt

    fig, ax = plt.subplots(1, 1)
    fig.set_dpi(100)

    txt = ax.text(0.1, 0.8, 'My Text ...')
    try:
    width = txt.get_bbox_patch().get_width() # You will get an AttributeError:
    except AttributeError:
    print("'NoneType' object has no attribute 'get_width'")

    Make it work with the "workaround of backgroundcolor":

    txt.set(backgroundcolor='none')
    width = txt.get_bbox_patch().get_width() # Now you will get the width

    plt.show()

  4. rcomer commented on Apr 6, 2025

    @rcomer
    Member

    There is a need to do something about the edge because by default rcParams["patch.linewidth"] is 1. The alternative to setting edgecolor to the same color as facecolor would be to set linewidth=0. That might be the slightly better approach. OTOH it will lead to subsequent txt.set_bbox(edgecolor=...) have on visual effect. So that's not ideal either. Overall, I believe it's not worth changing the behavior and the improved documentation in #29875 should suffice.

    Could we instead set edgecolor to "none"? If I understand correctly, that is the default for a patch these days anyway (though it is black in classic mode).

  5. timhoffm commented on Apr 6, 2025

    @timhoffm
    Member

    Thanks, that's a good idea and probably the correct solution.

    The only downside is that the size of the box changes (left half is edgecolor none):

    Image

    This is a breaking change. It's probably still worth doing so and communicating it as a fix.

  6. rcomer commented on Apr 7, 2025

    @rcomer
    Member

    Another option might be to set the edgecolor to match facecolor here

    self._bbox_patch.update(dict(facecolor=color))

    Then set_backgroundcolor would be the same as set_color directly on the patch

    def set_color(self, c):
    """
    Set both the edgecolor and the facecolor.
    Parameters
    ----------
    c : :mpltype:`color`
    See Also
    --------
    Patch.set_facecolor, Patch.set_edgecolor
    For setting the edge or face color individually.
    """
    self.set_facecolor(c)
    self.set_edgecolor(c)

  7. timhoffm commented on Apr 7, 2025

    @timhoffm
    Member

    That would be a bit counter-intuitive to me. My mental model is that the text background color is only the fill and does not affect the frame. E.g. when I have

    text.set_bbox(dict(facecolor=('red'), edgecolor='black'))
    text.set_backgroundcolor('green')
    

    I would expect that the black frame stays.

    I also like that the box has sharp color edges when no edge line is used. The boundary line is antialiased and blurs the edges.

  8. codingabhiroop commented on Jan 1, 2026

    @codingabhiroop
    Contributor

    Hi!
    I’d like to take on this issue.

    I'm planning on updating Text.set_backgroundcolor so that when the color is 'none', the _bbox_patch’s edgecolor is also set to 'none' to remove the unwanted border. Will add a test for this behavior.

  9. timhoffm commented on Jan 2, 2026

    @timhoffm
    Member

    Hi! I’d like to take on this issue.

    I'm planning on updating Text.set_backgroundcolor so that when the color is 'none', the _bbox_patch’s edgecolor is also set to 'none' to remove the unwanted border. Will add a test for this behavior.

    That doesn't sound like the right solution. I think we can just leave out edgecolor but t.b.c.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions