From 91757a7f824233b857b7ffcb970a47256d4a92bf Mon Sep 17 00:00:00 2001 From: Roberto Moura Date: Wed, 2 Sep 2026 15:48:28 +0000 Subject: [PATCH 1/4] Convert fill polygons to filled scatter traces --- plotly/matplotlylib/renderer.py | 26 ++++++++++++++++++++++ plotly/matplotlylib/tests/test_renderer.py | 18 +++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/plotly/matplotlylib/renderer.py b/plotly/matplotlylib/renderer.py index 65bbcfabb1..e806b93cb7 100644 --- a/plotly/matplotlylib/renderer.py +++ b/plotly/matplotlylib/renderer.py @@ -9,6 +9,7 @@ import warnings +import matplotlib.patches as mpatches import plotly.graph_objs as go from plotly.matplotlylib.mplexporter import Renderer from plotly.matplotlylib import mpltools @@ -602,6 +603,12 @@ def draw_path(self, **props): is_bar = mpltools.is_bar(self.current_mpl_ax.containers, **props) if is_bar: self.current_bars += [props] + elif ( + isinstance(props["mplobj"], mpatches.Polygon) + and props["coordinates"] == "data" + ): + self.msg += " Drawing a filled polygon\n" + self._draw_filled_polygon(props) else: self.msg += " This path isn't a bar, not drawing\n" warnings.warn( @@ -609,6 +616,25 @@ def draw_path(self, **props): "of a bar chart. Ignoring." ) + def _draw_filled_polygon(self, props): + """Draw a matplotlib Polygon patch as a filled scatter trace.""" + style = props["style"] + verts = props["data"] + facecolor = _export_color(style["facecolor"]) + edgecolor = _export_color(style["edgecolor"]) + self.plotly_fig.add_trace( + go.Scatter( + x=self._convert_x_dates([v[0] for v in verts]), + y=[v[1] for v in verts], + mode="lines", + line=go.scatter.Line(color=edgecolor, width=style["edgewidth"]), + fill="toself", + fillcolor=facecolor, + xaxis="x{0}".format(self.axis_ct), + yaxis="y{0}".format(self.axis_ct), + ) + ) + def draw_text(self, **props): """Create an annotation dict for a text obj. diff --git a/plotly/matplotlylib/tests/test_renderer.py b/plotly/matplotlylib/tests/test_renderer.py index 02dd6cf34a..0a2b895b65 100644 --- a/plotly/matplotlylib/tests/test_renderer.py +++ b/plotly/matplotlylib/tests/test_renderer.py @@ -348,6 +348,24 @@ def test_uneven_custom_date_xtickvals_are_converted(): ) +def test_fill_converts(): + """plt.fill polygons convert to filled scatter traces.""" + x = np.linspace(0, 2 * np.pi, 50) + fig, ax = plt.subplots() + ax.fill(x, np.sin(x), "g") + + plotly_fig = tls.mpl_to_plotly(fig) + + assert len(plotly_fig.data) == 1 + trace = plotly_fig.data[0] + assert trace.type == "scatter" + assert trace.fill == "toself" + assert np.allclose(trace.x, x) + assert np.allclose(trace.y, np.sin(x)) + assert trace.fillcolor == "#007F00" + assert trace.line.color == "rgba(0,0,0,0)" + + def test_custom_date_xtickvals_given_as_numbers_are_converted(): """Custom date ticks given as matplotlib date numbers must be converted to date strings.""" From 7420a3050617a1b39e97583a0bd852bcd267d3c9 Mon Sep 17 00:00:00 2001 From: Roberto Moura Date: Tue, 6 Oct 2026 08:41:32 +0000 Subject: [PATCH 2/4] Support legend labels on filled polygon traces --- plotly/matplotlylib/renderer.py | 9 ++++++++ plotly/matplotlylib/tests/test_renderer.py | 24 ++++++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/plotly/matplotlylib/renderer.py b/plotly/matplotlylib/renderer.py index e806b93cb7..68ab05e543 100644 --- a/plotly/matplotlylib/renderer.py +++ b/plotly/matplotlylib/renderer.py @@ -622,8 +622,17 @@ def _draw_filled_polygon(self, props): verts = props["data"] facecolor = _export_color(style["facecolor"]) edgecolor = _export_color(style["edgecolor"]) + mplobj = props.get("mplobj") + label = mplobj.get_label() if mplobj is not None else None + if not label or (isinstance(label, str) and label.startswith("_")): + label = None + showlegend = False + else: + showlegend = None self.plotly_fig.add_trace( go.Scatter( + name=label, + showlegend=showlegend, x=self._convert_x_dates([v[0] for v in verts]), y=[v[1] for v in verts], mode="lines", diff --git a/plotly/matplotlylib/tests/test_renderer.py b/plotly/matplotlylib/tests/test_renderer.py index 0a2b895b65..65b2df4a8c 100644 --- a/plotly/matplotlylib/tests/test_renderer.py +++ b/plotly/matplotlylib/tests/test_renderer.py @@ -366,6 +366,30 @@ def test_fill_converts(): assert trace.line.color == "rgba(0,0,0,0)" +def test_fill_with_legend_label(): + """plt.fill polygons with labels export their name and show in legend.""" + fig, ax = plt.subplots() + ax.fill([0, 1, 2], [0, 2, 0], "b", label="Shaded Area") + ax.fill([0, 1, 2], [0, 1, 0], "g") + ax.plot([0, 1, 2], [1, 1, 1], "r-", label="Threshold") + ax.legend() + + plotly_fig = tls.mpl_to_plotly(fig) + + assert plotly_fig.layout.showlegend == True + assert len(plotly_fig.data) == 3 + + labeled_fill = next( + t for t in plotly_fig.data if t.fill == "toself" and t.name == "Shaded Area" + ) + assert labeled_fill.showlegend is not False + + unlabeled_fill = next( + t for t in plotly_fig.data if t.fill == "toself" and t.name is None + ) + assert unlabeled_fill.showlegend is False + + def test_custom_date_xtickvals_given_as_numbers_are_converted(): """Custom date ticks given as matplotlib date numbers must be converted to date strings.""" From e80c7ad732798fb1220bd612bf523b1a32268fd0 Mon Sep 17 00:00:00 2001 From: Roberto Moura Date: Tue, 6 Oct 2026 08:46:11 +0000 Subject: [PATCH 3/4] Add changelog entry --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea837e3d0b..0344560937 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased +### Added +- Add support for converting matplotlib fill polygons (`plt.fill`) to filled scatter traces in `mpl_to_plotly`, with thanks to @robertoffmoura for the contribution! + ### Fixed - 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! - 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! From 8429b6f228e5bbd9a3dde273a7db499148b4423f Mon Sep 17 00:00:00 2001 From: Roberto Moura Date: Tue, 6 Oct 2026 09:03:23 +0000 Subject: [PATCH 4/4] Update changelog entry with PR number 5795 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0344560937..7525068fe8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ This project adheres to [Semantic Versioning](http://semver.org/). ## Unreleased ### Added -- Add support for converting matplotlib fill polygons (`plt.fill`) to filled scatter traces in `mpl_to_plotly`, with thanks to @robertoffmoura for the contribution! +- Add support for converting matplotlib fill polygons (`plt.fill`) to filled scatter traces in `mpl_to_plotly` [[#5795](https://github.com/plotly/plotly.py/pull/5795)], with thanks to @robertoffmoura for the contribution! ### Fixed - 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!