Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 46 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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

Expand All @@ -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

Expand Down Expand Up @@ -275,7 +298,7 @@ canvas.show(backend="plotext")
1.0 1.8 3.2 5.6 10.0
y x

<maxplotlib.backends.plotext.figure.PlotextFigure at 0x10ee1ce10>
<maxplotlib.backends.plotext.figure.PlotextFigure at 0x1102a0690>

### Layers

Expand Down Expand Up @@ -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)

(<Figure size 590.551x324.803 with 1 Axes>,
array([[<Axes: xlabel='x'>]], dtype=object))
Expand All @@ -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)

(<Figure size 590.551x324.803 with 1 Axes>,
array([[<Axes: xlabel='x'>]], dtype=object))
24 changes: 24 additions & 0 deletions README.qmd
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Binary file modified README_files/figure-commonmark/cell-14-output-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
95 changes: 95 additions & 0 deletions src/maxplotlib/canvas/canvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down Expand Up @@ -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
):
Expand Down
Loading
Loading