Skip to content

Commit d3d7e4d

Browse files
authored
Merge pull request #5699 from robertoffmoura/rm/hide-internal-mpl-label-legends
Only show a legend when the matplotlib figure has one
2 parents 52d0c7f + 5d3f89b commit d3d7e4d

3 files changed

Lines changed: 43 additions & 5 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ This project adheres to [Semantic Versioning](http://semver.org/).
99
- Fix concurrent first access to lazily initialized graph object properties, which could raise `ValueError("Invalid value")` [[#5691](https://github.com/plotly/plotly.py/pull/5691)], with thanks to @hb1915 for the contribution!
1010
- Fix `mpl_to_plotly` crashing on touching bars (such as `plt.hist`) due to floating-point noise producing negative `bargap` values by clamping `bargap` to `[0, 1]` [[#5696](https://github.com/plotly/plotly.py/pull/5696)], with thanks to @robertoffmoura for the contribution!
1111
- Fix `mpl_to_plotly` drawing step plots with diagonal lines instead of vertical/horizontal step segments by mapping matplotlib step drawstyles (`steps-pre`, `steps-post`, `steps-mid`) to plotly line shapes [[#5697](https://github.com/plotly/plotly.py/pull/5697)], with thanks to @robertoffmoura for the contribution!
12+
- Fix `mpl_to_plotly` showing unwanted legends with placeholder or internal label entries (`_nolegend_`, `_childN`) by defaulting `layout.showlegend=False` unless the matplotlib figure explicitly defines a legend [[#5699](https://github.com/plotly/plotly.py/pull/5699)], with thanks to @robertoffmoura for the contribution!
1213
- Export tick label colors to the plotly tickfont in `mpl_to_plotly` [[#5716](https://github.com/plotly/plotly.py/pull/5716)], with thanks to @robertoffmoura for the contribution!
1314
- Fix `px.sunburst`, `px.treemap` and `px.icicle` listing sectors in a different order on every run when `path` is used with a Polars DataFrame; sectors now follow their order of first appearance for all dataframe backends [[#5766](https://github.com/plotly/plotly.py/pull/5766)], with thanks to @Irahan2 for the contribution!
1415

‎plotly/matplotlylib/renderer.py‎

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,10 @@ def open_figure(self, fig, props):
9191
height=int(props["figheight"] * props["dpi"]),
9292
autosize=False,
9393
hovermode="closest",
94+
# plotly.js auto-names unnamed traces "trace N" and shows them
95+
# in the legend; the legend is only enabled when the mpl figure
96+
# actually has one (see open_legend)
97+
showlegend=False,
9498
)
9599
self.plotly_fig["layout"].paper_bgcolor = _export_color(props["figbg"])
96100
self.mpl_x_bounds, self.mpl_y_bounds = mpltools.get_axes_bounds(fig)
@@ -485,13 +489,18 @@ def draw_marked_line(self, **props):
485489
),
486490
)
487491
if props["coordinates"] == "data":
492+
label = props["label"]
493+
# matplotlib uses "_nolegend_" and auto-generated "_childN"
494+
# labels for artists that must not appear in a legend
495+
if not label or (isinstance(label, str) and label.startswith("_")):
496+
label = None
497+
showlegend = False
498+
else:
499+
showlegend = None
488500
marked_line = go.Scatter(
489501
mode=mode,
490-
name=(
491-
str(props["label"])
492-
if isinstance(props["label"], str)
493-
else props["label"]
494-
),
502+
name=label,
503+
showlegend=showlegend,
495504
x=[xy_pair[0] for xy_pair in props["data"]],
496505
y=[xy_pair[1] for xy_pair in props["data"]],
497506
xaxis="x{0}".format(self.axis_ct),

‎plotly/matplotlylib/tests/test_renderer.py‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -527,6 +527,34 @@ def test_fixed_formatter_ticktext():
527527
assert plotly_fig.layout.xaxis.ticktext == ("Baseline", "param = 1", "param = 2")
528528

529529

530+
def test_no_legend_entries_for_internal_mpl_labels():
531+
"""mpl internal labels (_nolegend_, _childN) must not become legend entries."""
532+
fig, ax = plt.subplots()
533+
ax.plot([0, 1, 2, 3], [0, 1, 0, 1], "b", [0, 1, 2, 3], [1, 0, 1, 0], "r--")
534+
535+
plotly_fig = tls.mpl_to_plotly(fig)
536+
537+
assert plotly_fig.layout.showlegend == False
538+
assert all(t.name is None for t in plotly_fig.data)
539+
540+
541+
def test_unlabeled_traces_hidden_from_legend_when_figure_has_legend():
542+
"""Traces without labels must have showlegend=False when a figure has a legend."""
543+
fig, ax = plt.subplots()
544+
ax.plot([0, 1], [0, 1], label="Labeled line")
545+
ax.plot([0, 1], [1, 0]) # Unlabeled line
546+
ax.legend()
547+
548+
plotly_fig = tls.mpl_to_plotly(fig)
549+
550+
assert plotly_fig.layout.showlegend == True
551+
assert len(plotly_fig.data) == 2
552+
assert plotly_fig.data[0].name == "Labeled line"
553+
assert plotly_fig.data[0].showlegend is not False
554+
assert plotly_fig.data[1].name is None
555+
assert plotly_fig.data[1].showlegend is False
556+
557+
530558
def test_custom_date_xtickvals_are_converted():
531559
"""Custom tick values on a date axis must be converted to date strings,
532560
not left as raw matplotlib date numbers or datetime objects."""

0 commit comments

Comments
 (0)