Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support format string for with_text functions #29

Merged
merged 7 commits into from
Feb 27, 2024
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
5 changes: 2 additions & 3 deletions docs/_scripts/_screenshots.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
from whitecanvas.theme import update_default

DOCS: Path = Path(__file__).parent.parent
# CODE_BLOCK = re.compile("``` ?python\n([^`]*)```", re.DOTALL)
CODE_BLOCK = re.compile("``` ?python.*?\n([^`]*)```")

def _exec_code(src: str, ns: dict, dest: str) -> dict[str, Any]:
Expand All @@ -32,9 +31,9 @@ def _exec_code(src: str, ns: dict, dest: str) -> dict[str, Any]:

def _write_image(src: str, ns: dict, dest: str) -> None:
ns = _exec_code(src, ns, dest)
if isinstance(ns.get("grid", None), CanvasGrid):
if "grid" in src and isinstance(ns.get("grid", None), CanvasGrid):
canvas = ns["grid"]
else:
elif "canvas" in src:
canvas = ns["canvas"]
assert isinstance(canvas, (CanvasGrid, SingleCanvas)), type(canvas)
with mkdocs_gen_files.open(dest, "wb") as f:
Expand Down
16 changes: 13 additions & 3 deletions docs/categorical/num_num.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,11 @@ import numpy as np
rng = np.random.default_rng(12345)

# sample data
steps = np.array([0] * 60 + [3] * 30 + [6] * 40)
df = {
"label": ["A"] * 60 + ["B"] * 30 + ["C"] * 40,
"X": rng.normal(loc=0.0, size=130),
"Y": rng.normal(loc=1.0, size=130),
"X": rng.normal(loc=0.0, size=130) + steps,
"Y": rng.normal(loc=1.0, size=130) + steps / 2,
}
```

Expand Down Expand Up @@ -145,7 +146,16 @@ canvas.show()
``` python
#!name: cat_hist2d
canvas = new_canvas("matplotlib")
canvas.cat(df, x="X", y="Y").add_hist2d(cmap=["white", "blue"], bins=(8, 10))
canvas.cat(df, x="X", y="Y").add_hist2d(bins=(8, 10), color="label")
canvas.show()
```

and similarly, 2-dimensional KDE can be added by `add_kde2d`.

``` python
#!name: cat_kde2d
canvas = new_canvas("matplotlib")
canvas.cat(df, x="X", y="Y").add_kde2d(color="label")
canvas.show()
```

Expand Down
62 changes: 59 additions & 3 deletions docs/layers/layer_groups.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ print(canvas.layers) # LayerList([Plot<'myplot'>])
The `Plot` layer can be further converted into a `LabeledPlot` layer by adding error
bars using `with_xerr` and/or `with_yerr` method.

``` python
``` python hl_lines="9 10"
#!name: layer_groups_line_markers_yerr
from whitecanvas import new_canvas

Expand Down Expand Up @@ -85,7 +85,7 @@ Both cases can be achieved using the `Line` methods.
To fill the area that represents the errors, such as confidence interval and standard
deviation, use the `with_xband` or `with_yband` method.

``` python
``` python hl_lines="9 15"
#!name: layer_groups_line_yband
from whitecanvas import new_canvas

Expand All @@ -110,7 +110,7 @@ canvas.show()
To fill the area between the line and the axis, use the `with_xfill` or `with_yfill`
respectively.

``` python
``` python hl_lines="8 13"
#!name: layer_groups_line_xfill
from whitecanvas import new_canvas

Expand All @@ -135,6 +135,62 @@ canvas.show()
orientation of the filling is in the direction of the y-axis, consistent with the
methods such as `with_xband` and `with_xerr`.

### Add texts

Adding text at the data points can be done using the `with_text` method.

``` python hl_lines="8"
#!name: layer_groups_line_text
from whitecanvas import new_canvas

canvas = new_canvas("matplotlib")

(
canvas
.add_line([0, 1, 2], [3, 2, 4], color="black")
.with_text(["i=0", "i=1", "i=2"], size=20, color="red")
)
canvas.show()
```

You can use text alignment and text offset to adjust the position of the text.

``` python hl_lines="9 14"
#!name: layer_groups_line_text_align
from whitecanvas import new_canvas

canvas = new_canvas("matplotlib")

(
canvas
.add_line([0, 1, 2], [3, 2, 4], color="black")
.with_text(["i=0", "i=1", "i=2"], size=20, color="red")
.with_text_offset(0.2, 0.0)
)
(
canvas
.add_line([3, 4, 5], [3, 2, 4], color="black")
.with_text(["i=0", "i=1", "i=2"], size=20, color="green", anchor="top")
)
canvas.show()
```

You can use "x", "y" and "i" as the placeholders in the text to format the text using
the x/y data and the indices.

``` python hl_lines="8"
#!name: layer_groups_line_text_format
from whitecanvas import new_canvas

canvas = new_canvas("matplotlib")

(
canvas
.add_line([0, 1, 2], [3, 2, 4], color="black")
.with_text("x={x:.2f}, y={y:.2f}, i={i}", size=20, color="red")
)
```

## Layer Groups with Markers

### Markers with error bars
Expand Down
70 changes: 70 additions & 0 deletions docs/layers/texts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Texts

Text layer is a collection of text elements. It is usually created by the [`add_text`]
[whitecanvas.canvas.CanvasBase.add_text] method.

A text can be added by specifying the x and y coordinates, and the text string.

``` python
#!name: text_layer_scalar
from whitecanvas import new_canvas

canvas = new_canvas("matplotlib")
canvas.add_text(0, 0, "Hello, World!", size=20, color="black")
canvas.show()
```

Multiple texts can be added by specifying the array of x and y coordinates, and the text
strings.

``` python
#!name: text_layer
from whitecanvas import new_canvas

canvas = new_canvas("matplotlib")
x = [0, 1, 2, 3]
y = [0, 1, 0, 1]
texts = [f"(x={x[i]}, y={y[i]})" for i in range(4)]
canvas.add_text(x, y, texts, size=20, color="black")
canvas.x.lim = (-1, 4)
canvas.y.lim = (-1, 2)
canvas.show()
```

The `anchor=` argument can be used to specify the text anchor position.

``` python
#!name: text_layer_anchor
from whitecanvas import new_row

grid = new_row(2, backend="matplotlib").fill()

grid[0].add_markers([0], [0])
for anchor in ["top", "bottom", "left", "right"]:
grid[0].add_text(0, 0, anchor, size=20, color="black", anchor=anchor)

grid[1].add_markers([0], [0])
for anchor in ["top_left", "top_right", "bottom_left", "bottom_right"]:
grid[1].add_text(0, 0, anchor, size=20, color="black", anchor=anchor)

grid.show()
```

`matplotlib`, `pyqtgraph` and `bokeh` backends support background rectangles for the
texts. As for the layers with both faces and edges, `with_face` and `with_edge` methods
can be used to set the background face and edge properties.

``` python hl_lines="6 7"
#!name: text_layer_with_face
from whitecanvas import new_canvas

canvas = new_canvas("matplotlib")
(
canvas.add_text(x, y, texts, size=20, color="black")
.with_face(color="yellow")
.with_edge(color="red", width=2, style="--")
)
canvas.x.lim = (-1, 4)
canvas.y.lim = (-1, 2)
canvas.show()
```
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -217,4 +217,5 @@ exclude_lines = [
"no cov",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
"@overload",
]
11 changes: 9 additions & 2 deletions tests/test_canvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -153,9 +153,11 @@ def test_legend(backend: str):
canvas.add_markers([0, 1, 2], [0, 1, 2], name="markers")
canvas.add_bars([0, 1, 2], [0, 1, 2], name="bars")
canvas.add_line([3, 4, 5], [1, 2, 1], name="plot").with_markers()
canvas.add_line([3, 4, 5], [1, 2, 1], name="plot").with_xband([1, 1, 1])
canvas.add_line([3, 4, 5], [1, 2, 1], name="plot").with_yband([1, 1, 1])
canvas.add_line([3, 4, 5], [2, 3, 2], name="line+err").with_yerr([1, 1, 1])
canvas.add_markers([3, 4, 5], [3, 4, 3], name="markers+err").with_xerr([1, 1, 1])
canvas.add_line([3, 4, 5], [4, 5, 4], name="plot+err").with_markers().with_xerr([1, 1, 1])
canvas.add_line([3, 4, 5], [4, 5, 4], name="plot+err").with_markers().with_xerr([1, 1, 1]).with_yerr([1, 1, 1])
canvas.add_markers([3, 4, 5], [5, 6, 5], name="markers+err+err").with_stem()
canvas.add_legend(location="bottom_right")

Expand All @@ -177,5 +179,10 @@ def test_multidim():
x = np.arange(5)
ys = [x, x ** 2, x ** 3]
canvas.dims.add_line(x, ys)
img = np.zeros((3, 5, 5))
canvas.dims.add_markers(x, ys, color="red", symbol="+")
canvas.dims.add_rug(ys)
canvas.dims.add_band(x, ys, [y0 + 1 for y0 in ys])
canvas.dims.add_errorbars(x, ys, [y - 1 for y in ys])
canvas.dims.add_text(x, ys, ["a", "b", "c", "d", "e"])
img = np.zeros((2, 5, 5))
canvas.dims.add_image(img)
Loading
Loading