Skip to content

Commit 9b149ba

Browse files
committed
Enhance documentation for figure, markers, and widget classes with detailed parameter descriptions, examples, and return values
1 parent a77ee38 commit 9b149ba

3 files changed

Lines changed: 476 additions & 25 deletions

File tree

anyplotlib/figure.py

Lines changed: 66 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,44 @@
3737
class Figure(anywidget.AnyWidget):
3838
"""Multi-panel interactive figure widget.
3939
40-
Create via :func:`subplots` or directly::
40+
The top-level container for all plots and the only ``anywidget.AnyWidget``
41+
subclass in anyplotlib. It owns all traitlets and acts as the Python ↔
42+
JavaScript bridge via the ``figure_esm.js`` canvas renderer.
43+
44+
Create via :func:`subplots` (recommended) or directly::
4145
4246
fig = Figure(2, 2, figsize=(800, 600))
4347
ax = fig.add_subplot((0, 0))
4448
v2d = ax.imshow(data)
49+
50+
Parameters
51+
----------
52+
nrows, ncols : int, optional
53+
Grid dimensions. Default 1 row, 1 column.
54+
figsize : (width, height), optional
55+
Figure size in CSS pixels. Default ``(640, 480)``.
56+
width_ratios : list of float, optional
57+
Relative column widths. Length must equal *ncols*.
58+
height_ratios : list of float, optional
59+
Relative row heights. Length must equal *nrows*.
60+
sharex, sharey : bool, optional
61+
Link pan/zoom across all panels on the respective axis.
62+
Default False (independent pan/zoom per panel).
63+
64+
Attributes
65+
----------
66+
fig_width : int
67+
Current figure width in pixels (synced with JS).
68+
fig_height : int
69+
Current figure height in pixels (synced with JS).
70+
layout_json : str
71+
JSON serialization of the grid layout (synced with JS).
72+
event_json : str
73+
JSON serialization of interaction events from JS.
74+
75+
See Also
76+
--------
77+
subplots : Recommended factory for creating Figure and Axes grid.
4578
"""
4679

4780
layout_json = traitlets.Unicode("{}").tag(sync=True)
@@ -75,11 +108,29 @@ def add_subplot(self, spec) -> Axes:
75108
76109
Parameters
77110
----------
78-
spec : SubplotSpec | int | (row, col) tuple
111+
spec : SubplotSpec or int or (row, col) tuple
112+
Specifies which grid cell(s) to occupy:
79113
- ``SubplotSpec``: used directly (e.g. from ``GridSpec[r, c]``).
80114
- ``int``: converted to ``(row, col)`` via ``divmod(spec, ncols)``,
81115
matching ``matplotlib.Figure.add_subplot(num)`` numbering.
82116
- ``(row, col)`` tuple: selects a single cell.
117+
118+
Returns
119+
-------
120+
Axes
121+
The subplot axes object. Call plotting methods like ``.imshow()``,
122+
``.plot()``, ``.bar()`` to attach data.
123+
124+
Raises
125+
------
126+
TypeError
127+
If *spec* is not a SubplotSpec, int, or tuple.
128+
129+
Examples
130+
--------
131+
>>> fig = Figure(2, 2)
132+
>>> ax1 = fig.add_subplot(0) # top-left (via numbering)
133+
>>> ax2 = fig.add_subplot((0, 1)) # top-right (via tuple)
83134
"""
84135
if isinstance(spec, SubplotSpec):
85136
pass # use as-is
@@ -229,6 +280,14 @@ def _push_widget(self, panel_id: str, widget_id: str, fields: dict) -> None:
229280

230281
# ── helpers ───────────────────────────────────────────────────────────────
231282
def get_axes(self) -> list:
283+
"""Return a list of all Axes, sorted by grid position.
284+
285+
Returns
286+
-------
287+
list of Axes
288+
Axes sorted by (row_start, col_start) to match typical left-to-right,
289+
top-to-bottom iteration order.
290+
"""
232291
return sorted(self._axes_map.values(),
233292
key=lambda a: (a._spec.row_start, a._spec.col_start))
234293

@@ -238,6 +297,11 @@ def _repr_html_(self) -> str:
238297
Used by Sphinx Gallery (via :class:`~docs._sg_html_scraper.ViewerScraper`)
239298
and by any HTML-capable notebook frontend that falls back to
240299
``_repr_html_`` instead of the full ipywidgets protocol.
300+
301+
Returns
302+
-------
303+
str
304+
HTML string containing an embedded iframe with srcdoc attribute.
241305
"""
242306
from anyplotlib._repr_utils import repr_html_iframe
243307
return repr_html_iframe(self)

anyplotlib/markers.py

Lines changed: 157 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -99,14 +99,22 @@ def __init__(self, marker_type: str, name: str, kwargs: dict, push_fn):
9999

100100
# ------------------------------------------------------------------
101101
def set(self, **kwargs) -> None:
102-
"""Update one or more properties and push the change to the plot."""
102+
"""Update one or more properties and push the change to the plot.
103+
104+
Parameters
105+
----------
106+
**kwargs : dict
107+
Properties to update (e.g., offsets, radius, facecolors).
108+
Matplotlib-style names are translated to wire format.
109+
"""
103110
self._data.update(kwargs)
104111
self._push_fn()
105112

106113
def __repr__(self) -> str: # pragma: no cover
107114
return f"MarkerGroup(type={self._type!r}, name={self._name!r}, n={self._count()})"
108115

109116
def _count(self) -> int:
117+
"""Return the number of markers in this group."""
110118
offs = self._data.get("offsets")
111119
if offs is None:
112120
return 0
@@ -119,7 +127,19 @@ def _count(self) -> int:
119127
# Wire-format serialisation
120128
# ------------------------------------------------------------------
121129
def to_wire(self, group_id: str) -> dict:
122-
"""Return a dict in the JS wire format for this marker group."""
130+
"""Return a dict in the JS wire format for this marker group.
131+
132+
Parameters
133+
----------
134+
group_id : str
135+
Unique identifier for this marker group (usually UUID).
136+
137+
Returns
138+
-------
139+
dict
140+
Wire-format dict with type-specific structure, ready for JSON
141+
serialization and transmission to the JavaScript renderer.
142+
"""
123143
d = self._data
124144
t = self._type
125145

@@ -333,6 +353,13 @@ class MarkerTypeDict:
333353
334354
Any modification (``__setitem__``, ``__delitem__``) automatically triggers
335355
the ``_push_fn`` callback so the plot re-renders.
356+
357+
Parameters
358+
----------
359+
marker_type : str
360+
Type of markers (e.g., 'circles', 'arrows', 'lines').
361+
push_fn : callable
362+
Zero-arg callback to trigger re-render on mutations.
336363
"""
337364

338365
def __init__(self, marker_type: str, push_fn):
@@ -343,38 +370,112 @@ def __init__(self, marker_type: str, push_fn):
343370
# ------------------------------------------------------------------
344371
# dict-like interface
345372
def __getitem__(self, name: str) -> MarkerGroup:
373+
"""Return a MarkerGroup by name.
374+
375+
Parameters
376+
----------
377+
name : str
378+
Name of the marker group.
379+
380+
Returns
381+
-------
382+
MarkerGroup
383+
The requested marker group.
384+
385+
Raises
386+
------
387+
KeyError
388+
If the name is not found.
389+
"""
346390
return self._groups[name]
347391

348392
def __setitem__(self, name: str, group: MarkerGroup) -> None:
393+
"""Register a MarkerGroup and trigger re-render.
394+
395+
Parameters
396+
----------
397+
name : str
398+
Name to register the group under.
399+
group : MarkerGroup
400+
The marker group object.
401+
"""
349402
self._groups[name] = group
350403
self._push_fn()
351404

352405
def __delitem__(self, name: str) -> None:
406+
"""Remove a MarkerGroup by name and trigger re-render.
407+
408+
Parameters
409+
----------
410+
name : str
411+
Name of the group to remove.
412+
413+
Raises
414+
------
415+
KeyError
416+
If the name is not found.
417+
"""
353418
del self._groups[name]
354419
self._push_fn()
355420

356421
def __contains__(self, name: object) -> bool:
422+
"""Check if a named group exists.
423+
424+
Parameters
425+
----------
426+
name : object
427+
Name to check.
428+
429+
Returns
430+
-------
431+
bool
432+
True if the group exists.
433+
"""
357434
return name in self._groups
358435

359436
def __iter__(self):
437+
"""Iterate over group names."""
360438
return iter(self._groups)
361439

362440
def __len__(self) -> int:
441+
"""Return the number of groups."""
363442
return len(self._groups)
364443

365444
def __repr__(self) -> str: # pragma: no cover
366445
return f"MarkerTypeDict(type={self._type!r}, groups={list(self._groups)})"
367446

368447
def keys(self):
448+
"""Return group names."""
369449
return self._groups.keys()
370450

371451
def values(self):
452+
"""Return MarkerGroup objects."""
372453
return self._groups.values()
373454

374455
def items(self):
456+
"""Return (name, MarkerGroup) pairs."""
375457
return self._groups.items()
376458

377459
def pop(self, name: str, *args):
460+
"""Remove and return a MarkerGroup by name.
461+
462+
Parameters
463+
----------
464+
name : str
465+
Name of the group to remove.
466+
*args : optional
467+
Default value if name is not found.
468+
469+
Returns
470+
-------
471+
MarkerGroup
472+
The removed group, or default value if provided.
473+
474+
Raises
475+
------
476+
KeyError
477+
If name is not found and no default is provided.
478+
"""
378479
result = self._groups.pop(name, *args)
379480
self._push_fn()
380481
return result
@@ -437,6 +538,23 @@ def __init__(self, push_fn, allowed: frozenset | None = None):
437538

438539
# ------------------------------------------------------------------
439540
def __getitem__(self, marker_type: str) -> MarkerTypeDict:
541+
"""Return the MarkerTypeDict for a type, auto-creating if needed.
542+
543+
Parameters
544+
----------
545+
marker_type : str
546+
Type name (e.g., 'circles', 'lines', 'arrows').
547+
548+
Returns
549+
-------
550+
MarkerTypeDict
551+
The dict of named groups for this type.
552+
553+
Raises
554+
------
555+
ValueError
556+
If the type is not in the allowed set (if one was provided).
557+
"""
440558
if self._allowed is not None and marker_type not in self._allowed:
441559
raise ValueError(
442560
f"Marker type '{marker_type}' is not allowed on this panel. "
@@ -447,9 +565,11 @@ def __getitem__(self, marker_type: str) -> MarkerTypeDict:
447565
return self._types[marker_type]
448566

449567
def __contains__(self, marker_type: object) -> bool:
568+
"""Check if a marker type has any registered groups."""
450569
return marker_type in self._types
451570

452571
def __iter__(self):
572+
"""Iterate over marker types that have registered groups."""
453573
return iter(self._types)
454574

455575
def __repr__(self) -> str: # pragma: no cover
@@ -482,16 +602,23 @@ def add(self, marker_type: str, name: str | None = None, **kwargs) -> MarkerGrou
482602
483603
Parameters
484604
----------
485-
marker_type :
486-
Type string, e.g. ``'circles'``.
487-
name :
488-
Group name. Auto-generated (``'circles_1'`` etc.) if ``None``.
489-
**kwargs :
490-
Matplotlib-style kwargs for the group.
605+
marker_type : str
606+
Type string, e.g. ``'circles'``, ``'lines'``, ``'arrows'``.
607+
name : str, optional
608+
Group name. Auto-generated (``'circles_1'`` etc.) if ``None``.
609+
**kwargs : dict
610+
Matplotlib-style kwargs for the group (offsets, radius, colors, etc.).
491611
492612
Returns
493613
-------
494614
MarkerGroup
615+
The created marker group. Call ``.set()`` to update, or access
616+
properties as attributes.
617+
618+
Examples
619+
--------
620+
>>> group = registry.add("circles", name="my_circles", offsets=[[10, 20]], radius=5)
621+
>>> group.set(radius=8)
495622
"""
496623
if name is None:
497624
name = self._auto_name(marker_type)
@@ -501,16 +628,35 @@ def add(self, marker_type: str, name: str | None = None, **kwargs) -> MarkerGrou
501628
return g
502629

503630
def remove(self, marker_type: str, name: str) -> None:
504-
"""Remove a named group (triggers a push)."""
631+
"""Remove a named marker group and trigger re-render.
632+
633+
Parameters
634+
----------
635+
marker_type : str
636+
Type of the group.
637+
name : str
638+
Name of the group to remove.
639+
640+
Raises
641+
------
642+
KeyError
643+
If the type or name is not found.
644+
"""
505645
del self[marker_type][name] # MarkerTypeDict.__delitem__ pushes
506646

507647
def clear(self) -> None:
508-
"""Remove all markers of all types."""
648+
"""Remove all markers of all types and trigger re-render."""
509649
self._types.clear()
510650
self._push_fn()
511651

512652
def to_wire_list(self) -> list:
513-
"""Flatten the full registry to a list of wire-format dicts."""
653+
"""Flatten the full registry to a list of wire-format dicts.
654+
655+
Returns
656+
-------
657+
list of dict
658+
Wire-format dicts ready for JSON serialization and JS rendering.
659+
"""
514660
out = []
515661
for td in self._types.values():
516662
out.extend(td.to_wire_list())

0 commit comments

Comments
 (0)