Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork8.1k
Add legend.linewidth parameter to control legend box edge linewidth#30780
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Conversation
rcomer left a comment• edited
Loading Uh oh!
There was an error while loading.Please reload this page.
edited
Uh oh!
There was an error while loading.Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Thanks for your work on this@manit2004. As well as my specific comments below, I think this should default to the patch linewidth. As you noted in your summary, that is what is currently used so having it as the default will give us backwards compatibility. I think that will clear up the image test failures.
This should also get atest to verify the behaviour.
Uh oh!
There was an error while loading.Please reload this page.
| value from ``axes.linewidth``. This allows for independent control of the | ||
| legend frame line width without affecting other elements. | ||
| ..versionadded::3.10 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
The versionadded directive is not needed in the release notes. All these files will later be combined to make a single page for the release.Like this
| The `.Legend` constructor also accepts a new *linewidth* parameter to set the | ||
| legend frame line width directly, overriding the rcParam value:: | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Using theplot directive here will mean we get an output image with the example. Seehere for an example of how that’s done.
lib/matplotlib/legend.py Outdated
| The legend's background patch edge linewidth. | ||
| If ``None``, use :rc:`axes.linewidth`. | ||
| .. versionadded:: 3.10 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
| ..versionadded::3.10 | |
| ..versionadded::3.11 |
v3.10 is already released.
lib/matplotlib/mpl-data/matplotlibrc Outdated
| #legend.handletextpad: 0.8 # the space between the legend line and legend text | ||
| #legend.borderaxespad: 0.5 # the border between the axes and legend edge | ||
| #legend.columnspacing: 2.0 # column separation | ||
| #legend.linewidth: None # line width of the legend frame, None means inherit from axes.linewidth |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I would move this up with edgecolor, etc. Although it is technically a size, it comes under the same style consideration as color and alpha. Also it is measured in points, not fraction of font size.
lib/matplotlib/rcsetup.py Outdated
| # legend properties | ||
| "legend.fancybox":validate_bool, | ||
| "legend.loc":_validate_legend_loc, | ||
| "legend.linewidth":validate_float_or_None,# linewidth of legend frame |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Move this down with framealpha.
rcomer left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Thanks for the quick update@manit2004. Just a couple more minor comments but otherwise this looks great!
Uh oh!
There was an error while loading.Please reload this page.
lib/matplotlib/legend.py Outdated
| iflinewidthisNone: | ||
| linewidth=mpl.rcParams["patch.linewidth"] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
| iflinewidthisNone: | |
| linewidth=mpl.rcParams["patch.linewidth"] |
I think we can leave this asNone, and theFancyBboxPatch will take care of swapping it for the rcParam.
manit2004 commentedNov 23, 2025
@rcomer I have implemented your suggestions and also added a test to verify the feature implemented. Added
|
manit2004 commentedNov 23, 2025
@rcomer I have renamed the .rst file and also removed those two lines as you pointed out. Is there anything else that I should do or is it ready to merge? |
rcomer left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Thanks@manit2004 this looks good to me. For code changes we require two approving reviews, so now we just need to wait for someone else to take a look.
manit2004 commentedNov 23, 2025
Thanks@rcomer for guiding me through one of my first contributions in a big project like matplotlib. Waiting for the reviews. |
| #legend.framealpha: 0.8 # legend patch transparency | ||
| #legend.facecolor: inherit # inherit from axes.facecolor; or color spec | ||
| #legend.edgecolor: 0.8 # background patch boundary color | ||
| #legend.linewidth: None # line width of the legend frame, None means inherit from patch.linewidth |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
It seems we have a mixture already in that we sometimes use "inherit" and sometimes None. We should do an analysis of the cases and decide which one to use in the future.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
I see the point about consistency. I followed the edgecolor pattern which uses "inherit". Happy to adjust based on the team's preferred convention.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Unfortunately, we are inconsistent already.
rcParams using inherit:
- legend.facecolor: inherit # inherit from axes.facecolor; or color spec
- legend.edgecolor
- xtick.labelcolor
- ytick.labelcolor
rcParams using None:
- grid.major.color: None # If None defaults to grid.color
- grid.major.linestyle: None # If None defaults to grid.linestyle
- grid.major.linewidth: None # If None defaults to grid.linewidth
- grid.major.alpha: None # If None defaults to grid.alpha
- grid.minor.color: None # If None defaults to grid.color
- grid.minor.linestyle: None # If None defaults to grid.linestyle
- grid.minor.linewidth: None # If None defaults to grid.linewidth
- grid.minor.alpha: None # If None defaults to grid.alpha
- legend.labelcolor: None # None: use text.color
- contour.linewidth: None # {float, None} Size of the contour line widths. If set to None, it falls back to
line.linewidth.
rcParams using auto
- axes.titlecolor: auto # color of the axes title, auto falls back to text.color as default value
- savefig.facecolor: auto # figure face color when saving
Summary
"auto" is out. The decision is between None and inherit. There are arguments for both.
I'm ok with sticking withNone for now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others.Learn more.
Thanks for clarifying. Is there anything needed from my side to move this PR forward being merged?
Uh oh!
There was an error while loading.Please reload this page.
7bf7e47 intomatplotlib:mainUh oh!
There was an error while loading.Please reload this page.
Uh oh!
There was an error while loading.Please reload this page.
PR summary
Why is this change necessary?
Users can customize many aspects of legend appearance through rcParams (facecolor, edgecolor, framealpha, shadow, etc.), but there was no way to control the linewidth of the legend's box edges. This created an inconsistency in the customization API and required workarounds when users wanted legends with thicker or thinner frame lines to match their plot aesthetics.
What problem does it solve?
This solves the problem of not being able to independently control legend frame linewidth. Previously, the legend frame linewidth was implicitly tied to the default patch linewidth, making it impossible to have, for example, thin axes borders but thick legend borders, or vice versa.
What is the reasoning for this implementation?
The implementation follows matplotlib's existing pattern for legend customization:
legend.linewidthrcParam (default:None)None, it inherits fromaxes.linewidthfor backward compatibilitylinewidthparameter toLegend.__init__()for per-legend controlFancyBboxPatchthat draws the legend frameThis approach is consistent with how other legend properties like
edgecolorandfacecolorwork, making it intuitive for users already familiar with matplotlib's legend customization.Minimum self-contained example:
PR checklist
128/131 legend tests pass; 3 image comparison tests have minor visual differences (RMS < 3.0) that may require baseline updates
This is a styling parameter not a new plotting feature.
Check doc/api/next_api_changes/development/30767-MS.rst