Skip to content

Commit 6cb8f1a

Browse files
committed
Improve documentation
1 parent 0c0978c commit 6cb8f1a

File tree

4 files changed

+95
-24
lines changed

4 files changed

+95
-24
lines changed

docs/source/api.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
API Reference
2+
=============
3+
4+
Note that because we are exposing the Web Canvas API, you can find more tutorials and documentation following this link: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API
5+
6+
There are some API differences though:
7+
8+
- The Canvas widget is directly exposing the `CanvasRenderingContext2D <https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D>`_ API
9+
- All the API is written in *snake_case* instead of *camelCase*, so for example ``canvas.fillStyle = 'red'`` in JavaScript becomes ``canvas.fill_style = 'red'`` in Python
10+
- The Canvas widget exposes a ``clear`` method, ``canvas.clear()`` is a shortcut for ``canvas.clear_rect(0, 0, canvas.size[0], canvas.size[1])``
11+
- We provide a `hold_canvas` context manager if you want to perform lots of commands at once
12+
- The Web canvas putImageData method does not support transparency and the current transformation state, our ``Canvas.put_image_data`` does support them!
13+
14+
15+
.. automodule:: ipycanvas.canvas
16+
:members:

docs/source/conf.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33

44
extensions = [
55
'sphinx.ext.autodoc',
6+
'sphinx.ext.napoleon',
67
# 'sphinx.ext.intersphinx',
78
# 'sphinx.ext.autosummary',
89
# 'sphinx.ext.viewcode',
9-
# 'sphinx.ext.napoleon',
1010
# 'jupyter_sphinx.embed_widgets',
1111
]
1212

@@ -28,3 +28,5 @@
2828
html_theme = "sphinx_rtd_theme"
2929
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
3030
htmlhelp_basename = 'ipycanvasdoc'
31+
32+
autodoc_member_order = 'bysource'

docs/source/index.rst

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,22 @@
11
ipycanvas: Jupyter Interactive Widgets library exposing the browser's Canvas API
22
================================================================================
33

4+
Try it online:
5+
--------------
6+
7+
You can try ipycanvas, without the need of installing anything on your computer, using `mybinder <https://mybinder.org/>`_ by clicking on this badge:
8+
9+
.. image:: https://img.shields.io/badge/start-drawing!-F5A252.svg?logo=data:image/png;base64,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
10+
:target: https://mybinder.org/v2/gh/martinRenou/ipycanvas/stable?filepath=examples
11+
412
.. toctree::
513
:caption: Installation
614
:maxdepth: 2
715

816
installation
17+
18+
19+
.. toctree::
20+
:caption: API Reference
21+
22+
api

ipycanvas/canvas.py

Lines changed: 62 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,17 @@
1616

1717

1818
def to_camel_case(snake_str):
19-
"""Turn a snake_case string into a camelCase one."""
2019
components = snake_str.split('_')
2120
return components[0] + ''.join(x.title() for x in components[1:])
2221

2322

2423
class Canvas(DOMWidget):
24+
"""Create a Canvas widget.
25+
26+
Args:
27+
size (tuple): The size (in pixels) of the canvas
28+
"""
29+
2530
_model_name = Unicode('CanvasModel').tag(sync=True)
2631
_model_module = Unicode(module_name).tag(sync=True)
2732
_model_module_version = Unicode(module_version).tag(sync=True)
@@ -31,15 +36,33 @@ class Canvas(DOMWidget):
3136

3237
size = Tuple((700, 500), help='Size of the Canvas, this is not equal to the size of the view').tag(sync=True)
3338

39+
#: (valid HTML color) The color for filling rectangles and paths. Default to ``'black'``.
3440
fill_style = Color('black')
41+
42+
#: (valid HTML color) The color for rectangles and paths stroke. Default to ``'black'``.
3543
stroke_style = Color('black')
44+
45+
#: (float) Transparency level. Default to ``1.0``.
3646
global_alpha = Float(1.0)
3747

48+
#: (str) Font for the text rendering. Default to ``'12px serif'``.
3849
font = Unicode('12px serif')
39-
text_align = Unicode('start')
40-
text_baseline = Unicode('alphabetic')
41-
direction = Unicode('inherit')
4250

51+
#: (str) Text alignment, possible values are ``'start'``, ``'end'``, ``'left'``, ``'right'``, and ``'center'``.
52+
#: Default to ``'start'``.
53+
text_align = Enum(['start', 'end', 'left', 'right', 'center'], default_value='start')
54+
55+
#: (str) Text baseline, possible values are ``'top'``, ``'hanging'``, ``'middle'``, ``'alphabetic'``, ``'ideographic'``
56+
#: and ``'bottom'``.
57+
#: Default to ``'alphabetic'``.
58+
text_baseline = Enum(['top', 'hanging', 'middle', 'alphabetic', 'ideographic', 'bottom'], default_value='alphabetic')
59+
60+
#: (str) Text direction, possible values are ``'ltr'``, ``'rtl'``, and ``'inherit'``.
61+
#: Default to ``'inherit'``.
62+
direction = Enum(['ltr', 'rtl', 'inherit'], default_value='inherit')
63+
64+
#: (str) Global composite operation, possible values are listed below:
65+
#: https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial/Compositing#globalCompositeOperation
4366
global_composite_operation = Enum(
4467
['source-over', 'source-in', 'source-out', 'source-atop',
4568
'destination-over', 'destination-in', 'destination-out',
@@ -62,21 +85,17 @@ def __init__(self, *args, **kwargs):
6285

6386
# Rectangles methods
6487
def fill_rect(self, x, y, width, height):
65-
"""Draw a filled rectangle."""
88+
"""Draw a filled rectangle of size ``(width, height)`` at the ``(x, y)`` position."""
6689
self._send_canvas_command('fillRect', (x, y, width, height))
6790

6891
def stroke_rect(self, x, y, width, height):
69-
"""Draw a rectangular outline."""
92+
"""Draw a rectangular outline of size ``(width, height)`` at the ``(x, y)`` position."""
7093
self._send_canvas_command('strokeRect', (x, y, width, height))
7194

7295
def clear_rect(self, x, y, width, height):
73-
"""Clear the specified rectangular area, making it fully transparent."""
96+
"""Clear the specified rectangular area of size ``(width, height)`` at the ``(x, y)`` position, making it fully transparent."""
7497
self._send_canvas_command('clearRect', (x, y, width, height))
7598

76-
def rect(self, x, y, width, height):
77-
"""Draw a rectangle whose top-left corner is specified by (x, y) with the specified width and height."""
78-
self._send_canvas_command('rect', (x, y, width, height))
79-
8099
# Paths methods
81100
def begin_path(self):
82101
"""Call this method when you want to create a new path."""
@@ -91,35 +110,42 @@ def close_path(self):
91110
self._send_canvas_command('closePath')
92111

93112
def stroke(self):
94-
"""Stroke (outlines) the current path with the current stroke style."""
113+
"""Stroke (outlines) the current path with the current ``stroke_style``."""
95114
self._send_canvas_command('stroke')
96115

97116
def fill(self):
98-
"""Fill the current or given path with the current fillStyle."""
117+
"""Fill the current or given path with the current ``fill_style``."""
99118
self._send_canvas_command('fill')
100119

101120
def move_to(self, x, y):
102-
"""Move the "pen" to the given (x, y) coordinates."""
121+
"""Move the "pen" to the given ``(x, y)`` coordinates."""
103122
self._send_canvas_command('moveTo', (x, y))
104123

105124
def line_to(self, x, y):
106-
"""Add a straight line to the current path by connecting the path's last point to the specified (x, y) coordinates.
125+
"""Add a straight line to the current path by connecting the path's last point to the specified ``(x, y)`` coordinates.
107126
108127
Like other methods that modify the current path, this method does not directly render anything. To
109128
draw the path onto the canvas, you can use the fill() or stroke() methods.
110129
"""
111130
self._send_canvas_command('lineTo', (x, y))
112131

132+
def rect(self, x, y, width, height):
133+
"""Draw a rectangle of size ``(width, height)`` at the ``(x, y)`` position in the current path."""
134+
self._send_canvas_command('rect', (x, y, width, height))
135+
113136
def arc(self, x, y, radius, start_angle, end_angle, anticlockwise=False):
114-
"""Create a circular arc centered at (x, y) with a radius of radius.
137+
"""Create a circular arc centered at ``(x, y)`` with a radius of ``radius``.
115138
116-
The path starts at startAngle and ends at endAngle, and travels in the direction given by
117-
anticlockwise (defaulting to clockwise).
139+
The path starts at ``start_angle`` and ends at ``end_angle``, and travels in the direction given by
140+
``anticlockwise`` (defaulting to clockwise: ``False``).
118141
"""
119142
self._send_canvas_command('arc', (x, y, radius, start_angle, end_angle, anticlockwise))
120143

121144
def arc_to(self, x1, y1, x2, y2, radius):
122-
"""Add a circular arc to the current path, using the given control points and radius."""
145+
"""Add a circular arc to the current path.
146+
147+
Using the given control points ``(x1, y1)`` and ``(x2, y2)`` and the ``radius``.
148+
"""
123149
self._send_canvas_command('arcTo', (x1, y1, x2, y2, radius))
124150

125151
def quadratic_curve_to(self, cp1x, cp1y, x, y):
@@ -142,11 +168,11 @@ def bezier_curve_to(self, cp1x, cp1y, cp2x, cp2y, x, y):
142168

143169
# Text methods
144170
def fill_text(self, text, x, y, max_width=None):
145-
"""Fill a given text at the given (x,y) position. Optionally with a maximum width to draw."""
171+
"""Fill a given text at the given ``(x, y)`` position. Optionally with a maximum width to draw."""
146172
self._send_canvas_command('fillText', (text, x, y, max_width))
147173

148174
def stroke_text(self, text, x, y, max_width=None):
149-
"""Stroke a given text at the given (x,y) position. Optionally with a maximum width to draw."""
175+
"""Stroke a given text at the given ``(x, y)`` position. Optionally with a maximum width to draw."""
150176
self._send_canvas_command('strokeText', (text, x, y, max_width))
151177

152178
# Image methods
@@ -264,6 +290,13 @@ def _send_command(self, command, buffers=[]):
264290

265291

266292
class MultiCanvas(DOMWidget):
293+
"""Create a MultiCanvas widget with n_canvases Canvas widgets.
294+
295+
Args:
296+
n_canvases (int): The number of canvases to create
297+
size (tuple): The size (in pixels) of the canvases
298+
"""
299+
267300
_model_name = Unicode('MultiCanvasModel').tag(sync=True)
268301
_model_module = Unicode(module_name).tag(sync=True)
269302
_model_module_version = Unicode(module_version).tag(sync=True)
@@ -276,7 +309,7 @@ class MultiCanvas(DOMWidget):
276309
_canvases = List(Instance(Canvas)).tag(sync=True, **widget_serialization)
277310

278311
def __init__(self, n_canvases=3, *args, **kwargs):
279-
"""Create a MultiCanvas widget with n_canvases Canvas widgets."""
312+
"""Constructor."""
280313
size = kwargs.get('size', (700, 500))
281314

282315
super(MultiCanvas, self).__init__(*args, _canvases=[Canvas(size=size) for _ in range(n_canvases)], **kwargs)
@@ -295,7 +328,13 @@ def _on_size_change(self, change):
295328

296329
@contextmanager
297330
def hold_canvas(canvas):
298-
"""Hold any drawing on the canvas, and perform only one draw command at the end."""
331+
"""Hold any drawing on the canvas, and perform all commands in a single shot at the end.
332+
333+
This is way more efficient than sending commands one by one.
334+
335+
Args:
336+
canvas (ipycanvas.canvas.Canvas): The canvas widget on which to hold the commands
337+
"""
299338
canvas.caching = True
300339
yield
301340
canvas.flush()

0 commit comments

Comments
 (0)