diff --git a/CHANGELOG.md b/CHANGELOG.md index cd91e4979a..6b56e5ac90 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,9 @@ This project adheres to [Semantic Versioning](http://semver.org/). ### Added - Support `marginal_x`/`marginal_y="heatmap"` in `density_heatmap`, drawing a single-row/column heatmap strip in the margin colored by the same `z`/`histfunc` aggregate as the main plot and sharing its color scale [[#5706](https://github.com/plotly/plotly.py/issues/5706)], with thanks to @lucasjamar for the contribution! +### Fixed +- Fix `mpl_to_plotly` not setting `paper_bgcolor` and `plot_bgcolor` from the matplotlib figure and axes backgrounds, so converted figures match the source figure's background colors [[#5285](https://github.com/plotly/plotly.py/pull/5285)], with thanks to @robertoffmoura for the contribution! + ## [7.0.0] - 2026-08-25 ### Fixed diff --git a/plotly/matplotlylib/mplexporter/utils.py b/plotly/matplotlylib/mplexporter/utils.py index 23bfd414f7..8170ced8f0 100644 --- a/plotly/matplotlylib/mplexporter/utils.py +++ b/plotly/matplotlylib/mplexporter/utils.py @@ -271,6 +271,7 @@ def get_figure_properties(fig): "figwidth": fig.get_figwidth(), "figheight": fig.get_figheight(), "dpi": fig.dpi, + "figbg": export_color(fig.patch.get_facecolor()), } diff --git a/plotly/matplotlylib/renderer.py b/plotly/matplotlylib/renderer.py index a282c67cec..65bbcfabb1 100644 --- a/plotly/matplotlylib/renderer.py +++ b/plotly/matplotlylib/renderer.py @@ -101,6 +101,7 @@ def open_figure(self, fig, props): autosize=False, hovermode="closest", ) + self.plotly_fig["layout"].paper_bgcolor = _export_color(props["figbg"]) self.mpl_x_bounds, self.mpl_y_bounds = mpltools.get_axes_bounds(fig) margin = go.layout.Margin( l=int(self.mpl_x_bounds[0] * self.plotly_fig["layout"]["width"]), @@ -166,6 +167,8 @@ def open_axes(self, ax, props): ] self.current_bars = [] self.axis_ct += 1 + # update plot background with the axes background from mpl + self.plotly_fig["layout"].plot_bgcolor = _export_color(props["axesbg"]) # set defaults in axes xaxis = go.layout.XAxis( anchor="y{0}".format(self.axis_ct), zeroline=False, ticks="inside" diff --git a/plotly/matplotlylib/tests/test_renderer.py b/plotly/matplotlylib/tests/test_renderer.py index f56d830917..81b7c22271 100644 --- a/plotly/matplotlylib/tests/test_renderer.py +++ b/plotly/matplotlylib/tests/test_renderer.py @@ -199,3 +199,37 @@ def test_filled_path_collection_date_xaxis(): filled = [t for t in plotly_fig.data if t.fill == "toself"] assert len(filled) >= 1 assert all(isinstance(x, str) for x in filled[0].x) + + +def test_background_colors_from_matplotlib_defaults(): + fig, ax = plt.subplots() + ax.plot([0, 1], [0, 1]) + + plotly_fig = tls.mpl_to_plotly(fig) + + assert plotly_fig.layout.plot_bgcolor == "#FFFFFF" + assert plotly_fig.layout.paper_bgcolor == "#FFFFFF" + + +def test_custom_background_colors_are_preserved(): + fig, ax = plt.subplots() + fig.patch.set_facecolor("lightyellow") + ax.set_facecolor("lightgray") + ax.plot([0, 1], [0, 1]) + + plotly_fig = tls.mpl_to_plotly(fig) + + assert plotly_fig.layout.plot_bgcolor == "#D3D3D3" + assert plotly_fig.layout.paper_bgcolor == "#FFFFE0" + + +def test_semitransparent_axes_background_preserved(): + """Axes backgrounds with alpha export as mpl-style rgba strings, which + must be passed through as-is, not re-parsed by export_color.""" + fig, ax = plt.subplots() + ax.set_facecolor((0.1, 0.2, 0.3, 0.4)) + ax.plot([0, 1], [0, 1]) + + plotly_fig = tls.mpl_to_plotly(fig) + + assert plotly_fig.layout.plot_bgcolor == "rgba(26, 51, 76, 0.4)"