Unterschiede
Hier werden die Unterschiede zwischen zwei Versionen der Seite angezeigt.
| Beide Seiten, vorherige Überarbeitung Vorherige Überarbeitung Nächste Überarbeitung | Vorherige Überarbeitung | ||
| user:hof:pyeditor [2024-02-28 18:37] – [First try: bottom.ch] hof | user:hof:pyeditor [2026-04-24 14:29] (aktuell) – hof | ||
|---|---|---|---|
| Zeile 1: | Zeile 1: | ||
| - | # First try: bottom.ch | + | # Bottom Python Editor |
| - | With iframe on bottom.ch/ksr/py/embed.html, which is based on pyodide and codemirror. | + | |
| + | Bottom Editor offers a standalone editor version at [[https://bottom.ch/editor/]] and a web component embeddable as normal HTML elements after loading the module once. See the [[https://bottom.ch/editor/stable/doc/|public documentation]] for all options. | ||
| + | |||
| + | Also see [[~kara]] for a Kara shim on top of bottom-editor. | ||
| + | ## Basic Usage | ||
| + | |||
| + | ### Direct HTML | ||
| + | Requires HTML-authoring (which may be a security risk with broad authorship). | ||
| + | |||
| + | Within Dokuwiki, the HTML needs to be embedded into `< | ||
| <code html> | <code html> | ||
| - | < | + | <!-- Include the script once (or from a DokuWiki plugin, or from userscripts). --> |
| + | < | ||
| - | < | + | <!-- Include a custom component in HTML. Use height, min-height, max-height to control the size. --> |
| + | <!-- Leading empty lines will be dropped. --> | ||
| + | < | ||
| + | print(42) | ||
| + | </bottom-editor></ | ||
| + | </code> | ||
| + | ### With Plugin | ||
| + | To avoid needing raw trusted HTML access, install the [[https:// | ||
| + | |||
| + | <code html> | ||
| + | < | ||
| + | print(42) | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | |||
| + | ## Shared Sessions | ||
| + | By default, all editors on a page share a single python runtime. Functions defined in one execution are available in all editors afterwards, similar to jupyter notebooks. If you want an editor to have a separate python runtime, use the `session` attribute: | ||
| + | <code html> | ||
| + | < | ||
| + | def greet(name): | ||
| + | print(f' | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | greet(' | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | greet(' | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | def greet(name): | ||
| + | print(f' | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | greet(' | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | greet(' | ||
| + | </ | ||
| + | |||
| + | |||
| + | ## Turtle | ||
| + | Standard Python turtle with a small canvas. Use `layout=" | ||
| + | |||
| + | <code html> | ||
| + | < | ||
| + | import turtle | ||
| + | |||
| + | t = turtle.Turtle() | ||
| + | t.speed(9) | ||
| + | colors = [' | ||
| + | for i in range(90): | ||
| + | t.pencolor(colors[i % len(colors)]) | ||
| + | t.forward(i * 0.5) | ||
| + | t.left(59) | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | import turtle | ||
| + | |||
| + | t = turtle.Turtle() | ||
| + | t.speed(9) | ||
| + | colors = [' | ||
| + | for i in range(90): | ||
| + | t.pencolor(colors[i % len(colors)]) | ||
| + | t.forward(i * 0.5) | ||
| + | t.left(59) | ||
| + | </ | ||
| + | |||
| + | ## Matplotlib | ||
| + | `plt.show()` renders to the canvas. | ||
| + | |||
| + | <code html> | ||
| + | < | ||
| + | # Load packages from within python using micropip | ||
| + | import micropip | ||
| + | await micropip.install(' | ||
| + | |||
| + | import matplotlib.pyplot as plt | ||
| + | import math | ||
| + | |||
| + | x = [i * 0.1 for i in range(63)] | ||
| + | plt.figure(figsize=(5, | ||
| + | plt.plot(x, [math.sin(v) for v in x], label=' | ||
| + | plt.plot(x, [math.cos(v) for v in x], label=' | ||
| + | plt.legend() | ||
| + | plt.title(' | ||
| + | plt.tight_layout() | ||
| + | plt.show() | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | # Load packages from within python using micropip | ||
| + | import micropip | ||
| + | await micropip.install(' | ||
| + | |||
| + | import matplotlib.pyplot as plt | ||
| + | import math | ||
| + | |||
| + | x = [i * 0.1 for i in range(63)] | ||
| + | plt.figure(figsize=(5, | ||
| + | plt.plot(x, [math.sin(v) for v in x], label=' | ||
| + | plt.plot(x, [math.cos(v) for v in x], label=' | ||
| + | plt.legend() | ||
| + | plt.title(' | ||
| + | plt.tight_layout() | ||
| + | plt.show() | ||
| + | </ | ||
| + | |||
| + | ## OpenCV | ||
| + | `cv2.imshow()` renders to the canvas. | ||
| + | |||
| + | <code html> | ||
| + | < | ||
| + | # Load packages from within python using micropip | ||
| + | import micropip | ||
| + | await micropip.install(' | ||
| + | await micropip.install(' | ||
| + | |||
| + | import numpy as np | ||
| + | import cv2 | ||
| + | |||
| + | # Gradient image with a circle | ||
| + | img = np.zeros((300, | ||
| + | for y in range(300): | ||
| + | for x in range(300): | ||
| + | img[y, x] = [x * 255 // 300, y * 255 // 300, 128] | ||
| + | cv2.circle(img, | ||
| + | cv2.putText(img, | ||
| + | cv2.imshow(' | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | # Load packages from within python using micropip | ||
| + | import micropip | ||
| + | await micropip.install(' | ||
| + | await micropip.install(' | ||
| + | |||
| + | import numpy as np | ||
| + | import cv2 | ||
| + | |||
| + | # Gradient image with a circle | ||
| + | img = np.zeros((300, | ||
| + | for y in range(300): | ||
| + | for x in range(300): | ||
| + | img[y, x] = [x * 255 // 300, y * 255 // 300, 128] | ||
| + | cv2.circle(img, | ||
| + | cv2.putText(img, | ||
| + | cv2.imshow(' | ||
| + | </ | ||
| + | |||
| + | ## Installing files | ||
| + | You can install files from an URL (with CORS headers if from a different domain!): | ||
| + | |||
| + | <code html> | ||
| + | < | ||
| + | with open(' | ||
| + | for line in infile: | ||
| + | tokens = line.split(',' | ||
| + | town = tokens[0] | ||
| + | if town == ' | ||
| + | print(f' | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | with open(' | ||
| + | for line in infile: | ||
| + | tokens = line.split(',' | ||
| + | town = tokens[0] | ||
| + | if town == ' | ||
| + | print(f' | ||
| + | </ | ||
| + | |||
| + | ## Exercises | ||
| + | |||
| + | <code html> | ||
| + | < | ||
| + | Write a function < | ||
| + | <tt>1 + 2 + & | ||
| + | <tt>n ≤ 0</ | ||
| + | <script type=" | ||
| + | def sum_to(n): | ||
| + | pass | ||
| + | </ | ||
| + | <script type=" | ||
| + | assert sum_to(5) == 15, " | ||
| + | assert sum_to(1) == 1, " | ||
| + | assert sum_to(0) == 0, " | ||
| + | </ | ||
| + | <script type=" | ||
| + | def sum_to(n): | ||
| + | return sum(range(n+1)) | ||
| + | </ | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | < | ||
| + | Write a function < | ||
| + | <tt>1 + 2 + & | ||
| + | <tt>n ≤ 0</ | ||
| + | <script type=" | ||
| + | def sum_to(n): | ||
| + | pass | ||
| + | </ | ||
| + | <script type=" | ||
| + | assert sum_to(5) == 15, " | ||
| + | assert sum_to(1) == 1, " | ||
| + | assert sum_to(0) == 0, " | ||
| + | </ | ||
| + | <script type=" | ||
| + | def sum_to(n): | ||
| + | return sum(range(n+1)) | ||
| + | </ | ||
| + | </ | ||
| + | |||
| + | ## Persistence | ||
| + | |||
| + | Editors or excercises with an `id` attribute store the current state in the browser' | ||
| + | |||
| + | <code html> | ||
| + | < | ||
| + | print(42) | ||
| + | </ | ||
| + | </ | ||
| - | # Second try: JupyterLite | ||
| - | Also based on pyodide, but with jupyter support all around. | + | < |
| + | print(42) | ||
| + | </ | ||
| - | < | ||
| - | src=" | ||
| - | width=" | ||
| - | height=" | ||