diff --git a/README.md b/README.md index 5e2ec5f..cbf1bcc 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,28 @@ to explicitly render an already-built canvas, use `canvas.render(...)`. The older `canvas.plot(backend=...)` spelling is still supported for compatibility, but emits a `FutureWarning`. +Add several lines at once with shared styling: + +``` python +canvas.plot_many( + [(x, np.sin(x)), (x, np.cos(x))], + labels=["sin(x)", "cos(x)"], + linewidth=2, +) +``` + +Common figure and axis settings can be grouped with `configure()`: + +``` python +canvas.configure( + title="Trigonometry", + xlabel="Angle", + ylabel="Value", + grid=True, + facecolor="whitesmoke", +) +``` + For Matplotlib-specific customization, pass method calls declaratively. Figure methods run once and axes methods run for every subplot, providing access to any Matplotlib API without requiring a maxplotlib @@ -165,26 +187,27 @@ terminal_fig = canvas.render(backend="plotext") print(terminal_fig.build(keep_colors=False)) ``` + Trigonometry Runtime - ┌─────────────────────────────────────────────────────────────────────────┐ - 1.00┤ ▗▄▞▀▀▀▀▀▙▄▖ │ - │ ▗▄▀▘ ▝▀▄ │ - │ ▗▞▘ ▀▄ │ - 0.67┤ ▟▀ ▀▄ │ - │ ▄▛ ▚▖ │ - 0.33┤ ▗▞ ▝▄ │ - │ ▄▀ ▚▖ │ - │▗▞▘ ▀▄ │ - 0.00┤▀ ▝▚▖ ▞│ - │ ▀▄ ▗▞▘│ - │ ▝▚ ▄▀ │ - -0.33┤ ▀▖ ▞▘ │ - │ ▝▚ ▟▀ │ - -0.67┤ ▀▄ ▄▛ │ - │ ▀▄ ▗▞▘ │ - │ ▀▄▖ ▗▄▀▘ │ - -1.00┤ ▝▀▜▄▄▄▄▄▞▀▘ │ - └┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬┘ + ┌┬─────────────────┬─────────────────┬─────────────────┬─────────────────┬┐ + 1.00┼ ▞▞ sin(x) ──▗▄▞▀▀▀▀▀▙▄▖────────────┼─────────────────┼─────────────▄▄▀▀▀┤ + │ ▞▞ cos(x) ▄▀▘ │ ▝▀▄ │ │ ▄▞▀ ││ + ││ ▜▄▘ │ ▀▄ │ │ ▄▛ ││ + 0.67┼┼──────▟▀─▀▄──────┼─────────▀▄──────┼─────────────────┼──────▄▀─────────┼┤ + ││ ▄▛ ▝▚▖ │ ▚▖ │ │ ▗▞▘ ││ + 0.33┼┼──▗▞────────▀▖───┼────────────▝▄───┼─────────────────┼───▗▀────────────┼┤ + ││ ▄▀ ▝▚ │ ▚▖ │ │ ▞▘ ││ + │▗▞▘ ▀▖│ ▀▄│ │▗▀ ││ + 0.00┼▞────────────────▝▙────────────────▝▚▖────────────────▟▘────────────────▄┤ + ││ │▜▖ │▀▄ ▗▛│ ▗▞▘│ + ││ │ ▝▙ │ ▝▚ ▟▘ │ ▄▀ ││ + -0.33┼┼─────────────────┼───▚▖────────────┼───▀▖───────▗▞───┼─────────────▞▘──┼┤ + ││ │ ▝▄▖ │ ▝▚ ▗▄▘ │ ▟▀ ││ + -0.67┼┼─────────────────┼──────▀▖─────────┼──────▀▄─▗▀──────┼─────────▄▛──────┼┤ + ││ │ ▝▜▄ │ ▄▛▘ │ ▗▞▘ ││ + ││ │ ▀▄▖ │ ▗▄▀ ▀▄▖ │ ▗▄▀▘ ││ + -1.00┼┼─────────────────┼────────────▝▀▚▄▄▄▄▄▞▀▘───────▝▀▜▄▄▄▄▄▞▀▘────────────┼┤ + └┼─────────────────┼─────────────────┼─────────────────┼─────────────────┼┘ 0.0 1.6 3.1 4.7 6.3 Duration Time @@ -194,7 +217,7 @@ Or plot with the TikZ backend: canvas.show(backend="tikzfigure") ``` -![](README_files/figure-commonmark/cell-12-output-1.png) +![](README_files/figure-commonmark/cell-14-output-1.png) ### Horizontal Subplots with TikZ Backend @@ -275,7 +298,7 @@ canvas.show(backend="plotext") 1.0 1.8 3.2 5.6 10.0 y x - + ### Layers @@ -311,7 +334,7 @@ Show layer 0 only, then layers 0 and 1, then everything: canvas.show(layers=[0]) ``` -![](README_files/figure-commonmark/cell-16-output-1.png) +![](README_files/figure-commonmark/cell-18-output-1.png) (
, array([[]], dtype=object)) @@ -322,7 +345,7 @@ Show all layers: canvas.show() ``` -![](README_files/figure-commonmark/cell-17-output-1.png) +![](README_files/figure-commonmark/cell-19-output-1.png) (
, array([[]], dtype=object)) diff --git a/README.qmd b/README.qmd index 7d4ab21..a27e50c 100644 --- a/README.qmd +++ b/README.qmd @@ -47,6 +47,30 @@ explicitly render an already-built canvas, use `canvas.render(...)`. The older `canvas.plot(backend=...)` spelling is still supported for compatibility, but emits a `FutureWarning`. +Add several lines at once with shared styling: + +```{python} +#| output: false +canvas.plot_many( + [(x, np.sin(x)), (x, np.cos(x))], + labels=["sin(x)", "cos(x)"], + linewidth=2, +) +``` + +Common figure and axis settings can be grouped with `configure()`: + +```{python} +#| output: false +canvas.configure( + title="Trigonometry", + xlabel="Angle", + ylabel="Value", + grid=True, + facecolor="whitesmoke", +) +``` + For Matplotlib-specific customization, pass method calls declaratively. Figure methods run once and axes methods run for every subplot, providing access to any Matplotlib API without requiring a maxplotlib wrapper: diff --git a/README_files/figure-commonmark/cell-14-output-1.png b/README_files/figure-commonmark/cell-14-output-1.png index ed52dae..fee6ca2 100644 Binary files a/README_files/figure-commonmark/cell-14-output-1.png and b/README_files/figure-commonmark/cell-14-output-1.png differ diff --git a/src/maxplotlib/canvas/canvas.py b/src/maxplotlib/canvas/canvas.py index f65ff7a..fbcda42 100644 --- a/src/maxplotlib/canvas/canvas.py +++ b/src/maxplotlib/canvas/canvas.py @@ -459,6 +459,47 @@ def add_line( **kwargs, ) + def plot_many( + self, + series, + labels=None, + layer=0, + row: int | None = None, + col: int | None = None, + **kwargs, + ): + """Add several lines to the canvas and return the canvas. + + ``series`` is an iterable of ``(x, y)`` pairs. Shared line keyword + arguments are passed through ``kwargs``; individual labels can be + supplied with ``labels``. + """ + if labels is not None: + labels = list(labels) + count = 0 + for index, values in enumerate(series): + count += 1 + try: + x, y = values + except (TypeError, ValueError) as exc: + raise ValueError("each series entry must be an (x, y) pair") from exc + line_kwargs = dict(kwargs) + if labels is not None: + if index >= len(labels): + raise ValueError("labels must contain one label per series") + line_kwargs["label"] = labels[index] + self.add_line( + x, + y, + layer=layer, + row=row, + col=col, + **line_kwargs, + ) + if labels is not None and len(labels) != count: + raise ValueError("labels must contain one label per series") + return self + def _get_or_create_subplot(self, row, col): """Return the subplot at (row, col), creating it if needed.""" if row is not None and col is not None: @@ -985,6 +1026,60 @@ def set_title( """Set the title and text properties for a subplot.""" self._get_or_create_subplot(row, col).set_title(title, **kwargs) + def configure( + self, + *, + title=None, + xlabel=None, + ylabel=None, + grid=None, + facecolor=None, + axisbelow=None, + margins=None, + xscale=None, + yscale=None, + xlim=None, + ylim=None, + tight_layout=False, + row: int | None = None, + col: int | None = None, + ): + """Apply common figure and axis settings, returning the canvas. + + ``title``, ``xlabel``, and ``ylabel`` are figure-level settings. Axis + settings are applied to the selected subplot, or the default subplot + when ``row`` and ``col`` are omitted. Use ``tight_layout=True`` for a + final layout pass before rendering. + """ + if title is not None: + self.suptitle(title) + if xlabel is not None: + self.supxlabel(xlabel) + if ylabel is not None: + self.supylabel(ylabel) + if grid is not None: + self.set_grid(grid, row=row, col=col) + if facecolor is not None: + self.set_facecolor(facecolor, row=row, col=col) + if axisbelow is not None: + self.set_axisbelow(axisbelow, row=row, col=col) + if margins is not None: + if isinstance(margins, dict): + self.margins(row=row, col=col, **margins) + else: + self.margins(margins, row=row, col=col) + if xscale is not None: + self.set_xscale(xscale, row=row, col=col) + if yscale is not None: + self.set_yscale(yscale, row=row, col=col) + if xlim is not None: + self.set_xlim(*xlim, row=row, col=col) + if ylim is not None: + self.set_ylim(*ylim, row=row, col=col) + if tight_layout: + self.tight_layout() + return self + def set_xlim( self, left=None, right=None, row: int | None = None, col: int | None = None ): diff --git a/src/maxplotlib/tests/test_canvas.py b/src/maxplotlib/tests/test_canvas.py index 38c020e..8a6de64 100644 --- a/src/maxplotlib/tests/test_canvas.py +++ b/src/maxplotlib/tests/test_canvas.py @@ -160,7 +160,7 @@ def test_canvas_matplotlib_gridspec_kw_affects_2x2_line_spacing(): for ax in row_axes: ax.plot(x, (idx + 1) * x) idx += 1 - tight_fig, tight_matplotlib_axes = tight_canvas.plot(backend="matplotlib") + tight_fig, tight_matplotlib_axes = tight_canvas.render(backend="matplotlib") tight_hgap = ( tight_matplotlib_axes[0, 1].get_position().x0 - tight_matplotlib_axes[0, 0].get_position().x1 @@ -183,7 +183,7 @@ def test_canvas_matplotlib_gridspec_kw_affects_2x2_line_spacing(): for ax in row_axes: ax.plot(x, (idx + 1) * x) idx += 1 - loose_fig, loose_matplotlib_axes = loose_canvas.plot(backend="matplotlib") + loose_fig, loose_matplotlib_axes = loose_canvas.render(backend="matplotlib") loose_hgap = ( loose_matplotlib_axes[0, 1].get_position().x0 - loose_matplotlib_axes[0, 0].get_position().x1 @@ -222,7 +222,7 @@ def test_canvas_matplotlib_gridspec_kw_affects_2x2_imshow_spacing(): ax.add_imshow(data + idx, cmap="viridis") ax.set_title(f"Heatmap {idx + 1}") idx += 1 - tight_fig, tight_matplotlib_axes = tight_canvas.plot(backend="matplotlib") + tight_fig, tight_matplotlib_axes = tight_canvas.render(backend="matplotlib") tight_hgap = ( tight_matplotlib_axes[0, 1].get_position().x0 - tight_matplotlib_axes[0, 0].get_position().x1 @@ -246,7 +246,7 @@ def test_canvas_matplotlib_gridspec_kw_affects_2x2_imshow_spacing(): ax.add_imshow(data + idx, cmap="viridis") ax.set_title(f"Heatmap {idx + 1}") idx += 1 - loose_fig, loose_matplotlib_axes = loose_canvas.plot(backend="matplotlib") + loose_fig, loose_matplotlib_axes = loose_canvas.render(backend="matplotlib") loose_hgap = ( loose_matplotlib_axes[0, 1].get_position().x0 - loose_matplotlib_axes[0, 0].get_position().x1 @@ -672,7 +672,7 @@ def test_contour_labels_and_rasterization_zorder_are_supported(): axis.clabel(inline=True, fontsize=8) axis.set_rasterization_zorder(2) - fig, axes = canvas.plot(backend="matplotlib") + fig, axes = canvas.render(backend="matplotlib") assert len(axes[0, 0].texts) > 0 assert axes[0, 0].get_rasterization_zorder() == 2 @@ -694,7 +694,7 @@ def test_axis_layout_and_log_shortcuts_are_supported(): axis.set_xticklabels(["one", "two", "four"], rotation=30) axis.set_yticklabels(["low", "high"], color="navy") - fig, axes = canvas.plot(backend="matplotlib") + fig, axes = canvas.render(backend="matplotlib") matplotlib_axis = axes[0, 0] assert matplotlib_axis.get_xscale() == "log" @@ -718,7 +718,7 @@ def test_secondary_axes_are_supported_by_matplotlib(): "right", functions=(lambda y: y + 1, lambda y: y - 1), label="offset" ) - fig, axes = canvas.plot(backend="matplotlib") + fig, axes = canvas.render(backend="matplotlib") assert len(axes[0, 0].child_axes) == 2 plt.close(fig) @@ -734,7 +734,7 @@ def test_twiny_is_supported_by_matplotlib(): axis.plot([0, 1], [0, 1]) secondary.plot([10, 20], [0, 1], color="red") - fig, axes = canvas.plot(backend="matplotlib") + fig, axes = canvas.render(backend="matplotlib") assert canvas.twiny_axes[(0, 0)] is not axes[0, 0] plt.close(fig) @@ -759,7 +759,7 @@ def test_axis_state_setter_aliases_are_supported(): axis.set_xbound(-1, 2) axis.set_ybound(-2, 3) - fig, axes = canvas.plot(backend="matplotlib") + fig, axes = canvas.render(backend="matplotlib") matplotlib_axis = axes[0, 0] assert matplotlib_axis.get_frame_on() is False @@ -805,7 +805,7 @@ def test_generic_axis_setters_and_metadata_getters_are_supported(): axis.update({"aspect": "equal"}) axis.invert_xaxis() - fig, axes = canvas.plot(backend="matplotlib") + fig, axes = canvas.render(backend="matplotlib") matplotlib_axis = axes[0, 0] assert canvas.get_suptitle() == "Figure title" @@ -835,7 +835,7 @@ def test_figure_layout_helpers_and_aliases_are_supported(): canvas.align_ylabels() canvas.autofmt_xdate(rotation=20) - fig, axes = canvas.plot(backend="matplotlib") + fig, axes = canvas.render(backend="matplotlib") assert axes[0, 0].get_legend() is not None assert len(axes[0, 0].tables) == 1 @@ -913,6 +913,49 @@ def test_legacy_plot_backend_form_warns(): canvas.plot(backend="plotly") +def test_plot_many_adds_labeled_lines_and_returns_canvas(): + from maxplotlib import Canvas + + canvas = Canvas() + result = canvas.plot_many( + [([0, 1], [0, 1]), ([0, 1], [1, 0])], + labels=["up", "down"], + ) + + assert result is canvas + assert [ + line["kwargs"]["label"] + for line in canvas._subplot_matrix[0][0].layered_line_data[0] + ] == [ + "up", + "down", + ] + + +def test_configure_applies_common_canvas_settings(): + from maxplotlib import Canvas + + canvas = Canvas() + canvas.plot([0, 1], [0, 1]).configure( + title="Example", + xlabel="Time", + ylabel="Value", + grid=True, + facecolor="whitesmoke", + xlim=(0, 2), + ylim=(-1, 2), + ) + + subplot = canvas._subplot_matrix[0][0] + assert canvas._suptitle == "Example" + assert canvas._supxlabel == "Time" + assert canvas._supylabel == "Value" + assert subplot._grid is True + assert subplot._facecolor == "whitesmoke" + assert (subplot._xmin, subplot._xmax) == (0, 2) + assert (subplot._ymin, subplot._ymax) == (-1, 2) + + def test_matplotlib_postprocess_can_customize_figure_and_axes(): import matplotlib.pyplot as plt from matplotlib.colors import to_rgba @@ -995,7 +1038,7 @@ def test_matplotlib_postprocess_rejects_non_matplotlib_backends(): from maxplotlib import Canvas with pytest.raises(ValueError, match="only supported with the matplotlib backend"): - Canvas().plot(backend="plotly", matplotlib_postprocess=lambda fig, axes: None) + Canvas().render(backend="plotly", matplotlib_postprocess=lambda fig, axes: None) def test_show_canvas_script_invokes_canvas_show(monkeypatch): diff --git a/src/maxplotlib/tests/test_plot.py b/src/maxplotlib/tests/test_plot.py index 7667ed3..3e65d65 100644 --- a/src/maxplotlib/tests/test_plot.py +++ b/src/maxplotlib/tests/test_plot.py @@ -19,7 +19,7 @@ def test_python_example_nxm_line_subplots_spacing_changes(): for i, row in enumerate(tight_axes): for j, ax in enumerate(row): ax.plot(x, np.sin((i + 1) * (j + 1) * x)) - tight_fig, tight_m_axes = tight_canvas.plot(backend="matplotlib") + tight_fig, tight_m_axes = tight_canvas.render(backend="matplotlib") tight_hgap = ( tight_m_axes[0, 1].get_position().x0 - tight_m_axes[0, 0].get_position().x1 ) @@ -38,7 +38,7 @@ def test_python_example_nxm_line_subplots_spacing_changes(): for i, row in enumerate(loose_axes): for j, ax in enumerate(row): ax.plot(x, np.sin((i + 1) * (j + 1) * x)) - loose_fig, loose_m_axes = loose_canvas.plot(backend="matplotlib") + loose_fig, loose_m_axes = loose_canvas.render(backend="matplotlib") loose_hgap = ( loose_m_axes[0, 1].get_position().x0 - loose_m_axes[0, 0].get_position().x1 ) @@ -69,7 +69,7 @@ def test_python_example_nxm_color_subplots_spacing_changes(): for ax in row: ax.add_imshow(base + idx, cmap="viridis") idx += 1 - tight_fig, tight_m_axes = tight_canvas.plot(backend="matplotlib") + tight_fig, tight_m_axes = tight_canvas.render(backend="matplotlib") tight_hgap = ( tight_m_axes[0, 1].get_position().x0 - tight_m_axes[0, 0].get_position().x1 ) @@ -90,7 +90,7 @@ def test_python_example_nxm_color_subplots_spacing_changes(): for ax in row: ax.add_imshow(base + idx, cmap="viridis") idx += 1 - loose_fig, loose_m_axes = loose_canvas.plot(backend="matplotlib") + loose_fig, loose_m_axes = loose_canvas.render(backend="matplotlib") loose_hgap = ( loose_m_axes[0, 1].get_position().x0 - loose_m_axes[0, 0].get_position().x1 ) diff --git a/src/maxplotlib/tests/test_plotext.py b/src/maxplotlib/tests/test_plotext.py index ee34a63..a43b6fd 100644 --- a/src/maxplotlib/tests/test_plotext.py +++ b/src/maxplotlib/tests/test_plotext.py @@ -27,7 +27,7 @@ def test_canvas_plot_plotext_builds_terminal_output(): ax.set_legend(True) canvas.suptitle("Plotext demo") - figure = canvas.plot(backend="plotext") + figure = canvas.render(backend="plotext") output = strip_ansi(figure.build()) assert isinstance(figure, PlotextFigure) @@ -57,7 +57,7 @@ def test_canvas_plot_plotext_supports_scalar_errorbars(): ax.set_xscale("log") ax.set_title("Log errors") - output = strip_ansi(canvas.plot(backend="plotext").build()) + output = strip_ansi(canvas.render(backend="plotext").build()) assert "Log errors" in output @@ -72,7 +72,7 @@ def test_canvas_plot_plotext_supports_fill_between_curves_and_annotations(): ax.set_title("Filled band") ax.set_legend(True) - output = strip_ansi(canvas.plot(backend="plotext").build()) + output = strip_ansi(canvas.render(backend="plotext").build()) assert "Filled band" in output assert "band" in output @@ -88,7 +88,7 @@ def test_canvas_plot_plotext_supports_matrix_plots_and_patches(): ax.add_patch(mpatches.Circle((1.8, 1.8), 0.4, fill=False, edgecolor="cyan")) ax.set_title("Matrix plot") - output = strip_ansi(canvas.plot(backend="plotext").build()) + output = strip_ansi(canvas.render(backend="plotext").build()) assert "Matrix plot" in output @@ -98,7 +98,7 @@ def test_canvas_plot_plotext_supports_colorbar_notes_symlog_aspect_and_generic_p ax.add_imshow(np.eye(3)) ax.add_colorbar(label="scale") ax.set_title("Heatmap") - output = strip_ansi(canvas.plot(backend="plotext").build()) + output = strip_ansi(canvas.render(backend="plotext").build()) assert "Heatmap" in output assert "scale:" in output @@ -111,7 +111,7 @@ def test_canvas_plot_plotext_supports_colorbar_notes_symlog_aspect_and_generic_p ax.set_aspect("equal") ax.add_caption("caption text") ax.set_title("Symlog view") - output = strip_ansi(canvas.plot(backend="plotext").build()) + output = strip_ansi(canvas.render(backend="plotext").build()) assert "Symlog view" in output assert "caption text" in output @@ -129,7 +129,7 @@ def test_canvas_plot_plotext_supports_colorbar_notes_symlog_aspect_and_generic_p ) ax.set_title("Generic patch") ax.set_legend(True) - output = strip_ansi(canvas.plot(backend="plotext").build()) + output = strip_ansi(canvas.render(backend="plotext").build()) assert "Generic patch" in output assert "ellipse" in output diff --git a/src/maxplotlib/tests/test_plotly_backend.py b/src/maxplotlib/tests/test_plotly_backend.py index 9fde583..8b0f3a9 100644 --- a/src/maxplotlib/tests/test_plotly_backend.py +++ b/src/maxplotlib/tests/test_plotly_backend.py @@ -18,7 +18,7 @@ def test_plotly_backend_supports_common_primitives(): ax.set_grid(True) ax.set_legend(True) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert fig is not None assert len(fig.data) >= 4 # line, scatter, errorbar, fill_between @@ -35,7 +35,7 @@ def test_plotly_backend_supports_tick_label_rotation(): axis.plot([0, 1], [0, 1]) axis.set_xticks([0, 1], labels=["zero", "one"], rotation=45) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert fig.layout.xaxis.tickangle == 45 @@ -49,7 +49,7 @@ def test_plotly_backend_supports_twinx(): secondary.plot([0, 1], [10, 20], color="red") secondary.set_ylabel("Secondary") - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert len(fig.data) == 2 assert fig.layout.yaxis2.title.text == "Secondary" @@ -66,7 +66,7 @@ def test_plotly_backend_supports_common_added_primitives(): axis.axhspan(0.25, 0.75, alpha=0.1) axis.arrow(0, 0, 1, 1) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert len(fig.data) >= 3 assert len(fig.layout.shapes) >= 2 @@ -82,7 +82,7 @@ def test_plotly_backend_supports_step_stairs_broken_barh_and_pie(): axis.broken_barh([(0, 1), (2, 0.5)], (0, 0.5)) axis.pie([2, 3, 4], labels=["A", "B", "C"]) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert any(trace.type == "pie" for trace in fig.data) assert len(fig.data) >= 5 @@ -98,7 +98,7 @@ def test_plotly_backend_supports_statistical_and_event_plots(): axis.violinplot([[1, 2, 3], [2, 4, 5]]) axis.eventplot([[0.2, 0.5], [1.0, 1.5]]) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert any(trace.type == "box" for trace in fig.data) assert any(trace.type == "violin" for trace in fig.data) @@ -122,7 +122,7 @@ def test_plotly_backend_supports_scientific_field_plots(): axis.hexbin(xx.ravel(), yy.ravel(), gridsize=5) axis.matshow(z) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert any(trace.type == "contour" for trace in fig.data) assert any(trace.type == "heatmap" for trace in fig.data) @@ -138,7 +138,7 @@ def test_plotly_backend_supports_contour_labels(): axis.contour(x, x, xx**2 + yy**2) axis.clabel(fontsize=10, color="black") - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert fig.data[0].contours.showlabels is True assert fig.data[0].contours.labelfont.size == 10 @@ -152,7 +152,7 @@ def test_plotly_backend_supports_fill_log_scales_and_ticklabels(): axis.loglog([1, 2, 4], [1, 4, 16]) axis.set_xticklabels(["one", "two", "four"], color="navy") - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert any(trace.fill == "toself" for trace in fig.data) assert fig.layout.xaxis.type == "log" @@ -168,8 +168,8 @@ def test_plotly_backend_respects_layers(): ax.plot(x, x, color="black", label="L0", layer=0) ax.plot(x, x**2, color="red", label="L1", layer=1) - fig0 = canvas.plot(backend="plotly", layers=[0]) - fig1 = canvas.plot(backend="plotly", layers=[1]) + fig0 = canvas.render(backend="plotly", layers=[0]) + fig1 = canvas.render(backend="plotly", layers=[1]) assert len(fig0.data) == 1 assert len(fig1.data) == 1 @@ -203,7 +203,7 @@ def test_plotly_backend_supports_common_patches_and_symlog(): ax.set_title("patches") ax.set_legend(True) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert fig is not None assert len(getattr(fig.layout, "shapes", []) or []) >= 4 # patch labels become dummy legend traces @@ -214,7 +214,7 @@ def test_plotly_backend_supports_common_patches_and_symlog(): ax2.plot(x, x**3, color="cyan", label="x^3") ax2.set_xscale("symlog") ax2.set_yscale("symlog") - fig2 = canvas2.plot(backend="plotly") + fig2 = canvas2.render(backend="plotly") assert fig2 is not None @@ -230,7 +230,7 @@ def test_plotly_backend_renders_mixed_vector_primitives(): np.ones((3, 3)), ) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert len(fig.data) > 1 @@ -241,7 +241,7 @@ def test_plotly_backend_supports_bar_labels(): axis.bar([0, 1], [2, 3]) axis.bar_label(fmt="%d") - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") labels = [annotation.text for annotation in fig.layout.annotations] assert labels[-2:] == ["2", "3"] @@ -264,7 +264,7 @@ def test_plotly_backend_supports_pseudocolor_spy_table_and_triplot(): axis.table(cellText=[["A", "B"], ["1", "2"]]) axis.triplot(points_x, points_y, triangles=triangles) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert sum(trace.type == "heatmap" for trace in fig.data) >= 3 assert any(trace.type == "table" for trace in fig.data) @@ -277,7 +277,7 @@ def test_plotly_backend_supports_quiver(): canvas, axis = Canvas.subplots() axis.quiver([0, 1], [0, 1], [1, -1], [1, 1], color="purple", alpha=0.5) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") arrows = [ annotation for annotation in fig.layout.annotations if annotation.showarrow @@ -297,7 +297,7 @@ def test_plotly_backend_supports_tripcolor(): triangles=[[0, 1, 2], [1, 3, 2]], ) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert sum(trace.fill == "toself" for trace in fig.data) == 2 @@ -313,7 +313,7 @@ def test_plotly_backend_supports_triangulated_contours(): axis.tricontour(x, y, values, triangles=triangles, levels=3) axis.tricontourf(x, y, values, triangles=triangles, levels=3) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert any(trace.fill is None for trace in fig.data) assert any(trace.fill == "toself" for trace in fig.data) @@ -332,7 +332,7 @@ def test_plotly_backend_supports_streamplot(): color="darkgreen", ) - fig = canvas.plot(backend="plotly") + fig = canvas.render(backend="plotly") assert len(fig.data) > 0 assert all(trace.type == "scatter" for trace in fig.data)