├── .gitignore
├── README.md
├── altair
├── README.md
├── assets
│ ├── css
│ │ └── examples.css
│ ├── favicon.png
│ └── logo.png
├── index.html
├── main.py
└── pyscript.toml
├── antigravity
├── README.md
├── antigravity.py
├── antigravity.svg
├── assets
│ ├── css
│ │ └── examples.css
│ ├── favicon.png
│ └── logo.png
├── index.html
├── main.py
└── pyscript.toml
├── api-proxy-tutorial
├── index.html
├── main.py
└── pyscript.toml
├── api-secrets-tutorial
├── index.html
├── main.py
└── pyscript.toml
├── bokeh
├── README.md
├── assets
│ ├── css
│ │ └── examples.css
│ ├── favicon.png
│ └── logo.png
├── index.html
├── main.py
└── pyscript.toml
├── d3
├── README.md
├── assets
│ ├── css
│ │ └── examples.css
│ ├── d3.v7.min.js
│ ├── favicon.png
│ └── logo.png
├── index.html
├── main.py
└── pyscript.toml
├── folium
├── README.md
├── assets
│ ├── css
│ │ └── examples.css
│ ├── favicon.png
│ └── logo.png
├── index.html
├── main.py
└── pyscript.toml
├── fractals_with_numpy_and_canvas
├── README.md
├── assets
│ ├── css
│ │ └── examples.css
│ ├── favicon.png
│ └── logo.png
├── fractals.py
├── index.html
├── main.py
├── palettes.py
└── pyscript.toml
├── hello_world
├── README.md
├── assets
│ ├── css
│ │ └── examples.css
│ ├── favicon.png
│ └── logo.png
├── index.html
└── pyscript.toml
├── icosahedron
├── README.md
├── assets
│ ├── css
│ │ ├── examples.css
│ │ └── icosahedron.css
│ ├── favicon.png
│ └── logo.png
├── index.html
├── main.py
└── pyscript.toml
├── matplotlib
├── README.md
├── assets
│ ├── css
│ │ └── examples.css
│ ├── favicon.png
│ └── logo.png
├── index.html
├── main.py
└── pyscript.toml
├── pandas
├── README.md
├── assets
│ ├── css
│ │ └── examples.css
│ ├── favicon.png
│ └── logo.png
├── index.html
├── main.py
└── pyscript.toml
├── panel
├── README.md
├── assets
│ ├── css
│ │ └── examples.css
│ ├── favicon.png
│ └── logo.png
├── index.html
├── main.py
└── pyscript.toml
├── panel_deckgl
├── README.md
├── assets
│ ├── css
│ │ └── examples.css
│ ├── favicon.png
│ └── logo.png
├── index.html
├── main.py
└── pyscript.toml
├── panel_kmeans
├── README.md
├── assets
│ ├── css
│ │ └── examples.css
│ ├── favicon.png
│ └── logo.png
├── index.html
├── main.py
└── pyscript.toml
├── panel_streaming
├── README.md
├── assets
│ ├── css
│ │ └── examples.css
│ ├── favicon.png
│ └── logo.png
├── index.html
├── main.py
└── pyscript.toml
├── panel_with_hvplot
├── README.md
├── assets
│ ├── css
│ │ └── examples.css
│ ├── favicon.png
│ └── logo.png
├── index.html
├── main.py
└── pyscript.toml
├── py-jokes
├── README.md
├── assets
│ ├── css
│ │ └── examples.css
│ ├── favicon.png
│ └── logo.png
├── index.html
├── main.py
└── pyscript.toml
├── simple_clock
├── README.md
├── assets
│ ├── css
│ │ └── examples.css
│ ├── favicon.png
│ └── logo.png
├── index.html
├── main.py
└── pyscript.toml
├── tic-tac-toe
├── README.md
├── assets
│ ├── css
│ │ ├── examples.css
│ │ └── tictactoe.css
│ ├── favicon.png
│ └── logo.png
├── index.html
├── main.py
└── pyscript.toml
└── todo
├── README.md
├── assets
├── css
│ └── examples.css
├── favicon.png
└── logo.png
├── index.html
├── main.py
└── pyscript.toml
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .pyscript.com
3 | .pyscript-dev.com
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Pyscript Examples
2 |
3 | In this repository you will find the code examples for all the [PyScript Examples](https://pyscript.com/@examples) account.
4 |
5 | ## Useful Links:
6 |
7 | - [Pyscript](https://pyscript.net)
8 | - [PyScript.com](https://pyscript.com)
9 | - [PyScript examples](https://pyscript.com/@examples)
10 | - [Pyscript documentation](https://docs.pyscript.net)
11 | - [Pyscript.com issue tracker](https://github.com/anaconda/pyscript-dot-com-issues)
--------------------------------------------------------------------------------
/altair/README.md:
--------------------------------------------------------------------------------
1 | # Altair Example
2 |
3 | This is an example of how to use `Altair` in `PyScript`, in this application we are using the _movies_ dataset from the `vega_datasets` package to create two graphs. The first graph creates a heatmap of the `IMDB` ratings and the `Rotten Tomatoes` ratings of the movies. The second graph creates a bar chart of the movie genres.
4 |
5 | ## Libraries Used
6 |
7 | - [Altair](https://altair-viz.github.io/)
8 | - [Pandas](https://pandas.pydata.org/)
9 | - [Vega Datasets](https://vega.github.io/vega-datasets/)
--------------------------------------------------------------------------------
/altair/assets/css/examples.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | .pyscript {
6 | margin: 0.5rem;
7 | }
8 |
9 | html {
10 | font-family:
11 | ui-sans-serif,
12 | system-ui,
13 | -apple-system,
14 | BlinkMacSystemFont,
15 | "Segoe UI",
16 | Roboto,
17 | "Helvetica Neue",
18 | Arial,
19 | "Noto Sans",
20 | sans-serif,
21 | "Apple Color Emoji",
22 | "Segoe UI Emoji",
23 | "Segoe UI Symbol",
24 | "Noto Color Emoji";
25 | line-height: 1.5;
26 | }
27 |
28 | nav {
29 | position: sticky;
30 | width: 100%;
31 | top: 0;
32 | left: 0;
33 | z-index: 9999;
34 | }
35 |
36 | .logo {
37 | padding-right: 10px;
38 | font-size: 28px;
39 | height: 30px;
40 | max-width: inherit;
41 | }
42 |
43 | .title {
44 | text-decoration: none;
45 | text-decoration-line: none;
46 | text-decoration-style: initial;
47 | text-decoration-color: initial;
48 | font-weight: 400;
49 | font-size: 1.5em;
50 | line-height: 2em;
51 | white-space: nowrap;
52 | }
53 |
54 | .app-header {
55 | display: flex;
56 | align-items: center;
57 | padding: 0.5rem 1rem;
58 | }
59 |
--------------------------------------------------------------------------------
/altair/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/altair/assets/favicon.png
--------------------------------------------------------------------------------
/altair/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/altair/assets/logo.png
--------------------------------------------------------------------------------
/altair/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
26 |
27 | Altair
28 |
29 |
30 |
31 |
32 |
35 |
36 |
44 |
45 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/altair/main.py:
--------------------------------------------------------------------------------
1 | from pyscript import display
2 | import altair as alt
3 | from vega_datasets import data
4 |
5 | source = data.movies.url
6 |
7 | pts = alt.selection_point(encodings=['x'])
8 |
9 | rect = alt.Chart(data.movies.url).mark_rect().encode(
10 | alt.X('IMDB_Rating:Q', bin=True),
11 | alt.Y('Rotten_Tomatoes_Rating:Q', bin=True),
12 | alt.Color('count()',
13 | scale=alt.Scale(scheme='greenblue'),
14 | legend=alt.Legend(title='Total Records')
15 | )
16 | )
17 |
18 | circ = rect.mark_point().encode(
19 | alt.ColorValue('grey'),
20 | alt.Size('count()',
21 | legend=alt.Legend(title='Records in Selection')
22 | )
23 | ).transform_filter(
24 | pts
25 | )
26 |
27 | bar = alt.Chart(source).mark_bar().encode(
28 | x='Major_Genre:N',
29 | y='count()',
30 | color=alt.condition(pts, alt.ColorValue("steelblue"), alt.ColorValue("grey"))
31 | ).properties(
32 | width=550,
33 | height=200
34 | ).add_params(pts)
35 |
36 | display(alt.vconcat(rect + circ, bar).resolve_legend(color="independent", size="independent"), target="altair")
37 |
--------------------------------------------------------------------------------
/altair/pyscript.toml:
--------------------------------------------------------------------------------
1 | name = "Altair"
2 | description = "An application that uses vega datasets and Vega to plot heatmap and bar graphs."
3 | packages = ["altair", "pandas", "vega_datasets"]
4 |
--------------------------------------------------------------------------------
/antigravity/README.md:
--------------------------------------------------------------------------------
1 | # Antigravity Example
2 |
3 | This application is based on the famous XKCD comic [Python](https://xkcd.com/353/). It creates an `Antigravity` class to load a svg and then use PyScript to display and animate it.
4 |
5 | It also shows you how you can import a file and use it in your Pyscript application - have a look at `pyscript.toml` to see how it's done.
6 |
7 | ## References
8 |
9 | - [Pyodide - open_url](https://pyodide.org/en/stable/usage/api/python-api/http.html#pyodide.http.open_url)
10 | - [Pyodide - set_interval](https://pyodide.org/en/stable/usage/api/python-api/ffi.html#pyodide.ffi.wrappers.set_interval)
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/antigravity/antigravity.py:
--------------------------------------------------------------------------------
1 | import random
2 | from pyweb import pydom
3 | from js import DOMParser
4 | from pyodide.http import open_url
5 | from pyodide.ffi.wrappers import set_interval
6 |
7 |
8 | class Antigravity:
9 | url = "./antigravity.svg"
10 |
11 | def __init__(self, target=None, interval=10, append=True, fly=False):
12 | if isinstance(target, str):
13 | # get element with target as id
14 | self.target = pydom[f"#{target}"][0]
15 | else:
16 | self.target = pydom["body"][0]
17 |
18 | doc = DOMParser.new().parseFromString(
19 | open_url(self.url).read(), "image/svg+xml"
20 | )
21 | self.node = doc.documentElement
22 |
23 | if append:
24 | self.target.append(self.node)
25 | else:
26 | self.target._js.replaceChildren(self.node)
27 |
28 | self.xoffset, self.yoffset = 0, 0
29 | self.interval = interval
30 |
31 | if fly:
32 | self.fly()
33 |
34 | def fly(self):
35 | set_interval(self.move, self.interval)
36 |
37 | def move(self):
38 | char = self.node.getElementsByTagName("g")[1]
39 | char.setAttribute("transform", f"translate({self.xoffset}, {-self.yoffset})")
40 | self.xoffset += random.normalvariate(0, 1) / 20
41 | if self.yoffset < 50:
42 | self.yoffset += 0.1
43 | else:
44 | self.yoffset += random.normalvariate(0, 1) / 20
45 |
46 | _auto = Antigravity(append=True)
47 | fly = _auto.fly
48 |
--------------------------------------------------------------------------------
/antigravity/assets/css/examples.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | .pyscript {
6 | margin: 0.5rem;
7 | }
8 |
9 | html {
10 | font-family:
11 | ui-sans-serif,
12 | system-ui,
13 | -apple-system,
14 | BlinkMacSystemFont,
15 | "Segoe UI",
16 | Roboto,
17 | "Helvetica Neue",
18 | Arial,
19 | "Noto Sans",
20 | sans-serif,
21 | "Apple Color Emoji",
22 | "Segoe UI Emoji",
23 | "Segoe UI Symbol",
24 | "Noto Color Emoji";
25 | line-height: 1.5;
26 | }
27 |
28 | nav {
29 | position: sticky;
30 | width: 100%;
31 | top: 0;
32 | left: 0;
33 | z-index: 9999;
34 | }
35 |
36 | .logo {
37 | padding-right: 10px;
38 | font-size: 28px;
39 | height: 30px;
40 | max-width: inherit;
41 | }
42 |
43 | .title {
44 | text-decoration: none;
45 | text-decoration-line: none;
46 | text-decoration-style: initial;
47 | text-decoration-color: initial;
48 | font-weight: 400;
49 | font-size: 1.5em;
50 | line-height: 2em;
51 | white-space: nowrap;
52 | }
53 |
54 | .app-header {
55 | display: flex;
56 | align-items: center;
57 | padding: 0.5rem 1rem;
58 | }
59 |
--------------------------------------------------------------------------------
/antigravity/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/antigravity/assets/favicon.png
--------------------------------------------------------------------------------
/antigravity/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/antigravity/assets/logo.png
--------------------------------------------------------------------------------
/antigravity/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
26 |
27 | Antigravity
28 |
29 |
30 |
31 |
32 |
35 |
36 |
44 |
45 |
46 | Based on xkcd: antigravity https://xkcd.com/353/.
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/antigravity/main.py:
--------------------------------------------------------------------------------
1 | import antigravity
2 |
3 |
4 | antigravity.fly()
5 |
--------------------------------------------------------------------------------
/antigravity/pyscript.toml:
--------------------------------------------------------------------------------
1 | name = "Antigravity"
2 | description = "A simple application to display an image and animate it based on the famous XKCD comic."
3 |
4 | [files]
5 | "./antigravity.py" = ""
6 |
--------------------------------------------------------------------------------
/api-proxy-tutorial/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | API Proxy Introduction
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/api-proxy-tutorial/main.py:
--------------------------------------------------------------------------------
1 | from pyscript import fetch
2 |
3 | response = await fetch(
4 | "https://examples.pyscriptapps.com/api-proxy-tutorial/api/proxies/status-check",
5 | method="GET"
6 | ).json()
7 |
8 | print(response)
9 |
--------------------------------------------------------------------------------
/api-proxy-tutorial/pyscript.toml:
--------------------------------------------------------------------------------
1 | name = "API Proxy tutorial"
--------------------------------------------------------------------------------
/api-secrets-tutorial/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | API Secrets Tutorial
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/api-secrets-tutorial/main.py:
--------------------------------------------------------------------------------
1 | from pyscript import fetch
2 |
3 | response = await fetch(
4 | "https://examples.pyscriptapps.com/secrets/api/proxies/list-secrets",
5 | method="GET"
6 | ).json()
7 |
8 |
9 | print(response)
10 |
11 |
12 |
--------------------------------------------------------------------------------
/api-secrets-tutorial/pyscript.toml:
--------------------------------------------------------------------------------
1 | name = "API Secrets Tutorial"
--------------------------------------------------------------------------------
/bokeh/README.md:
--------------------------------------------------------------------------------
1 | # Bokeh Example
2 |
3 | This simple application uses both `Pandas` and `Bokeh` to create a simple interactive plot with `PyScript`. We are using circles with orange colour and a size of 15 to represent the data points at hardcoded locations.
4 |
5 | This application is a great start if you want to start with `Bokeh` and `Pandas` in `PyScript`.
6 |
7 | ## Libraries Used
8 |
9 | - [Bokeh](https://bokeh.org/)
10 | - [Pandas](https://pandas.pydata.org/)
--------------------------------------------------------------------------------
/bokeh/assets/css/examples.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | .pyscript {
6 | margin: 0.5rem;
7 | }
8 |
9 | html {
10 | font-family:
11 | ui-sans-serif,
12 | system-ui,
13 | -apple-system,
14 | BlinkMacSystemFont,
15 | "Segoe UI",
16 | Roboto,
17 | "Helvetica Neue",
18 | Arial,
19 | "Noto Sans",
20 | sans-serif,
21 | "Apple Color Emoji",
22 | "Segoe UI Emoji",
23 | "Segoe UI Symbol",
24 | "Noto Color Emoji";
25 | line-height: 1.5;
26 | }
27 |
28 | nav {
29 | position: sticky;
30 | width: 100%;
31 | top: 0;
32 | left: 0;
33 | z-index: 9999;
34 | }
35 |
36 | .logo {
37 | padding-right: 10px;
38 | font-size: 28px;
39 | height: 30px;
40 | max-width: inherit;
41 | }
42 |
43 | .title {
44 | text-decoration: none;
45 | text-decoration-line: none;
46 | text-decoration-style: initial;
47 | text-decoration-color: initial;
48 | font-weight: 400;
49 | font-size: 1.5em;
50 | line-height: 2em;
51 | white-space: nowrap;
52 | }
53 |
54 | .app-header {
55 | display: flex;
56 | align-items: center;
57 | padding: 0.5rem 1rem;
58 | }
59 |
--------------------------------------------------------------------------------
/bokeh/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/bokeh/assets/favicon.png
--------------------------------------------------------------------------------
/bokeh/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/bokeh/assets/logo.png
--------------------------------------------------------------------------------
/bokeh/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
26 |
27 | Bokeh Example
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
39 |
40 |
41 |
42 |
45 |
46 |
54 |
55 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/bokeh/main.py:
--------------------------------------------------------------------------------
1 | import json
2 |
3 | from js import Bokeh, JSON
4 |
5 | from bokeh.embed import json_item
6 | from bokeh.plotting import figure
7 |
8 | # create a new plot with default tools, using figure
9 | p = figure(width=400, height=400)
10 |
11 | # add a circle renderer with x and y coordinates, size, color, and alpha
12 | p.circle([1, 2, 3, 4, 5], [6, 7, 2, 4, 5], size=15, line_color="navy", fill_color="orange", fill_alpha=0.5)
13 | p_json = json.dumps(json_item(p, "myplot"))
14 |
15 | Bokeh.embed.embed_item(JSON.parse(p_json))
16 |
--------------------------------------------------------------------------------
/bokeh/pyscript.toml:
--------------------------------------------------------------------------------
1 | name = "Bokeh"
2 | description = "A simple application that uses Pandas and Bokeh to plot a graph."
3 | packages = ["pandas", "bokeh==3.2.2", "xyzservices"]
4 |
--------------------------------------------------------------------------------
/d3/README.md:
--------------------------------------------------------------------------------
1 | # D3 Example
2 |
3 | This application uses the D3 library to create a pie graph using some hardcoded data. We use this data to create a graph both in javascript and in PyScript to show you how you can use one or the other to create a graph.
4 |
5 |
6 | ## Libraries Used
7 |
8 | - [D3](https://d3js.org/)
9 |
10 | ## References
11 |
12 | - [Pyodide - create_proxy](https://pyodide.org/en/stable/usage/api/python-api/ffi.html#pyodide.ffi.create_proxy)
13 | - [Pyodide - to_jw](https://pyodide.org/en/stable/usage/api/python-api/ffi.html#pyodide.ffi.to_js)
14 |
--------------------------------------------------------------------------------
/d3/assets/css/examples.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | .pyscript {
6 | margin: 0.5rem;
7 | }
8 |
9 | html {
10 | font-family:
11 | ui-sans-serif,
12 | system-ui,
13 | -apple-system,
14 | BlinkMacSystemFont,
15 | "Segoe UI",
16 | Roboto,
17 | "Helvetica Neue",
18 | Arial,
19 | "Noto Sans",
20 | sans-serif,
21 | "Apple Color Emoji",
22 | "Segoe UI Emoji",
23 | "Segoe UI Symbol",
24 | "Noto Color Emoji";
25 | line-height: 1.5;
26 | }
27 |
28 | nav {
29 | position: sticky;
30 | width: 100%;
31 | top: 0;
32 | left: 0;
33 | z-index: 9999;
34 | }
35 |
36 | .logo {
37 | padding-right: 10px;
38 | font-size: 28px;
39 | height: 30px;
40 | max-width: inherit;
41 | }
42 |
43 | .title {
44 | text-decoration: none;
45 | text-decoration-line: none;
46 | text-decoration-style: initial;
47 | text-decoration-color: initial;
48 | font-weight: 400;
49 | font-size: 1.5em;
50 | line-height: 2em;
51 | white-space: nowrap;
52 | }
53 |
54 | .app-header {
55 | display: flex;
56 | align-items: center;
57 | padding: 0.5rem 1rem;
58 | }
59 |
--------------------------------------------------------------------------------
/d3/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/d3/assets/favicon.png
--------------------------------------------------------------------------------
/d3/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/d3/assets/logo.png
--------------------------------------------------------------------------------
/d3/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
26 |
27 | D3 Visualization
28 |
29 |
30 |
31 |
48 |
49 |
50 |
51 |
52 |
55 |
56 |
64 |
65 |
66 |
67 | Based on
68 | Learn D3: Shapes
69 |
70 | tutorial.
71 |
72 |
73 |
74 |
JavaScript version
75 |
78 |
79 |
80 |
PyScript version
81 |
84 |
85 |
86 |
87 |
143 |
144 |
145 |
146 |
147 |
148 |
--------------------------------------------------------------------------------
/d3/main.py:
--------------------------------------------------------------------------------
1 | from js import d3
2 | from pyodide.ffi import create_proxy, to_js
3 |
4 | fruits = [
5 | {"name": "🍊", "count": 21},
6 | {"name": "🍇", "count": 13},
7 | {"name": "🍏", "count": 8},
8 | {"name": "🍌", "count": 5},
9 | {"name": "🍐", "count": 3},
10 | {"name": "🍋", "count": 2},
11 | {"name": "🍎", "count": 1},
12 | {"name": "🍉", "count": 1},
13 | ]
14 |
15 | fn = create_proxy(lambda d, *_: d["count"])
16 | data = d3.pie().value(fn)(to_js(fruits))
17 |
18 | arc = (
19 | d3.arc()
20 | .innerRadius(210)
21 | .outerRadius(310)
22 | .padRadius(300)
23 | .padAngle(2 / 300)
24 | .cornerRadius(8)
25 | )
26 |
27 | py = d3.select("#py")
28 | py.select(".loading").remove()
29 |
30 | svg = (
31 | py.append("svg")
32 | .attr("viewBox", "-320 -320 640 640")
33 | .attr("width", "400")
34 | .attr("height", "400")
35 | )
36 |
37 | for d in data:
38 | d_py = d.to_py()
39 |
40 | (svg.append("path").style("fill", "steelblue").attr("d", arc(d)))
41 |
42 | text = (
43 | svg.append("text")
44 | .style("fill", "white")
45 | .attr("transform", f"translate({arc.centroid(d).join(',')})")
46 | .attr("text-anchor", "middle")
47 | )
48 |
49 | (
50 | text.append("tspan")
51 | .style("font-size", "24")
52 | .attr("x", "0")
53 | .text(d_py["data"]["name"])
54 | )
55 |
56 | (
57 | text.append("tspan")
58 | .style("font-size", "18")
59 | .attr("x", "0")
60 | .attr("dy", "1.3em")
61 | .text(d_py["value"])
62 | )
63 |
--------------------------------------------------------------------------------
/d3/pyscript.toml:
--------------------------------------------------------------------------------
1 | name = "D3 Visualization"
2 | description = "A simple application that uses D3 to plot a graph both in Javascript and Pyscript."
3 |
--------------------------------------------------------------------------------
/folium/README.md:
--------------------------------------------------------------------------------
1 | # Folium Example
2 |
3 | This application uses the `Folium` library to create a map of the United States and then uses Pandas to load a dataset of the unemployment rate in the US on October 2021. We then use this data to create a choropleth map of the unemployment rate in the US.
4 |
5 | ## Libraries Used
6 |
7 | - [Folium](https://python-visualization.github.io/folium/)
8 | - [Pandas](https://pandas.pydata.org/)
9 |
10 | ## References
11 |
12 | - [Folium - Choropleth](https://python-visualization.github.io/folium/latest/reference.html#folium.features.Choropleth)
13 | - [Folium - Map](https://python-visualization.github.io/folium/modules.html#folium.folium.Map)
14 | - [Pandas - Read CSV](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_csv.html)
15 | - [Pyodide - open_url](https://pyodide.org/en/stable/usage/api/python-api/http.html#pyodide.http.open_url)
16 |
--------------------------------------------------------------------------------
/folium/assets/css/examples.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | .pyscript {
6 | margin: 0.5rem;
7 | }
8 |
9 | html {
10 | font-family:
11 | ui-sans-serif,
12 | system-ui,
13 | -apple-system,
14 | BlinkMacSystemFont,
15 | "Segoe UI",
16 | Roboto,
17 | "Helvetica Neue",
18 | Arial,
19 | "Noto Sans",
20 | sans-serif,
21 | "Apple Color Emoji",
22 | "Segoe UI Emoji",
23 | "Segoe UI Symbol",
24 | "Noto Color Emoji";
25 | line-height: 1.5;
26 | }
27 |
28 | nav {
29 | position: sticky;
30 | width: 100%;
31 | top: 0;
32 | left: 0;
33 | z-index: 9999;
34 | }
35 |
36 | .logo {
37 | padding-right: 10px;
38 | font-size: 28px;
39 | height: 30px;
40 | max-width: inherit;
41 | }
42 |
43 | .title {
44 | text-decoration: none;
45 | text-decoration-line: none;
46 | text-decoration-style: initial;
47 | text-decoration-color: initial;
48 | font-weight: 400;
49 | font-size: 1.5em;
50 | line-height: 2em;
51 | white-space: nowrap;
52 | }
53 |
54 | .app-header {
55 | display: flex;
56 | align-items: center;
57 | padding: 0.5rem 1rem;
58 | }
59 |
--------------------------------------------------------------------------------
/folium/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/folium/assets/favicon.png
--------------------------------------------------------------------------------
/folium/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/folium/assets/logo.png
--------------------------------------------------------------------------------
/folium/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
26 |
27 | Folium
28 |
29 |
30 |
31 |
32 |
35 |
36 |
44 |
45 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/folium/main.py:
--------------------------------------------------------------------------------
1 | from pyscript import display
2 | import folium
3 | import json
4 | import pandas as pd
5 |
6 | from pyodide.http import open_url
7 |
8 | url = "https://raw.githubusercontent.com/python-visualization/folium/master/examples/data"
9 | state_geo = f"{url}/us-states.json"
10 | state_unemployment = f"{url}/US_Unemployment_Oct2012.csv"
11 | state_data = pd.read_csv(open_url(state_unemployment))
12 | geo_json = json.loads(open_url(state_geo).read())
13 |
14 | m = folium.Map(location=[48, -102], zoom_start=3)
15 |
16 | folium.Choropleth(
17 | geo_data=geo_json,
18 | name="choropleth",
19 | data=state_data,
20 | columns=["State", "Unemployment"],
21 | key_on="feature.id",
22 | fill_color="YlGn",
23 | fill_opacity=0.7,
24 | line_opacity=0.2,
25 | legend_name="Unemployment Rate (%)",
26 | ).add_to(m)
27 |
28 | folium.LayerControl().add_to(m)
29 |
30 | display(m, target="folium")
31 |
--------------------------------------------------------------------------------
/folium/pyscript.toml:
--------------------------------------------------------------------------------
1 | name = "Folium"
2 | description = "A simple application that uses pandas and folium to draw heatmaps on a map."
3 | packages = ["folium", "pandas"]
4 |
--------------------------------------------------------------------------------
/fractals_with_numpy_and_canvas/README.md:
--------------------------------------------------------------------------------
1 | # Fractals with Numpy and Canvas Example
2 |
3 | This example demonstrates how to use Numpy and Canvas to create a web-based visualization of the Mandelbrot and Julia set.
4 |
5 | ## Libraries Used
6 |
7 | - [Numpy](https://numpy.org): The fundamental package for scientific computing with Python
8 | - [Canvas](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API): A web-based, JavaScript drawing API
9 | - [SymPy](https://www.sympy.org): A Python library for symbolic mathematics
--------------------------------------------------------------------------------
/fractals_with_numpy_and_canvas/assets/css/examples.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | .pyscript {
6 | margin: 0.5rem;
7 | }
8 |
9 | html {
10 | font-family:
11 | ui-sans-serif,
12 | system-ui,
13 | -apple-system,
14 | BlinkMacSystemFont,
15 | "Segoe UI",
16 | Roboto,
17 | "Helvetica Neue",
18 | Arial,
19 | "Noto Sans",
20 | sans-serif,
21 | "Apple Color Emoji",
22 | "Segoe UI Emoji",
23 | "Segoe UI Symbol",
24 | "Noto Color Emoji";
25 | line-height: 1.5;
26 | }
27 |
28 | nav {
29 | position: sticky;
30 | width: 100%;
31 | top: 0;
32 | left: 0;
33 | z-index: 9999;
34 | }
35 |
36 | .logo {
37 | padding-right: 10px;
38 | font-size: 28px;
39 | height: 30px;
40 | max-width: inherit;
41 | }
42 |
43 | .title {
44 | text-decoration: none;
45 | text-decoration-line: none;
46 | text-decoration-style: initial;
47 | text-decoration-color: initial;
48 | font-weight: 400;
49 | font-size: 1.5em;
50 | line-height: 2em;
51 | white-space: nowrap;
52 | }
53 |
54 | .app-header {
55 | display: flex;
56 | align-items: center;
57 | padding: 0.5rem 1rem;
58 | }
59 |
--------------------------------------------------------------------------------
/fractals_with_numpy_and_canvas/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/fractals_with_numpy_and_canvas/assets/favicon.png
--------------------------------------------------------------------------------
/fractals_with_numpy_and_canvas/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/fractals_with_numpy_and_canvas/assets/logo.png
--------------------------------------------------------------------------------
/fractals_with_numpy_and_canvas/fractals.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | from numpy.polynomial import Polynomial
3 |
4 |
5 | def mandelbrot(
6 | width: int,
7 | height: int,
8 | *,
9 | x: float = -0.5,
10 | y: float = 0,
11 | zoom: int = 1,
12 | max_iterations: int = 100
13 | ) -> np.array:
14 | """
15 | https://www.learnpythonwithrune.org/numpy-compute-mandelbrot-set-by-vectorization
16 | """
17 | # To make navigation easier we calculate these values
18 | x_width, y_height = 1.5, 1.5 * height / width
19 | x_from, x_to = x - x_width / zoom, x + x_width / zoom
20 | y_from, y_to = y - y_height / zoom, y + y_height / zoom
21 |
22 | # Here the actual algorithm starts
23 | x = np.linspace(x_from, x_to, width).reshape((1, width))
24 | y = np.linspace(y_from, y_to, height).reshape((height, 1))
25 | c = x + 1j * y
26 |
27 | # Initialize z to all zero
28 | z = np.zeros(c.shape, dtype=np.complex128)
29 |
30 | # To keep track in which iteration the point diverged
31 | div_time = np.zeros(z.shape, dtype=int)
32 |
33 | # To keep track on which points did not converge so far
34 | m = np.full(c.shape, True, dtype=bool)
35 | for i in range(max_iterations):
36 | z[m] = z[m] ** 2 + c[m]
37 | diverged = np.greater(
38 | np.abs(z), 2, out=np.full(c.shape, False), where=m
39 | ) # Find diverging
40 | div_time[diverged] = i # set the value of the diverged iteration number
41 | m[np.abs(z) > 2] = False # to remember which have diverged
42 |
43 | return div_time
44 |
45 |
46 | def julia(
47 | width: int,
48 | height: int,
49 | *,
50 | c: complex = -0.4 + 0.6j,
51 | x: float = 0,
52 | y: float = 0,
53 | zoom: int = 1,
54 | max_iterations: int = 100
55 | ) -> np.array:
56 | """
57 | https://www.learnpythonwithrune.org/numpy-calculate-the-julia-set-with-vectorization
58 | """
59 | # To make navigation easier we calculate these values
60 | x_width, y_height = 1.5, 1.5 * height / width
61 | x_from, x_to = x - x_width / zoom, x + x_width / zoom
62 | y_from, y_to = y - y_height / zoom, y + y_height / zoom
63 |
64 | # Here the actual algorithm starts
65 | x = np.linspace(x_from, x_to, width).reshape((1, width))
66 | y = np.linspace(y_from, y_to, height).reshape((height, 1))
67 | z = x + 1j * y
68 |
69 | # Initialize z to all zero
70 | c = np.full(z.shape, c)
71 |
72 | # To keep track in which iteration the point diverged
73 | div_time = np.zeros(z.shape, dtype=int)
74 |
75 | # To keep track on which points did not converge so far
76 | m = np.full(c.shape, True, dtype=bool)
77 | for i in range(max_iterations):
78 | z[m] = z[m] ** 2 + c[m]
79 | m[np.abs(z) > 2] = False
80 | div_time[m] = i
81 |
82 | return div_time
83 |
84 |
85 | def newton(
86 | width: int,
87 | height: int,
88 | *,
89 | p: Polynomial,
90 | a: complex,
91 | xr: tuple[float, float] = (-2.5, 1),
92 | yr: tuple[float, float] = (-1, 1),
93 | max_iterations: int = 100
94 | ) -> tuple[np.array, np.array]:
95 | """ """
96 | # To make navigation easier we calculate these values
97 | x_from, x_to = xr
98 | y_from, y_to = yr
99 |
100 | # Here the actual algorithm starts
101 | x = np.linspace(x_from, x_to, width).reshape((1, width))
102 | y = np.linspace(y_from, y_to, height).reshape((height, 1))
103 | z = x + 1j * y
104 |
105 | # Compute the derivative
106 | dp = p.deriv()
107 |
108 | # Compute roots
109 | roots = p.roots()
110 | epsilon = 1e-5
111 |
112 | # Set the initial conditions
113 | a = np.full(z.shape, a)
114 |
115 | # To keep track in which iteration the point diverged
116 | div_time = np.zeros(z.shape, dtype=int)
117 |
118 | # To keep track on which points did not converge so far
119 | m = np.full(a.shape, True, dtype=bool)
120 |
121 | # To keep track which root each point converged to
122 | r = np.full(a.shape, 0, dtype=int)
123 |
124 | for i in range(max_iterations):
125 | z[m] = z[m] - a[m] * p(z[m]) / dp(z[m])
126 |
127 | for j, root in enumerate(roots):
128 | converged = (np.abs(z.real - root.real) < epsilon) & (
129 | np.abs(z.imag - root.imag) < epsilon
130 | )
131 | m[converged] = False
132 | r[converged] = j + 1
133 |
134 | div_time[m] = i
135 |
136 | return div_time, r
137 |
--------------------------------------------------------------------------------
/fractals_with_numpy_and_canvas/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
26 |
27 | Fractals with NumPy and canvas
28 |
29 |
30 |
31 |
52 |
53 |
54 |
55 |
56 |
59 |
60 |
68 |
69 |
70 |
71 |
72 |
73 |
Mandelbrot set
74 |
78 |
79 |
80 |
87 |
88 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
--------------------------------------------------------------------------------
/fractals_with_numpy_and_canvas/main.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 | import sympy
3 | import asyncio
4 |
5 | from pyweb import pydom
6 | from pyscript import when
7 | from pyodide.ffi import to_js
8 | from js import (
9 | CanvasRenderingContext2D as Context2d,
10 | ImageData,
11 | Uint8ClampedArray,
12 | console
13 | )
14 |
15 | from palettes import Magma256
16 | from fractals import mandelbrot, julia, newton
17 |
18 | def prepare_canvas(width: int, height: int, canvas: pydom.Element) -> Context2d:
19 | ctx = canvas._js.getContext("2d")
20 |
21 | canvas.style["width"] = f"{width}px"
22 | canvas.style["height"] = f"{height}px"
23 |
24 | canvas._js.width = width
25 | canvas._js.height = height
26 |
27 | ctx.clearRect(0, 0, width, height)
28 |
29 | return ctx
30 |
31 | def color_map(array: np.array, palette: np.array) -> np.array:
32 | size, _ = palette.shape
33 | index = (array / array.max() * (size - 1)).round().astype("uint8")
34 |
35 | width, height = array.shape
36 | image = np.full((width, height, 4), 0xff, dtype=np.uint8)
37 | image[:, :, :3] = palette[index]
38 |
39 | return image
40 |
41 | def draw_image(ctx: Context2d, image: np.array) -> None:
42 | data = Uint8ClampedArray.new(to_js(image.tobytes()))
43 | width, height, _ = image.shape
44 | image_data = ImageData.new(data, width, height)
45 | ctx.putImageData(image_data, 0, 0)
46 |
47 | async def draw_mandelbrot(width, height) -> None:
48 | spinner = pydom["#mandelbrot .loading"]
49 | canvas = pydom["#mandelbrot canvas"][0]
50 |
51 | spinner.style["display"] = ""
52 | canvas.style["display"] = "none"
53 |
54 | ctx = prepare_canvas(width, height, canvas)
55 |
56 | console.log("Computing Mandelbrot set ...")
57 | console.time("mandelbrot")
58 | iters = mandelbrot(width, height)
59 | console.timeEnd("mandelbrot")
60 |
61 | image = color_map(iters, Magma256)
62 | draw_image(ctx, image)
63 |
64 | spinner.style["display"] = "none"
65 | canvas.style["display"] = "block"
66 |
67 | async def draw_julia(width, height) -> None:
68 | spinner = pydom["#julia .loading"]
69 | canvas = pydom["#julia canvas"][0]
70 |
71 | spinner.style["display"] = ""
72 | canvas.style["display"] = "none"
73 |
74 | ctx = prepare_canvas(width, height, canvas)
75 |
76 | console.log("Computing Julia set ...")
77 | console.time("julia")
78 | iters = julia(width, height)
79 | console.timeEnd("julia")
80 |
81 | image = color_map(iters, Magma256)
82 | draw_image(ctx, image)
83 |
84 | spinner.style["display"] = "none"
85 | canvas.style["display"] = "block"
86 |
87 | def ranges():
88 | x0_in = pydom["#x0"][0]
89 | x1_in = pydom["#x1"][0]
90 | y0_in = pydom["#y0"][0]
91 | y1_in = pydom["#y1"][0]
92 |
93 | xr = (float(x0_in.value), float(x1_in.value))
94 | yr = (float(y0_in.value), float(y1_in.value))
95 |
96 | return xr, yr
97 |
98 | current_image = None
99 |
100 | async def draw_newton(width, height) -> None:
101 | spinner = pydom["#newton .loading"]
102 | canvas = pydom["#newton canvas"][0]
103 |
104 | spinner.style["display"] = ""
105 | canvas.style["display"] = "none"
106 |
107 | ctx = prepare_canvas(width, height, canvas)
108 |
109 | console.log("Computing Newton set ...")
110 |
111 | poly_in = pydom["#poly"][0]
112 | coef_in = pydom["#coef"][0]
113 | conv_in = pydom["#conv"][0]
114 |
115 | xr, yr = ranges()
116 |
117 | expr = sympy.parse_expr(poly_in.value)
118 | coeffs = [ complex(c) for c in reversed(sympy.Poly(expr, sympy.Symbol("z")).all_coeffs()) ]
119 | poly = np.polynomial.Polynomial(coeffs)
120 |
121 | coef = complex(sympy.parse_expr(coef_in.value))
122 |
123 | console.time("newton")
124 | iters, roots = newton(width, height, p=poly, a=coef, xr=xr, yr=yr)
125 | console.timeEnd("newton")
126 |
127 | if conv_in._js.checked:
128 | n = poly.degree() + 1
129 | k = int(len(Magma256)/n)
130 |
131 | colors = Magma256[::k, :][:n]
132 | colors[0, :] = [255, 0, 0] # red: no convergence
133 |
134 | image = color_map(roots, colors)
135 | else:
136 | image = color_map(iters, Magma256)
137 |
138 | global current_image
139 | current_image = image
140 | draw_image(ctx, image)
141 |
142 | spinner.style["display"] = "none"
143 | canvas.style["display"] = "block"
144 |
145 | newton_fieldset = pydom["#newton fieldset"]
146 |
147 | @when("change", newton_fieldset)
148 | async def fieldset_rerender(event):
149 | await draw_newton(width, height)
150 |
151 | width, height = 600, 600
152 | canvas = pydom["#newton canvas"][0]
153 |
154 | is_selecting = False
155 | init_sx, init_sy = None, None
156 | sx, sy = None, None
157 |
158 | @when("mousemove", canvas)
159 | async def mousemove(event):
160 | global is_selecting
161 | global init_sx
162 | global init_sy
163 | global sx
164 | global sy
165 |
166 | def invert(sx, source_range, target_range):
167 | source_start, source_end = source_range
168 | target_start, target_end = target_range
169 | factor = (target_end - target_start)/(source_end - source_start)
170 | offset = -(factor * source_start) + target_start
171 | return (sx - offset) / factor
172 |
173 | bds = canvas._js.getBoundingClientRect()
174 | event_sx, event_sy = event.clientX - bds.x, event.clientY - bds.y
175 |
176 | ctx = canvas._js.getContext("2d")
177 |
178 | pressed = event.buttons == 1
179 | if is_selecting:
180 | if not pressed:
181 | xr, yr = ranges()
182 |
183 | x0 = invert(init_sx, xr, (0, width))
184 | x1 = invert(sx, xr, (0, width))
185 | y0 = invert(init_sy, yr, (0, height))
186 | y1 = invert(sy, yr, (0, height))
187 |
188 | pydom["#x0"][0].value = x0
189 | pydom["#x1"][0].value = x1
190 | pydom["#y0"][0].value = y0
191 | pydom["#y1"][0].value = y1
192 |
193 | is_selecting = False
194 | init_sx, init_sy = None, None
195 | sx, sy = init_sx, init_sy
196 |
197 | await draw_newton(width, height)
198 | else:
199 | ctx.save()
200 | ctx.clearRect(0, 0, width, height)
201 | draw_image(ctx, current_image)
202 | sx, sy = event_sx, event_sy
203 | ctx.beginPath()
204 | ctx.rect(init_sx, init_sy, sx - init_sx, sy - init_sy)
205 | ctx.fillStyle = "rgba(255, 255, 255, 0.4)"
206 | ctx.strokeStyle = "rgba(255, 255, 255, 1.0)"
207 | ctx.fill()
208 | ctx.stroke()
209 | ctx.restore()
210 | else:
211 | if pressed:
212 | is_selecting = True
213 | init_sx, init_sy = event_sx, event_sy
214 | sx, sy = init_sx, init_sy
215 |
216 | async def main():
217 | _ = await asyncio.gather(draw_mandelbrot(width, height), draw_julia(width, height), draw_newton(width, height))
218 |
219 | asyncio.ensure_future(main())
220 |
--------------------------------------------------------------------------------
/fractals_with_numpy_and_canvas/palettes.py:
--------------------------------------------------------------------------------
1 | import numpy as np
2 |
3 | Magma256 = np.array(
4 | [
5 | [0x00, 0x00, 0x03],
6 | [0x00, 0x00, 0x04],
7 | [0x00, 0x00, 0x06],
8 | [0x01, 0x00, 0x07],
9 | [0x01, 0x01, 0x09],
10 | [0x01, 0x01, 0x0B],
11 | [0x02, 0x02, 0x0D],
12 | [0x02, 0x02, 0x0F],
13 | [0x03, 0x03, 0x11],
14 | [0x04, 0x03, 0x13],
15 | [0x04, 0x04, 0x15],
16 | [0x05, 0x04, 0x17],
17 | [0x06, 0x05, 0x19],
18 | [0x07, 0x05, 0x1B],
19 | [0x08, 0x06, 0x1D],
20 | [0x09, 0x07, 0x1F],
21 | [0x0A, 0x07, 0x22],
22 | [0x0B, 0x08, 0x24],
23 | [0x0C, 0x09, 0x26],
24 | [0x0D, 0x0A, 0x28],
25 | [0x0E, 0x0A, 0x2A],
26 | [0x0F, 0x0B, 0x2C],
27 | [0x10, 0x0C, 0x2F],
28 | [0x11, 0x0C, 0x31],
29 | [0x12, 0x0D, 0x33],
30 | [0x14, 0x0D, 0x35],
31 | [0x15, 0x0E, 0x38],
32 | [0x16, 0x0E, 0x3A],
33 | [0x17, 0x0F, 0x3C],
34 | [0x18, 0x0F, 0x3F],
35 | [0x1A, 0x10, 0x41],
36 | [0x1B, 0x10, 0x44],
37 | [0x1C, 0x10, 0x46],
38 | [0x1E, 0x10, 0x49],
39 | [0x1F, 0x11, 0x4B],
40 | [0x20, 0x11, 0x4D],
41 | [0x22, 0x11, 0x50],
42 | [0x23, 0x11, 0x52],
43 | [0x25, 0x11, 0x55],
44 | [0x26, 0x11, 0x57],
45 | [0x28, 0x11, 0x59],
46 | [0x2A, 0x11, 0x5C],
47 | [0x2B, 0x11, 0x5E],
48 | [0x2D, 0x10, 0x60],
49 | [0x2F, 0x10, 0x62],
50 | [0x30, 0x10, 0x65],
51 | [0x32, 0x10, 0x67],
52 | [0x34, 0x10, 0x68],
53 | [0x35, 0x0F, 0x6A],
54 | [0x37, 0x0F, 0x6C],
55 | [0x39, 0x0F, 0x6E],
56 | [0x3B, 0x0F, 0x6F],
57 | [0x3C, 0x0F, 0x71],
58 | [0x3E, 0x0F, 0x72],
59 | [0x40, 0x0F, 0x73],
60 | [0x42, 0x0F, 0x74],
61 | [0x43, 0x0F, 0x75],
62 | [0x45, 0x0F, 0x76],
63 | [0x47, 0x0F, 0x77],
64 | [0x48, 0x10, 0x78],
65 | [0x4A, 0x10, 0x79],
66 | [0x4B, 0x10, 0x79],
67 | [0x4D, 0x11, 0x7A],
68 | [0x4F, 0x11, 0x7B],
69 | [0x50, 0x12, 0x7B],
70 | [0x52, 0x12, 0x7C],
71 | [0x53, 0x13, 0x7C],
72 | [0x55, 0x13, 0x7D],
73 | [0x57, 0x14, 0x7D],
74 | [0x58, 0x15, 0x7E],
75 | [0x5A, 0x15, 0x7E],
76 | [0x5B, 0x16, 0x7E],
77 | [0x5D, 0x17, 0x7E],
78 | [0x5E, 0x17, 0x7F],
79 | [0x60, 0x18, 0x7F],
80 | [0x61, 0x18, 0x7F],
81 | [0x63, 0x19, 0x7F],
82 | [0x65, 0x1A, 0x80],
83 | [0x66, 0x1A, 0x80],
84 | [0x68, 0x1B, 0x80],
85 | [0x69, 0x1C, 0x80],
86 | [0x6B, 0x1C, 0x80],
87 | [0x6C, 0x1D, 0x80],
88 | [0x6E, 0x1E, 0x81],
89 | [0x6F, 0x1E, 0x81],
90 | [0x71, 0x1F, 0x81],
91 | [0x73, 0x1F, 0x81],
92 | [0x74, 0x20, 0x81],
93 | [0x76, 0x21, 0x81],
94 | [0x77, 0x21, 0x81],
95 | [0x79, 0x22, 0x81],
96 | [0x7A, 0x22, 0x81],
97 | [0x7C, 0x23, 0x81],
98 | [0x7E, 0x24, 0x81],
99 | [0x7F, 0x24, 0x81],
100 | [0x81, 0x25, 0x81],
101 | [0x82, 0x25, 0x81],
102 | [0x84, 0x26, 0x81],
103 | [0x85, 0x26, 0x81],
104 | [0x87, 0x27, 0x81],
105 | [0x89, 0x28, 0x81],
106 | [0x8A, 0x28, 0x81],
107 | [0x8C, 0x29, 0x80],
108 | [0x8D, 0x29, 0x80],
109 | [0x8F, 0x2A, 0x80],
110 | [0x91, 0x2A, 0x80],
111 | [0x92, 0x2B, 0x80],
112 | [0x94, 0x2B, 0x80],
113 | [0x95, 0x2C, 0x80],
114 | [0x97, 0x2C, 0x7F],
115 | [0x99, 0x2D, 0x7F],
116 | [0x9A, 0x2D, 0x7F],
117 | [0x9C, 0x2E, 0x7F],
118 | [0x9E, 0x2E, 0x7E],
119 | [0x9F, 0x2F, 0x7E],
120 | [0xA1, 0x2F, 0x7E],
121 | [0xA3, 0x30, 0x7E],
122 | [0xA4, 0x30, 0x7D],
123 | [0xA6, 0x31, 0x7D],
124 | [0xA7, 0x31, 0x7D],
125 | [0xA9, 0x32, 0x7C],
126 | [0xAB, 0x33, 0x7C],
127 | [0xAC, 0x33, 0x7B],
128 | [0xAE, 0x34, 0x7B],
129 | [0xB0, 0x34, 0x7B],
130 | [0xB1, 0x35, 0x7A],
131 | [0xB3, 0x35, 0x7A],
132 | [0xB5, 0x36, 0x79],
133 | [0xB6, 0x36, 0x79],
134 | [0xB8, 0x37, 0x78],
135 | [0xB9, 0x37, 0x78],
136 | [0xBB, 0x38, 0x77],
137 | [0xBD, 0x39, 0x77],
138 | [0xBE, 0x39, 0x76],
139 | [0xC0, 0x3A, 0x75],
140 | [0xC2, 0x3A, 0x75],
141 | [0xC3, 0x3B, 0x74],
142 | [0xC5, 0x3C, 0x74],
143 | [0xC6, 0x3C, 0x73],
144 | [0xC8, 0x3D, 0x72],
145 | [0xCA, 0x3E, 0x72],
146 | [0xCB, 0x3E, 0x71],
147 | [0xCD, 0x3F, 0x70],
148 | [0xCE, 0x40, 0x70],
149 | [0xD0, 0x41, 0x6F],
150 | [0xD1, 0x42, 0x6E],
151 | [0xD3, 0x42, 0x6D],
152 | [0xD4, 0x43, 0x6D],
153 | [0xD6, 0x44, 0x6C],
154 | [0xD7, 0x45, 0x6B],
155 | [0xD9, 0x46, 0x6A],
156 | [0xDA, 0x47, 0x69],
157 | [0xDC, 0x48, 0x69],
158 | [0xDD, 0x49, 0x68],
159 | [0xDE, 0x4A, 0x67],
160 | [0xE0, 0x4B, 0x66],
161 | [0xE1, 0x4C, 0x66],
162 | [0xE2, 0x4D, 0x65],
163 | [0xE4, 0x4E, 0x64],
164 | [0xE5, 0x50, 0x63],
165 | [0xE6, 0x51, 0x62],
166 | [0xE7, 0x52, 0x62],
167 | [0xE8, 0x54, 0x61],
168 | [0xEA, 0x55, 0x60],
169 | [0xEB, 0x56, 0x60],
170 | [0xEC, 0x58, 0x5F],
171 | [0xED, 0x59, 0x5F],
172 | [0xEE, 0x5B, 0x5E],
173 | [0xEE, 0x5D, 0x5D],
174 | [0xEF, 0x5E, 0x5D],
175 | [0xF0, 0x60, 0x5D],
176 | [0xF1, 0x61, 0x5C],
177 | [0xF2, 0x63, 0x5C],
178 | [0xF3, 0x65, 0x5C],
179 | [0xF3, 0x67, 0x5B],
180 | [0xF4, 0x68, 0x5B],
181 | [0xF5, 0x6A, 0x5B],
182 | [0xF5, 0x6C, 0x5B],
183 | [0xF6, 0x6E, 0x5B],
184 | [0xF6, 0x70, 0x5B],
185 | [0xF7, 0x71, 0x5B],
186 | [0xF7, 0x73, 0x5C],
187 | [0xF8, 0x75, 0x5C],
188 | [0xF8, 0x77, 0x5C],
189 | [0xF9, 0x79, 0x5C],
190 | [0xF9, 0x7B, 0x5D],
191 | [0xF9, 0x7D, 0x5D],
192 | [0xFA, 0x7F, 0x5E],
193 | [0xFA, 0x80, 0x5E],
194 | [0xFA, 0x82, 0x5F],
195 | [0xFB, 0x84, 0x60],
196 | [0xFB, 0x86, 0x60],
197 | [0xFB, 0x88, 0x61],
198 | [0xFB, 0x8A, 0x62],
199 | [0xFC, 0x8C, 0x63],
200 | [0xFC, 0x8E, 0x63],
201 | [0xFC, 0x90, 0x64],
202 | [0xFC, 0x92, 0x65],
203 | [0xFC, 0x93, 0x66],
204 | [0xFD, 0x95, 0x67],
205 | [0xFD, 0x97, 0x68],
206 | [0xFD, 0x99, 0x69],
207 | [0xFD, 0x9B, 0x6A],
208 | [0xFD, 0x9D, 0x6B],
209 | [0xFD, 0x9F, 0x6C],
210 | [0xFD, 0xA1, 0x6E],
211 | [0xFD, 0xA2, 0x6F],
212 | [0xFD, 0xA4, 0x70],
213 | [0xFE, 0xA6, 0x71],
214 | [0xFE, 0xA8, 0x73],
215 | [0xFE, 0xAA, 0x74],
216 | [0xFE, 0xAC, 0x75],
217 | [0xFE, 0xAE, 0x76],
218 | [0xFE, 0xAF, 0x78],
219 | [0xFE, 0xB1, 0x79],
220 | [0xFE, 0xB3, 0x7B],
221 | [0xFE, 0xB5, 0x7C],
222 | [0xFE, 0xB7, 0x7D],
223 | [0xFE, 0xB9, 0x7F],
224 | [0xFE, 0xBB, 0x80],
225 | [0xFE, 0xBC, 0x82],
226 | [0xFE, 0xBE, 0x83],
227 | [0xFE, 0xC0, 0x85],
228 | [0xFE, 0xC2, 0x86],
229 | [0xFE, 0xC4, 0x88],
230 | [0xFE, 0xC6, 0x89],
231 | [0xFE, 0xC7, 0x8B],
232 | [0xFE, 0xC9, 0x8D],
233 | [0xFE, 0xCB, 0x8E],
234 | [0xFD, 0xCD, 0x90],
235 | [0xFD, 0xCF, 0x92],
236 | [0xFD, 0xD1, 0x93],
237 | [0xFD, 0xD2, 0x95],
238 | [0xFD, 0xD4, 0x97],
239 | [0xFD, 0xD6, 0x98],
240 | [0xFD, 0xD8, 0x9A],
241 | [0xFD, 0xDA, 0x9C],
242 | [0xFD, 0xDC, 0x9D],
243 | [0xFD, 0xDD, 0x9F],
244 | [0xFD, 0xDF, 0xA1],
245 | [0xFD, 0xE1, 0xA3],
246 | [0xFC, 0xE3, 0xA5],
247 | [0xFC, 0xE5, 0xA6],
248 | [0xFC, 0xE6, 0xA8],
249 | [0xFC, 0xE8, 0xAA],
250 | [0xFC, 0xEA, 0xAC],
251 | [0xFC, 0xEC, 0xAE],
252 | [0xFC, 0xEE, 0xB0],
253 | [0xFC, 0xF0, 0xB1],
254 | [0xFC, 0xF1, 0xB3],
255 | [0xFC, 0xF3, 0xB5],
256 | [0xFC, 0xF5, 0xB7],
257 | [0xFB, 0xF7, 0xB9],
258 | [0xFB, 0xF9, 0xBB],
259 | [0xFB, 0xFA, 0xBD],
260 | [0xFB, 0xFC, 0xBF],
261 | ],
262 | dtype="uint8",
263 | )
264 |
--------------------------------------------------------------------------------
/fractals_with_numpy_and_canvas/pyscript.toml:
--------------------------------------------------------------------------------
1 | name = "Fractals with Numpy and Canvas"
2 | description = "Render Fractals using Numpy, Canvas in Pyscript."
3 | packages = ["numpy", "sympy"]
4 |
5 | [files]
6 | "./palettes.py" = ""
7 | "./fractals.py" = ""
8 |
--------------------------------------------------------------------------------
/hello_world/README.md:
--------------------------------------------------------------------------------
1 | # Hello World Example
2 |
3 | This is a basic example of how to create a `PyScript` application that adds a terminal to the page and prints "Hello World" to it. This example is meant to be a starting point for creating more complex applications.
4 |
5 | ## Resources
6 |
7 | - [Pyscript - Terminal](https://pyscript.github.io/docs/2024.3.1/user-guide/terminal/)
--------------------------------------------------------------------------------
/hello_world/assets/css/examples.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | .pyscript {
6 | margin: 0.5rem;
7 | }
8 |
9 | html {
10 | font-family:
11 | ui-sans-serif,
12 | system-ui,
13 | -apple-system,
14 | BlinkMacSystemFont,
15 | "Segoe UI",
16 | Roboto,
17 | "Helvetica Neue",
18 | Arial,
19 | "Noto Sans",
20 | sans-serif,
21 | "Apple Color Emoji",
22 | "Segoe UI Emoji",
23 | "Segoe UI Symbol",
24 | "Noto Color Emoji";
25 | line-height: 1.5;
26 | }
27 |
28 | nav {
29 | position: sticky;
30 | width: 100%;
31 | top: 0;
32 | left: 0;
33 | z-index: 9999;
34 | }
35 |
36 | .logo {
37 | padding-right: 10px;
38 | font-size: 28px;
39 | height: 30px;
40 | max-width: inherit;
41 | }
42 |
43 | .title {
44 | text-decoration: none;
45 | text-decoration-line: none;
46 | text-decoration-style: initial;
47 | text-decoration-color: initial;
48 | font-weight: 400;
49 | font-size: 1.5em;
50 | line-height: 2em;
51 | white-space: nowrap;
52 | }
53 |
54 | .app-header {
55 | display: flex;
56 | align-items: center;
57 | padding: 0.5rem 1rem;
58 | }
59 |
--------------------------------------------------------------------------------
/hello_world/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/hello_world/assets/favicon.png
--------------------------------------------------------------------------------
/hello_world/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/hello_world/assets/logo.png
--------------------------------------------------------------------------------
/hello_world/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
26 |
27 | PyScript Hello World
28 |
29 |
30 |
31 |
32 |
35 |
36 |
44 |
45 |
46 | Hello world!
47 | This is the current date and time, as computed by Python:
48 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/hello_world/pyscript.toml:
--------------------------------------------------------------------------------
1 | name = "Hello World"
2 | description = "Basic hello world example that displays text in a page with PyScript."
3 |
--------------------------------------------------------------------------------
/icosahedron/README.md:
--------------------------------------------------------------------------------
1 | # WebGL Icosahedron
2 |
3 | This example demonstrates how to use Three.js, WebGL and PyScript to create a web-based 3D visualization of an icosahedron.
4 |
5 | This example could be a good start if you wish to use Three.js in your own PyScript applications.
--------------------------------------------------------------------------------
/icosahedron/assets/css/examples.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | .pyscript {
6 | margin: 0.5rem;
7 | }
8 |
9 | html {
10 | font-family:
11 | ui-sans-serif,
12 | system-ui,
13 | -apple-system,
14 | BlinkMacSystemFont,
15 | "Segoe UI",
16 | Roboto,
17 | "Helvetica Neue",
18 | Arial,
19 | "Noto Sans",
20 | sans-serif,
21 | "Apple Color Emoji",
22 | "Segoe UI Emoji",
23 | "Segoe UI Symbol",
24 | "Noto Color Emoji";
25 | line-height: 1.5;
26 | }
27 |
28 | nav {
29 | position: sticky;
30 | width: 100%;
31 | top: 0;
32 | left: 0;
33 | z-index: 9999;
34 | }
35 |
36 | .logo {
37 | padding-right: 10px;
38 | font-size: 28px;
39 | height: 30px;
40 | max-width: inherit;
41 | }
42 |
43 | .title {
44 | text-decoration: none;
45 | text-decoration-line: none;
46 | text-decoration-style: initial;
47 | text-decoration-color: initial;
48 | font-weight: 400;
49 | font-size: 1.5em;
50 | line-height: 2em;
51 | white-space: nowrap;
52 | }
53 |
54 | .app-header {
55 | display: flex;
56 | align-items: center;
57 | padding: 0.5rem 1rem;
58 | }
59 |
--------------------------------------------------------------------------------
/icosahedron/assets/css/icosahedron.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | text-align: center;
4 | background-color: black;
5 | cursor: crosshair;
6 | }
7 |
8 | canvas {
9 | display: block;
10 | width: 100%;
11 | height: 100%;
12 | }
13 |
14 | .header {
15 | top: 45%;
16 | color: #dddddd;
17 | }
18 |
19 | .footer {
20 | bottom: 3%;
21 | }
22 |
23 | .description {
24 | color: gray;
25 | padding-top: 50px;
26 | }
27 |
28 | .btn {
29 | border-radius: 30px;
30 | padding: 10px 30px;
31 | }
32 |
33 | a,
34 | a:hover,
35 | a:visited {
36 | color: red;
37 | text-decoration: none;
38 | }
39 |
40 | .disable-selection {
41 | -moz-user-select: none; /* Firefox */
42 | -ms-user-select: none; /* Internet Explorer */
43 | -khtml-user-select: none; /* KHTML browsers (e.g. Konqueror) */
44 | -webkit-user-select: none; /* Chrome, Safari, and Opera */
45 | -webkit-touch-callout: none; /* Disable Android and iOS callouts*/
46 | }
47 |
48 | h1::after {
49 | content: " V 2.0";
50 | font-size: 12px;
51 | position: absolute;
52 | top: 3px;
53 | padding-left: 5px;
54 | font-weight: 400;
55 | }
56 |
57 | h2::after {
58 | content: "2";
59 | font-size: 12px;
60 | position: absolute;
61 | top: 14px;
62 | padding-left: 5px;
63 | }
64 |
--------------------------------------------------------------------------------
/icosahedron/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/icosahedron/assets/favicon.png
--------------------------------------------------------------------------------
/icosahedron/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/icosahedron/assets/logo.png
--------------------------------------------------------------------------------
/icosahedron/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
26 |
27 | Raycaster Icosahedron
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
38 |
39 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/icosahedron/main.py:
--------------------------------------------------------------------------------
1 | from pyscript.ffi import to_js
2 | from pyscript.js_modules import THREE
3 | from pyscript import when, window, document
4 | from js import Math, performance
5 | import asyncio
6 |
7 | mouse = THREE.Vector2.new()
8 |
9 | renderer = THREE.WebGLRenderer.new({"antialias": True})
10 | renderer.setSize(1000, 1000)
11 | renderer.shadowMap.enabled = False
12 | renderer.shadowMap.type = THREE.PCFSoftShadowMap
13 | renderer.shadowMap.needsUpdate = True
14 |
15 | document.body.appendChild(renderer.domElement)
16 |
17 | @when("mousemove", "body")
18 | def onMouseMove(event):
19 | event.preventDefault()
20 | mouse.x = (event.clientX / window.innerWidth) * 2 - 1
21 | mouse.y = -(event.clientY / window.innerHeight) * 2 + 1
22 |
23 | camera = THREE.PerspectiveCamera.new(35, window.innerWidth / window.innerHeight, 1, 500)
24 | scene = THREE.Scene.new()
25 | cameraRange = 3
26 |
27 | camera.aspect = window.innerWidth / window.innerHeight
28 | camera.updateProjectionMatrix()
29 | renderer.setSize( window.innerWidth, window.innerHeight )
30 |
31 | setcolor = "#000000"
32 |
33 | scene.background = THREE.Color.new(setcolor)
34 | scene.fog = THREE.Fog.new(setcolor, 2.5, 3.5)
35 |
36 | sceneGroup = THREE.Object3D.new()
37 | particularGroup = THREE.Object3D.new()
38 |
39 | def mathRandom(num = 1):
40 | setNumber = - Math.random() * num + Math.random() * num
41 | return setNumber
42 |
43 | particularGroup = THREE.Object3D.new()
44 | modularGroup = THREE.Object3D.new()
45 |
46 | perms = to_js({"flatShading":True, "color":"#111111", "transparent":False, "opacity":1, "wireframe":False})
47 |
48 | particle_perms = to_js({"color":"#FFFFFF", "side":THREE.DoubleSide})
49 |
50 | def create_cubes(mathRandom, modularGroup):
51 | i = 0
52 | while i < 30:
53 | geometry = THREE.IcosahedronGeometry.new()
54 | material = THREE.MeshStandardMaterial.new(perms)
55 | cube = THREE.Mesh.new(geometry, material)
56 | cube.speedRotation = Math.random() * 0.1
57 | cube.positionX = mathRandom()
58 | cube.positionY = mathRandom()
59 | cube.positionZ = mathRandom()
60 | cube.castShadow = True
61 | cube.receiveShadow = True
62 | newScaleValue = mathRandom(0.3)
63 | cube.scale.set(newScaleValue,newScaleValue,newScaleValue)
64 | cube.rotation.x = mathRandom(180 * Math.PI / 180)
65 | cube.rotation.y = mathRandom(180 * Math.PI / 180)
66 | cube.rotation.z = mathRandom(180 * Math.PI / 180)
67 | cube.position.set(cube.positionX, cube.positionY, cube.positionZ)
68 | modularGroup.add(cube)
69 | i += 1
70 |
71 | create_cubes(mathRandom, modularGroup)
72 |
73 |
74 | def generateParticle(mathRandom, particularGroup, num, amp = 2):
75 | gmaterial = THREE.MeshPhysicalMaterial.new(particle_perms)
76 | gparticular = THREE.CircleGeometry.new(0.2,5)
77 | i = 0
78 | while i < num:
79 | pscale = 0.001+Math.abs(mathRandom(0.03))
80 | particular = THREE.Mesh.new(gparticular, gmaterial)
81 | particular.position.set(mathRandom(amp),mathRandom(amp),mathRandom(amp))
82 | particular.rotation.set(mathRandom(),mathRandom(),mathRandom())
83 | particular.scale.set(pscale,pscale,pscale)
84 | particular.speedValue = mathRandom(1)
85 | particularGroup.add(particular)
86 | i += 1
87 |
88 | generateParticle(mathRandom, particularGroup, 200, 2)
89 |
90 | sceneGroup.add(particularGroup)
91 | scene.add(modularGroup)
92 | scene.add(sceneGroup)
93 |
94 | camera.position.set(0, 0, cameraRange)
95 | cameraValue = False
96 |
97 | ambientLight = THREE.AmbientLight.new(0xFFFFFF, 0.1)
98 |
99 | light = THREE.SpotLight.new(0xFFFFFF, 3)
100 | light.position.set(5, 5, 2)
101 | light.castShadow = True
102 | light.shadow.mapSize.width = 10000
103 | light.shadow.mapSize.height = light.shadow.mapSize.width
104 | light.penumbra = 0.5
105 |
106 | lightBack = THREE.PointLight.new(0x0FFFFF, 1)
107 | lightBack.position.set(0, -3, -1)
108 |
109 | scene.add(sceneGroup)
110 | #scene.add(light)
111 | #scene.add(lightBack)
112 | #scene.add(ambientLight)
113 |
114 | rectSize = 3
115 | intensity = 44
116 | rectLight = THREE.RectAreaLight.new( 0x0FFFFF, intensity, rectSize, rectSize )
117 | rectLight.position.set( 0, 0, 1 )
118 | rectLight.lookAt( 0, 0, 0 )
119 | scene.add(rectLight)
120 |
121 | raycaster = THREE.Raycaster.new()
122 | uSpeed = 0.1
123 |
124 | time = 0.0003
125 | camera.lookAt(scene.position)
126 |
127 | async def main():
128 | while True:
129 | time = performance.now() * 0.0003
130 | i = 0
131 | while i < particularGroup.children.length:
132 | newObject = particularGroup.children[i]
133 | newObject.rotation.x += newObject.speedValue/10
134 | newObject.rotation.y += newObject.speedValue/10
135 | newObject.rotation.z += newObject.speedValue/10
136 | i += 1
137 |
138 | i = 0
139 | while i < modularGroup.children.length:
140 | newCubes = modularGroup.children[i]
141 | newCubes.rotation.x += 0.008
142 | newCubes.rotation.y += 0.005
143 | newCubes.rotation.z += 0.003
144 |
145 | newCubes.position.x = Math.sin(time * newCubes.positionZ) * newCubes.positionY
146 | newCubes.position.y = Math.cos(time * newCubes.positionX) * newCubes.positionZ
147 | newCubes.position.z = Math.sin(time * newCubes.positionY) * newCubes.positionX
148 | i += 1
149 |
150 | particularGroup.rotation.y += 0.005
151 |
152 | modularGroup.rotation.y -= ((mouse.x * 4) + modularGroup.rotation.y) * uSpeed
153 | modularGroup.rotation.x -= ((-mouse.y * 4) + modularGroup.rotation.x) * uSpeed
154 |
155 | renderer.render( scene, camera )
156 | await asyncio.sleep(0.02)
157 |
158 | main()
--------------------------------------------------------------------------------
/icosahedron/pyscript.toml:
--------------------------------------------------------------------------------
1 | name = "WebGL Icosahedron"
2 | description = "An example how to use Three.js and WebGL to render an Icosahedron in PyScript."
3 |
4 | [js_modules.main]
5 | "https://esm.run/three" = "THREE"
--------------------------------------------------------------------------------
/matplotlib/README.md:
--------------------------------------------------------------------------------
1 | # Matplotlib Example
2 |
3 | This application uses Matplotlib and Numpy to create a Delaunay triangulation of a set of points and then uses the Pyscript `display` feature to display the graph on the page.
4 |
5 | ## Libraries Used
6 |
7 | - [Matplotlib](https://matplotlib.org/)
8 | - [Numpy](https://numpy.org/)
9 |
10 | ## Resources
11 |
12 | - [Pyscript - Display](https://pyscript.github.io/docs/2024.3.1/user-guide/builtins/#pyscriptdisplay)
13 | - [Matplotlib - Delaunay Triangulation](https://matplotlib.org/stable/gallery/images_contours_and_fields/tripcolor_demo.html)
--------------------------------------------------------------------------------
/matplotlib/assets/css/examples.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | .pyscript {
6 | margin: 0.5rem;
7 | }
8 |
9 | html {
10 | font-family:
11 | ui-sans-serif,
12 | system-ui,
13 | -apple-system,
14 | BlinkMacSystemFont,
15 | "Segoe UI",
16 | Roboto,
17 | "Helvetica Neue",
18 | Arial,
19 | "Noto Sans",
20 | sans-serif,
21 | "Apple Color Emoji",
22 | "Segoe UI Emoji",
23 | "Segoe UI Symbol",
24 | "Noto Color Emoji";
25 | line-height: 1.5;
26 | }
27 |
28 | nav {
29 | position: sticky;
30 | width: 100%;
31 | top: 0;
32 | left: 0;
33 | z-index: 9999;
34 | }
35 |
36 | .logo {
37 | padding-right: 10px;
38 | font-size: 28px;
39 | height: 30px;
40 | max-width: inherit;
41 | }
42 |
43 | .title {
44 | text-decoration: none;
45 | text-decoration-line: none;
46 | text-decoration-style: initial;
47 | text-decoration-color: initial;
48 | font-weight: 400;
49 | font-size: 1.5em;
50 | line-height: 2em;
51 | white-space: nowrap;
52 | }
53 |
54 | .app-header {
55 | display: flex;
56 | align-items: center;
57 | padding: 0.5rem 1rem;
58 | }
59 |
--------------------------------------------------------------------------------
/matplotlib/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/matplotlib/assets/favicon.png
--------------------------------------------------------------------------------
/matplotlib/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/matplotlib/assets/logo.png
--------------------------------------------------------------------------------
/matplotlib/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
26 |
27 | Matplotlib
28 |
29 |
30 |
31 |
32 |
35 |
36 |
44 |
45 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/matplotlib/main.py:
--------------------------------------------------------------------------------
1 | import matplotlib.pyplot as plt
2 | import matplotlib.tri as tri
3 | import numpy as np
4 |
5 | from pyscript import display
6 |
7 | # First create the x and y coordinates of the points.
8 | n_angles = 36
9 | n_radii = 8
10 | min_radius = 0.25
11 | radii = np.linspace(min_radius, 0.95, n_radii)
12 |
13 | angles = np.linspace(0, 2 * np.pi, n_angles, endpoint=False)
14 | angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)
15 | angles[:, 1::2] += np.pi / n_angles
16 |
17 | x = (radii * np.cos(angles)).flatten()
18 | y = (radii * np.sin(angles)).flatten()
19 | z = (np.cos(radii) * np.cos(3 * angles)).flatten()
20 |
21 | # Create the Triangulation; no triangles so Delaunay triangulation created.
22 | triang = tri.Triangulation(x, y)
23 |
24 | # Mask off unwanted triangles.
25 | triang.set_mask(np.hypot(x[triang.triangles].mean(axis=1),
26 | y[triang.triangles].mean(axis=1))
27 | < min_radius)
28 |
29 | fig1, ax1 = plt.subplots()
30 | ax1.set_aspect('equal')
31 | tpc = ax1.tripcolor(triang, z, shading='flat')
32 | fig1.colorbar(tpc)
33 | ax1.set_title('tripcolor of Delaunay triangulation, flat shading')
34 |
35 | display(fig1, target="mpl")
36 |
--------------------------------------------------------------------------------
/matplotlib/pyscript.toml:
--------------------------------------------------------------------------------
1 | name = "Matplotlib"
2 | description = "A simple application showing how to use Matplotlib to generate a graph and display it with PyScript."
3 | packages = ["matplotlib"]
4 |
--------------------------------------------------------------------------------
/pandas/README.md:
--------------------------------------------------------------------------------
1 | # Pandas Example
2 |
3 | This application uses `Pandas` to load a CSV file and display the data in a table on the page. It also includes an input field and a button which allows you to change the data to be loaded in the table.
4 |
5 | ## Libraries Used
6 |
7 | - [Pandas](https://pandas.pydata.org/)
8 |
9 | ## Resources
10 |
11 | - [Pyscript - Display](https://pyscript.github.io/docs/2024.3.1/user-guide/builtins/#pyscriptdisplay)
12 | - [Pandas - Read CSV](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.read_csv.html)
13 | - [Pyodide - open_url](https://pyodide.org/en/stable/usage/api/python-api/http.html#pyodide.http.open_url)
--------------------------------------------------------------------------------
/pandas/assets/css/examples.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | .pyscript {
6 | margin: 0.5rem;
7 | }
8 |
9 | html {
10 | font-family:
11 | ui-sans-serif,
12 | system-ui,
13 | -apple-system,
14 | BlinkMacSystemFont,
15 | "Segoe UI",
16 | Roboto,
17 | "Helvetica Neue",
18 | Arial,
19 | "Noto Sans",
20 | sans-serif,
21 | "Apple Color Emoji",
22 | "Segoe UI Emoji",
23 | "Segoe UI Symbol",
24 | "Noto Color Emoji";
25 | line-height: 1.5;
26 | }
27 |
28 | nav {
29 | position: sticky;
30 | width: 100%;
31 | top: 0;
32 | left: 0;
33 | z-index: 9999;
34 | }
35 |
36 | .logo {
37 | padding-right: 10px;
38 | font-size: 28px;
39 | height: 30px;
40 | max-width: inherit;
41 | }
42 |
43 | .title {
44 | text-decoration: none;
45 | text-decoration-line: none;
46 | text-decoration-style: initial;
47 | text-decoration-color: initial;
48 | font-weight: 400;
49 | font-size: 1.5em;
50 | line-height: 2em;
51 | white-space: nowrap;
52 | }
53 |
54 | .app-header {
55 | display: flex;
56 | align-items: center;
57 | padding: 0.5rem 1rem;
58 | }
59 |
--------------------------------------------------------------------------------
/pandas/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/pandas/assets/favicon.png
--------------------------------------------------------------------------------
/pandas/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/pandas/assets/logo.png
--------------------------------------------------------------------------------
/pandas/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
36 |
37 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
Data Source
55 |
56 |
64 |
65 |
66 |
70 |
71 |
72 |
Dev Console
73 |
74 |
75 |
76 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/pandas/main.py:
--------------------------------------------------------------------------------
1 | import pandas as pd
2 | from pyweb import pydom
3 | from pyodide.http import open_url
4 | from pyscript import display
5 | from js import console
6 |
7 | title = "Pandas (and basic DOM manipulation)"
8 | page_message = "This example loads a remote CSV file into a Pandas dataframe, and displays it."
9 | url = "https://raw.githubusercontent.com/datasets/airport-codes/master/data/airport-codes.csv"
10 |
11 | pydom["title#header-title"].html = title
12 | pydom["a#page-title"].html = title
13 | pydom["div#page-message"].html = page_message
14 | pydom["input#txt-url"][0].value = url
15 |
16 | def log(message):
17 | # log to pandas dev console
18 | print(message)
19 | # log to JS console
20 | console.log(message)
21 |
22 | def loadFromURL(event):
23 | pydom["div#pandas-output-inner"].html = ""
24 | url = pydom["input#txt-url"][0].value
25 |
26 | log(f"Trying to fetch CSV from {url}")
27 | df = pd.read_csv(open_url(url))
28 |
29 | pydom["div#pandas-output"].style["display"] = "block"
30 | pydom["div#pandas-dev-console"].style["display"] = "block"
31 |
32 | display(df, target="pandas-output-inner", append="False")
33 |
--------------------------------------------------------------------------------
/pandas/pyscript.toml:
--------------------------------------------------------------------------------
1 | name = "Pandas"
2 | description = "A simple application that loads a csv and displays a table of its contents."
3 | packages = ["pandas"]
4 |
--------------------------------------------------------------------------------
/panel/README.md:
--------------------------------------------------------------------------------
1 | # Simple Panel Example
2 |
3 | This is a basic example of a Panel application with PyScript - similar to the Hello World application, this example is aimed for you to get up and running with Panel and PyScript so you can extend it to your own needs.
4 |
5 | ## Libraries Used
6 |
7 | - [Panel](https://panel.holoviz.org/)
8 |
--------------------------------------------------------------------------------
/panel/assets/css/examples.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | .pyscript {
6 | margin: 0.5rem;
7 | }
8 |
9 | html {
10 | font-family:
11 | ui-sans-serif,
12 | system-ui,
13 | -apple-system,
14 | BlinkMacSystemFont,
15 | "Segoe UI",
16 | Roboto,
17 | "Helvetica Neue",
18 | Arial,
19 | "Noto Sans",
20 | sans-serif,
21 | "Apple Color Emoji",
22 | "Segoe UI Emoji",
23 | "Segoe UI Symbol",
24 | "Noto Color Emoji";
25 | line-height: 1.5;
26 | }
27 |
28 | nav {
29 | position: sticky;
30 | width: 100%;
31 | top: 0;
32 | left: 0;
33 | z-index: 9999;
34 | }
35 |
36 | .logo {
37 | padding-right: 10px;
38 | font-size: 28px;
39 | height: 30px;
40 | max-width: inherit;
41 | }
42 |
43 | .title {
44 | text-decoration: none;
45 | text-decoration-line: none;
46 | text-decoration-style: initial;
47 | text-decoration-color: initial;
48 | font-weight: 400;
49 | font-size: 1.5em;
50 | line-height: 2em;
51 | white-space: nowrap;
52 | }
53 |
54 | .app-header {
55 | display: flex;
56 | align-items: center;
57 | padding: 0.5rem 1rem;
58 | }
59 |
--------------------------------------------------------------------------------
/panel/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/panel/assets/favicon.png
--------------------------------------------------------------------------------
/panel/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/panel/assets/logo.png
--------------------------------------------------------------------------------
/panel/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
26 |
27 | Panel Example
28 |
29 |
30 |
31 |
32 |
33 |
34 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
46 |
47 |
55 |
56 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/panel/main.py:
--------------------------------------------------------------------------------
1 | import panel as pn
2 |
3 | pn.extension(sizing_mode="stretch_width")
4 |
5 | slider = pn.widgets.FloatSlider(start=0, end=10, name='Amplitude')
6 |
7 | def callback(new):
8 | return f'Amplitude is: {new}'
9 |
10 | pn.Row(slider, pn.bind(callback, slider)).servable(target='simple_app')
11 |
--------------------------------------------------------------------------------
/panel/pyscript.toml:
--------------------------------------------------------------------------------
1 | name = "Simple Panel"
2 | description = "A basic Panel application to be used with PyScript."
3 | packages = ["panel==1.3.8", "bokeh==3.2.2"]
4 |
--------------------------------------------------------------------------------
/panel_deckgl/README.md:
--------------------------------------------------------------------------------
1 | # NYC Taxi Panel DeckGL Example
2 |
3 | This example demonstrates how to use Panel and DeckGL to create a web-based dashboard for exploring a large dataset of taxi trips in New York City. The example uses the [NYC Taxi Dataset](https://www1.nyc.gov/site/tlc/about/tlc-trip-record-data.page) to visualize the pickup and dropoff locations of taxi trips on a map, and to explore the distribution of trip attributes such as trip distance, fare amount, and tip amount.
4 |
5 | ## Libraries Used
6 |
7 | - [Panel](https://panel.holoviz.org): A high-level app and dashboarding solution for Python
8 | - [DeckGL](https://deck.gl): A WebGL-powered framework for visual exploratory data analysis of large datasets
9 | - [Bokeh](https://bokeh.org): A powerful framework for building web-based interactive visualizations
10 | - [Pandas](https://pandas.pydata.org): A fast, powerful, flexible, and easy-to-use open-source data analysis and data manipulation library built on top of the Python programming language
11 | - [NumPy](https://numpy.org): The fundamental package for scientific computing with Python
--------------------------------------------------------------------------------
/panel_deckgl/assets/css/examples.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | .pyscript {
6 | height: 100%;
7 | }
8 |
9 | html {
10 | font-family:
11 | ui-sans-serif,
12 | system-ui,
13 | -apple-system,
14 | BlinkMacSystemFont,
15 | "Segoe UI",
16 | Roboto,
17 | "Helvetica Neue",
18 | Arial,
19 | "Noto Sans",
20 | sans-serif,
21 | "Apple Color Emoji",
22 | "Segoe UI Emoji",
23 | "Segoe UI Symbol",
24 | "Noto Color Emoji";
25 | line-height: 1.5;
26 | }
27 |
28 | nav {
29 | position: sticky;
30 | width: 100%;
31 | top: 0;
32 | left: 0;
33 | z-index: 9999;
34 | }
35 |
36 | .logo {
37 | padding-right: 10px;
38 | font-size: 28px;
39 | height: 30px;
40 | max-width: inherit;
41 | }
42 |
43 | .title {
44 | text-decoration: none;
45 | text-decoration-line: none;
46 | text-decoration-style: initial;
47 | text-decoration-color: initial;
48 | font-weight: 400;
49 | font-size: 1.5em;
50 | line-height: 2em;
51 | white-space: nowrap;
52 | }
53 |
54 | .app-header {
55 | display: flex;
56 | align-items: center;
57 | padding: 0.5rem 1rem;
58 | }
59 |
60 | #sidebar {
61 | width: 350px;
62 | }
63 |
64 | #plot {
65 | height: 100%;
66 | }
67 |
--------------------------------------------------------------------------------
/panel_deckgl/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/panel_deckgl/assets/favicon.png
--------------------------------------------------------------------------------
/panel_deckgl/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/panel_deckgl/assets/logo.png
--------------------------------------------------------------------------------
/panel_deckgl/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
26 |
27 | PyScript/Panel DeckGL Demo
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
67 |
68 |
76 |
77 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/panel_deckgl/main.py:
--------------------------------------------------------------------------------
1 | import panel as pn
2 | import pandas as pd
3 | import param
4 |
5 | from pyodide.http import open_url
6 |
7 | MAPBOX_KEY = "pk.eyJ1IjoicGFuZWxvcmciLCJhIjoiY2s1enA3ejhyMWhmZjNobjM1NXhtbWRrMyJ9.B_frQsAVepGIe-HiOJeqvQ"
8 |
9 | class App(param.Parameterized):
10 | data = param.DataFrame(precedence=-1)
11 | view = param.DataFrame(precedence=-1)
12 | arc_view = param.DataFrame(precedence=-1)
13 | radius = param.Integer(default=50, bounds=(20, 1000))
14 | elevation = param.Integer(default=10, bounds=(0, 50))
15 | hour = param.Integer(default=0, bounds=(0, 23))
16 | speed = param.Integer(default=1, bounds=(0, 10), precedence=-1)
17 | play = param.Event(label='▷')
18 |
19 | def __init__(self, **params):
20 | self.deck_gl = None
21 | super().__init__(**params)
22 | self.deck_gl = pn.pane.DeckGL(
23 | dict(self.spec),
24 | mapbox_api_key=MAPBOX_KEY,
25 | throttle={'click': 10},
26 | sizing_mode='stretch_both',
27 | margin=0
28 | )
29 | self.deck_gl.param.watch(self._update_arc_view, 'click_state')
30 | self._playing = False
31 | self._cb = pn.state.add_periodic_callback(
32 | self._update_hour, 1000//self.speed, start=False
33 | )
34 |
35 | @property
36 | def spec(self):
37 | return {
38 | "initialViewState": {
39 | "bearing": 0,
40 | "latitude": 40.7,
41 | "longitude": -73.9,
42 | "maxZoom": 15,
43 | "minZoom": 5,
44 | "pitch": 40.5,
45 | "zoom": 11
46 | },
47 | "layers": [self.hex_layer, self.arc_layer],
48 | "mapStyle": "mapbox://styles/mapbox/dark-v9",
49 | "views": [
50 | {"@@type": "MapView", "controller": True}
51 | ]
52 | }
53 |
54 | @property
55 | def hex_layer(self):
56 | return {
57 | "@@type": "HexagonLayer",
58 | "autoHighlight": True,
59 | "coverage": 1,
60 | "data": self.data if self.view is None else self.view,
61 | "elevationRange": [0, 100],
62 | "elevationScale": self.elevation,
63 | "radius": self.radius,
64 | "extruded": True,
65 | "getPosition": "@@=[pickup_x, pickup_y]",
66 | "id": "8a553b25-ef3a-489c-bbe2-e102d18a3211"
67 | }
68 |
69 | @property
70 | def arc_layer(self):
71 | return {
72 | "@@type": "ArcLayer",
73 | "id": 'arc-layer',
74 | "data": self.arc_view,
75 | "pickable": True,
76 | "getWidth": 1,
77 | "getSourcePosition": "@@=[pickup_x, pickup_y]",
78 | "getTargetPosition": "@@=[dropoff_x, dropoff_y]",
79 | "getSourceColor": [0, 255, 0, 180],
80 | "getTargetColor": [240, 100, 0, 180]
81 | }
82 |
83 | def _update_hour(self):
84 | self.hour = (self.hour+1) % 24
85 |
86 | @param.depends('view', watch=True)
87 | def _update_arc_view(self, event=None):
88 | data = self.data if self.view is None else self.view
89 | if not self.deck_gl or not self.deck_gl.click_state:
90 | self.arc_view = data.iloc[:0]
91 | return
92 | lon, lat = self.deck_gl.click_state['coordinate']
93 | tol = 0.001
94 | self.arc_view = data[
95 | (df.pickup_x>=float(lon-tol)) &
96 | (df.pickup_x<=float(lon+tol)) &
97 | (df.pickup_y>=float(lat-tol)) &
98 | (df.pickup_y<=float(lat+tol))
99 | ]
100 |
101 | @param.depends('hour', watch=True, on_init=True)
102 | def _update_hourly_view(self):
103 | self.view = self.data[self.data.hour==self.hour]
104 |
105 | @param.depends('speed', watch=True)
106 | def _update_speed(self):
107 | self._cb.period = 1000//self.speed
108 |
109 | @param.depends('play', watch=True)
110 | def _play_pause(self):
111 | if self._playing:
112 | self._cb.stop()
113 | self.param.play.label = '▷'
114 | self.param.speed.precedence = -1
115 | else:
116 | self._cb.start()
117 | self.param.play.label = '❚❚'
118 | self.param.speed.precedence = 1
119 | self._playing = not self._playing
120 |
121 | @param.depends('view', 'radius', 'elevation', 'arc_view', watch=True)
122 | def update_spec(self):
123 | if self.deck_gl:
124 | self.deck_gl.object = dict(self.spec)
125 |
126 | url = 'https://s3.eu-west-1.amazonaws.com/assets.holoviews.org/data/nyc_taxi_wide.csv'
127 | df = pd.read_csv(open_url(url))
128 | app = App(data=df)
129 | controls = pn.Param(app.param, sizing_mode='stretch_width', show_name=False)
130 |
131 | app.deck_gl.servable(target='plot')
132 | controls.servable(target='widgets')
133 |
--------------------------------------------------------------------------------
/panel_deckgl/pyscript.toml:
--------------------------------------------------------------------------------
1 | name = "NYC Taxi Panel DeckGL"
2 | description = "A Panel application that uses the NYC Taxi dataset and DeckGL to generate a data visualisation on a map."
3 | packages = ["bokeh==3.2.2", "numpy", "pandas", "panel==1.3.8"]
4 |
--------------------------------------------------------------------------------
/panel_kmeans/README.md:
--------------------------------------------------------------------------------
1 | # Panel KMeans Examples
2 |
3 | This application provides an example of **building a simple dashboard using Panel**.
4 |
5 | It demonstrates how to take the output of **k-means
6 | clustering on the Penguins dataset** using scikit-learn,
7 | parameterizing the number of clusters and the variables to
8 | plot.
9 |
10 | The plot and the table are linked, i.e. selecting on the plot
11 | will filter the data in the table. The **`x` marks the center** of the cluster.
12 |
13 | ## Libraries Used
14 |
15 | - [Panel](https://panel.holoviz.org/)
16 | - [Bokeh](https://bokeh.org/)
17 | - [Altair](https://altair-viz.github.io/)
18 | - [scikit-learn](https://scikit-learn.org/stable/)
19 | - [NumPy](https://numpy.org/)
20 | - [Pandas](https://pandas.pydata.org/)
21 |
--------------------------------------------------------------------------------
/panel_kmeans/assets/css/examples.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | .pyscript {
6 | margin: 0.5rem;
7 | }
8 |
9 | html {
10 | font-family:
11 | ui-sans-serif,
12 | system-ui,
13 | -apple-system,
14 | BlinkMacSystemFont,
15 | "Segoe UI",
16 | Roboto,
17 | "Helvetica Neue",
18 | Arial,
19 | "Noto Sans",
20 | sans-serif,
21 | "Apple Color Emoji",
22 | "Segoe UI Emoji",
23 | "Segoe UI Symbol",
24 | "Noto Color Emoji";
25 | line-height: 1.5;
26 | }
27 |
28 | nav {
29 | position: sticky;
30 | width: 100%;
31 | top: 0;
32 | left: 0;
33 | z-index: 9999;
34 | }
35 |
36 | .logo {
37 | padding-right: 10px;
38 | font-size: 28px;
39 | height: 30px;
40 | max-width: inherit;
41 | }
42 |
43 | .title {
44 | text-decoration: none;
45 | text-decoration-line: none;
46 | text-decoration-style: initial;
47 | text-decoration-color: initial;
48 | font-weight: 400;
49 | font-size: 1.5em;
50 | line-height: 2em;
51 | white-space: nowrap;
52 | }
53 |
54 | .app-header {
55 | display: flex;
56 | align-items: center;
57 | padding: 0.5rem 1rem;
58 | }
59 |
--------------------------------------------------------------------------------
/panel_kmeans/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/panel_kmeans/assets/favicon.png
--------------------------------------------------------------------------------
/panel_kmeans/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/panel_kmeans/assets/logo.png
--------------------------------------------------------------------------------
/panel_kmeans/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
26 |
27 | Pyscript/Panel KMeans Demo
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
55 |
56 |
57 |
58 |
59 |
60 |
65 |
66 |
67 |
68 |
69 |
72 |
73 |
81 |
82 |
100 |
101 |
102 |
103 |
--------------------------------------------------------------------------------
/panel_kmeans/main.py:
--------------------------------------------------------------------------------
1 | import altair as alt
2 | import panel as pn
3 | import pandas as pd
4 | import param
5 |
6 | from sklearn.cluster import KMeans
7 | from pyodide.http import open_url
8 |
9 | pn.config.sizing_mode = 'stretch_width'
10 |
11 | url = 'https://raw.githubusercontent.com/rfordatascience/tidytuesday/master/data/2020/2020-07-28/penguins.csv'
12 | penguins = pd.read_csv(open_url(url)).dropna()
13 | cols = list(penguins.columns)[2:6]
14 |
15 | x = pn.widgets.Select(name='x', options=cols, value='bill_depth_mm').servable(target='x-widget')
16 | y = pn.widgets.Select(name='y', options=cols, value='bill_length_mm').servable(target='y-widget')
17 | n_clusters = pn.widgets.IntSlider(name='n_clusters', start=1, end=5, value=3).servable(target='n-widget')
18 |
19 | brush = alt.selection_interval(name='brush') # selection of type "interval"
20 |
21 | def get_clusters(n_clusters):
22 | kmeans = KMeans(n_clusters=n_clusters, n_init=10)
23 | est = kmeans.fit(penguins[cols].values)
24 | df = penguins.copy()
25 | df['labels'] = est.labels_.astype('str')
26 | return df
27 |
28 | def get_chart(x, y, df):
29 | centers = df.groupby('labels').mean(numeric_only=True)
30 | return (
31 | alt.Chart(df)
32 | .mark_point(size=100)
33 | .encode(
34 | x=alt.X(x, scale=alt.Scale(zero=False)),
35 | y=alt.Y(y, scale=alt.Scale(zero=False)),
36 | shape='labels',
37 | color='species'
38 | ).add_params(brush).properties(width=800) +
39 | alt.Chart(centers)
40 | .mark_point(size=250, shape='cross', color='black')
41 | .encode(x=x+':Q', y=y+':Q')
42 | )
43 |
44 | intro = pn.pane.Markdown("""
45 | This app provides an example of **building a simple dashboard using
46 | Panel**.\n\nIt demonstrates how to take the output of **k-means
47 | clustering on the Penguins dataset** using scikit-learn,
48 | parameterizing the number of clusters and the variables to
49 | plot.\n\nThe plot and the table are linked, i.e. selecting on the plot
50 | will filter the data in the table.\n\n The **`x` marks the center** of
51 | the cluster.
52 | """).servable(target='intro')
53 |
54 | chart = pn.pane.Vega().servable(target='cluster-plot')
55 | table = pn.widgets.Tabulator(pagination='remote', page_size=10).servable(target='table')
56 |
57 | def update_table(event=None):
58 | table.value = get_clusters(n_clusters.value)
59 |
60 | n_clusters.param.watch(update_table, 'value')
61 |
62 | @pn.depends(x, y, n_clusters, watch=True)
63 | def update_chart(*events):
64 | chart.object = get_chart(x.value, y.value, table.value)
65 |
66 | @param.depends('brush', watch=True)
67 | def update_filters(event=None):
68 | filters = []
69 | for k, v in (getattr(event, 'new') or {}).items():
70 | filters.append(dict(field=k, type='>=', value=v[0]))
71 | filters.append(dict(field=k, type='<=', value=v[1]))
72 | table.filters = filters
73 |
74 | update_table()
75 | update_chart()
76 |
--------------------------------------------------------------------------------
/panel_kmeans/pyscript.toml:
--------------------------------------------------------------------------------
1 | name = "KMeans in Panel"
2 | description = "An example of how to build a dashboard using Panel."
3 | packages = ["bokeh==3.2.2", "altair", "numpy", "pandas", "scikit-learn", "panel==1.3.8"]
4 |
--------------------------------------------------------------------------------
/panel_streaming/README.md:
--------------------------------------------------------------------------------
1 | # Panel Streaming Example
2 |
3 | This application provides an example of how to handle streaming data and display it in a Panel application.
4 |
5 | ## Libraries Used
6 |
7 | - [Panel](https://panel.holoviz.org/)
8 | - [Bokeh](https://bokeh.org/)
9 | - [NumPy](https://numpy.org/)
10 | - [Pandas](https://pandas.pydata.org/)
11 |
--------------------------------------------------------------------------------
/panel_streaming/assets/css/examples.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | .pyscript {
6 | margin: 0.5rem;
7 | }
8 |
9 | html {
10 | font-family:
11 | ui-sans-serif,
12 | system-ui,
13 | -apple-system,
14 | BlinkMacSystemFont,
15 | "Segoe UI",
16 | Roboto,
17 | "Helvetica Neue",
18 | Arial,
19 | "Noto Sans",
20 | sans-serif,
21 | "Apple Color Emoji",
22 | "Segoe UI Emoji",
23 | "Segoe UI Symbol",
24 | "Noto Color Emoji";
25 | line-height: 1.5;
26 | }
27 |
28 | nav {
29 | position: sticky;
30 | width: 100%;
31 | top: 0;
32 | left: 0;
33 | z-index: 9999;
34 | }
35 |
36 | .logo {
37 | padding-right: 10px;
38 | font-size: 28px;
39 | height: 30px;
40 | max-width: inherit;
41 | }
42 |
43 | .title {
44 | text-decoration: none;
45 | text-decoration-line: none;
46 | text-decoration-style: initial;
47 | text-decoration-color: initial;
48 | font-weight: 400;
49 | font-size: 1.5em;
50 | line-height: 2em;
51 | white-space: nowrap;
52 | }
53 |
54 | .app-header {
55 | display: flex;
56 | align-items: center;
57 | padding: 0.5rem 1rem;
58 | }
59 |
--------------------------------------------------------------------------------
/panel_streaming/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/panel_streaming/assets/favicon.png
--------------------------------------------------------------------------------
/panel_streaming/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/panel_streaming/assets/logo.png
--------------------------------------------------------------------------------
/panel_streaming/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
26 |
27 | PyScript/Panel Streaming Demo
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
60 |
61 |
69 |
70 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/panel_streaming/main.py:
--------------------------------------------------------------------------------
1 | import panel as pn
2 | import numpy as np
3 | import pandas as pd
4 |
5 | from bokeh.models import ColumnDataSource
6 | from bokeh.plotting import figure
7 |
8 | df = pd.DataFrame(np.random.randn(10, 4), columns=list('ABCD')).cumsum()
9 |
10 | rollover = pn.widgets.IntInput(name='Rollover', value=15)
11 | follow = pn.widgets.Checkbox(name='Follow', value=True, align='end')
12 |
13 | tabulator = pn.widgets.Tabulator(df, height=450, width=400).servable(target='table')
14 |
15 | def color_negative_red(val):
16 | """
17 | Takes a scalar and returns a string with
18 | the css property `'color: red'` for negative
19 | strings, black otherwise.
20 | """
21 | color = 'red' if val < 0 else 'green'
22 | return 'color: %s' % color
23 |
24 | tabulator.style.applymap(color_negative_red)
25 |
26 | p = figure(height=450, width=600)
27 |
28 | cds = ColumnDataSource(data=ColumnDataSource.from_df(df))
29 |
30 | p.line('index', 'A', source=cds, line_color='red')
31 | p.line('index', 'B', source=cds, line_color='green')
32 | p.line('index', 'C', source=cds, line_color='blue')
33 | p.line('index', 'D', source=cds, line_color='purple')
34 |
35 | def stream():
36 | data = df.iloc[-1] + np.random.randn(4)
37 | tabulator.stream(data, rollover=rollover.value, follow=follow.value)
38 | value = {k: [v] for k, v in tabulator.value.iloc[-1].to_dict().items()}
39 | value['index'] = [tabulator.value.index[-1]]
40 | cds.stream(value)
41 |
42 | cb = pn.state.add_periodic_callback(stream, 200)
43 |
44 | pn.pane.Bokeh(p).servable(target='plot')
45 | pn.Row(cb.param.period, rollover, follow, width=400).servable(target='controls')
46 |
--------------------------------------------------------------------------------
/panel_streaming/pyscript.toml:
--------------------------------------------------------------------------------
1 | name = "Streaming in Panel"
2 | description = "A simple Panel application showing how to handle streaming data."
3 | packages = ["bokeh==3.2.2", "numpy", "pandas", "panel==1.3.8"]
4 |
--------------------------------------------------------------------------------
/panel_with_hvplot/README.md:
--------------------------------------------------------------------------------
1 | # Panel and hvPlot Example
2 |
3 | This application provides an example of how to use [hvPlot](https://hvplot.holoviz.org/) and [Panel](https://panel.holoviz.org/) to create an interactive data visualization application.
4 |
5 | ## Libraries Used
6 |
7 | - [Panel](https://panel.holoviz.org/)
8 | - [hvPlot](https://hvplot.holoviz.org/)
9 | - [Pandas](https://pandas.pydata.org/)
10 | - [Numpy](https://numpy.org/)
11 |
--------------------------------------------------------------------------------
/panel_with_hvplot/assets/css/examples.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | .pyscript {
6 | margin: 0.5rem;
7 | }
8 |
9 | html {
10 | font-family:
11 | ui-sans-serif,
12 | system-ui,
13 | -apple-system,
14 | BlinkMacSystemFont,
15 | "Segoe UI",
16 | Roboto,
17 | "Helvetica Neue",
18 | Arial,
19 | "Noto Sans",
20 | sans-serif,
21 | "Apple Color Emoji",
22 | "Segoe UI Emoji",
23 | "Segoe UI Symbol",
24 | "Noto Color Emoji";
25 | line-height: 1.5;
26 | }
27 |
28 | nav {
29 | position: sticky;
30 | width: 100%;
31 | top: 0;
32 | left: 0;
33 | z-index: 9999;
34 | }
35 |
36 | .logo {
37 | padding-right: 10px;
38 | font-size: 28px;
39 | height: 30px;
40 | max-width: inherit;
41 | }
42 |
43 | .title {
44 | text-decoration: none;
45 | text-decoration-line: none;
46 | text-decoration-style: initial;
47 | text-decoration-color: initial;
48 | font-weight: 400;
49 | font-size: 1.5em;
50 | line-height: 2em;
51 | white-space: nowrap;
52 | }
53 |
54 | .app-header {
55 | display: flex;
56 | align-items: center;
57 | padding: 0.5rem 1rem;
58 | }
59 |
--------------------------------------------------------------------------------
/panel_with_hvplot/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/panel_with_hvplot/assets/favicon.png
--------------------------------------------------------------------------------
/panel_with_hvplot/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/panel_with_hvplot/assets/logo.png
--------------------------------------------------------------------------------
/panel_with_hvplot/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
26 |
27 | Panel with hvPlot
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
56 |
57 |
65 |
66 |
67 |
68 |
69 | Build an app with hvPlot and Panel
70 |
71 |
72 |
73 | Leveraging hvPlot's interactive feature
74 | and extending it by adding an outliers count indicator.
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/panel_with_hvplot/main.py:
--------------------------------------------------------------------------------
1 | import hvplot.pandas
2 | import numpy as np
3 | from js import console
4 | from pyodide_http import patch_all
5 |
6 | import pandas as pd
7 | import panel as pn
8 |
9 | patch_all()
10 |
11 | pn.extension(design="material")
12 |
13 | csv_file = (
14 | "https://raw.githubusercontent.com/holoviz/panel/main/examples/assets/occupancy.csv"
15 | )
16 | data = pd.read_csv(csv_file, parse_dates=["date"], index_col="date")
17 | console.log("Downloaded data")
18 |
19 | # Panel Widgets
20 | variable_widget = pn.widgets.Select(
21 | name="variable", value="Temperature", options=list(data.columns)
22 | )
23 | window_widget = pn.widgets.IntSlider(name="window", value=30, start=1, end=60)
24 | sigma_widget = pn.widgets.IntSlider(name="sigma", value=10, start=0, end=20)
25 | console.log("Set up widgets!")
26 |
27 | # Interactive hvplot pipeline
28 | ## Compute the outliers
29 | data = data.interactive()
30 | avg = data[variable_widget].rolling(window=window_widget).mean()
31 | residual = data[variable_widget] - avg
32 | std = residual.rolling(window=window_widget).std()
33 | outliers = np.abs(residual) > std * sigma_widget
34 |
35 | ## Plot the average variable line together with the outliers as points
36 | pipeline = avg.hvplot(height=300, width=400, color="blue", legend=False) * avg[
37 | outliers
38 | ].hvplot.scatter(color="orange", padding=0.1, legend=False)
39 |
40 | # Compute the number of outliers
41 | count = outliers.pipe(
42 | lambda s: pn.indicators.Number(
43 | name="Outliers count",
44 | value=s.sum(),
45 | colors=[(10, "green"), (30, "gold"), (np.Inf, "red")],
46 | )
47 | )
48 |
49 | # Servable App
50 | pn.Column(pipeline.widgets(), pn.Row(count.output(), pipeline.output())).servable(
51 | target="panel"
52 | )
53 |
--------------------------------------------------------------------------------
/panel_with_hvplot/pyscript.toml:
--------------------------------------------------------------------------------
1 | name = "Panel and hvPlot"
2 | description = "Leverage hvPlot interactive feature and extending it with Panel."
3 | packages = ["bokeh==3.2.2", "panel==1.3.8", "markdown-it-py", "numpy", "pandas", "hvplot", "pyodide-http"]
4 |
--------------------------------------------------------------------------------
/py-jokes/README.md:
--------------------------------------------------------------------------------
1 | # Pyjokes Example
2 |
3 | This application provides an example of how to install the package `Pyjokes` and then use it in a PyScript application.
4 |
5 | Look at the `pyscript.toml` file and see how we are specifying the `pyjokes` package as a dependency and then using it in the `main.py` file.
6 |
7 |
8 | ## Libraries Used
9 |
10 | - [Pyjokes](https://pyjok.es/)
11 |
--------------------------------------------------------------------------------
/py-jokes/assets/css/examples.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | .pyscript {
6 | margin: 0.5rem;
7 | }
8 |
9 | html {
10 | font-family:
11 | ui-sans-serif,
12 | system-ui,
13 | -apple-system,
14 | BlinkMacSystemFont,
15 | "Segoe UI",
16 | Roboto,
17 | "Helvetica Neue",
18 | Arial,
19 | "Noto Sans",
20 | sans-serif,
21 | "Apple Color Emoji",
22 | "Segoe UI Emoji",
23 | "Segoe UI Symbol",
24 | "Noto Color Emoji";
25 | line-height: 1.5;
26 | }
27 |
28 | nav {
29 | position: sticky;
30 | width: 100%;
31 | top: 0;
32 | left: 0;
33 | z-index: 9999;
34 | }
35 |
36 | .logo {
37 | padding-right: 10px;
38 | font-size: 28px;
39 | height: 30px;
40 | max-width: inherit;
41 | }
42 |
43 | .title {
44 | text-decoration: none;
45 | text-decoration-line: none;
46 | text-decoration-style: initial;
47 | text-decoration-color: initial;
48 | font-weight: 400;
49 | font-size: 1.5em;
50 | line-height: 2em;
51 | white-space: nowrap;
52 | }
53 |
54 | .app-header {
55 | display: flex;
56 | align-items: center;
57 | padding: 0.5rem 1rem;
58 | }
59 |
--------------------------------------------------------------------------------
/py-jokes/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/py-jokes/assets/favicon.png
--------------------------------------------------------------------------------
/py-jokes/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/py-jokes/assets/logo.png
--------------------------------------------------------------------------------
/py-jokes/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
26 |
27 | PyScript Jokes
28 |
29 |
30 |
31 |
44 |
45 |
46 |
47 |
48 |
51 |
52 |
60 |
61 |
62 | <py>jokes
63 | One line jokes for programmers (jokes as a service)
64 |
65 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/py-jokes/main.py:
--------------------------------------------------------------------------------
1 | import pyjokes
2 | from pyweb import pydom
3 |
4 | def get_joke(event):
5 | pydom["div#jokes"].html = f"{pyjokes.get_joke()} 🥁"
6 |
--------------------------------------------------------------------------------
/py-jokes/pyscript.toml:
--------------------------------------------------------------------------------
1 | name = "PyScript Jokes"
2 | description = "A simple application showing how to install and use a package in PyScript."
3 | packages = ["pyjokes"]
4 |
--------------------------------------------------------------------------------
/simple_clock/README.md:
--------------------------------------------------------------------------------
1 | # Simple Clock Example
2 |
3 | This is a basic application showing how you can display the current time in PyScript and how to update the time every second.
4 |
5 | It uses the `async` parameter in the `script` that loads pyscript to allow for the use of `async` functions and `await` calls.
6 |
--------------------------------------------------------------------------------
/simple_clock/assets/css/examples.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | .pyscript {
6 | margin: 0.5rem;
7 | }
8 |
9 | html {
10 | font-family:
11 | ui-sans-serif,
12 | system-ui,
13 | -apple-system,
14 | BlinkMacSystemFont,
15 | "Segoe UI",
16 | Roboto,
17 | "Helvetica Neue",
18 | Arial,
19 | "Noto Sans",
20 | sans-serif,
21 | "Apple Color Emoji",
22 | "Segoe UI Emoji",
23 | "Segoe UI Symbol",
24 | "Noto Color Emoji";
25 | line-height: 1.5;
26 | }
27 |
28 | nav {
29 | position: sticky;
30 | width: 100%;
31 | top: 0;
32 | left: 0;
33 | z-index: 9999;
34 | }
35 |
36 | .logo {
37 | padding-right: 10px;
38 | font-size: 28px;
39 | height: 30px;
40 | max-width: inherit;
41 | }
42 |
43 | .title {
44 | text-decoration: none;
45 | text-decoration-line: none;
46 | text-decoration-style: initial;
47 | text-decoration-color: initial;
48 | font-weight: 400;
49 | font-size: 1.5em;
50 | line-height: 2em;
51 | white-space: nowrap;
52 | }
53 |
54 | .app-header {
55 | display: flex;
56 | align-items: center;
57 | padding: 0.5rem 1rem;
58 | }
59 |
--------------------------------------------------------------------------------
/simple_clock/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/simple_clock/assets/favicon.png
--------------------------------------------------------------------------------
/simple_clock/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/simple_clock/assets/logo.png
--------------------------------------------------------------------------------
/simple_clock/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
26 |
27 | Simple Clock Demo
28 |
29 |
30 |
31 |
32 |
35 |
36 |
44 |
45 |
46 |
47 | start time:
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/simple_clock/main.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime
2 | from pyscript import display
3 | import asyncio
4 |
5 | def now():
6 | fmt = "%m/%d/%Y, %H:%M:%S"
7 | return f"{datetime.now():{fmt}}"
8 |
9 | display(now(), target="output1", append=False)
10 |
11 | async def foo():
12 | while True:
13 | await asyncio.sleep(1)
14 | output = now()
15 | display(output, target="output2", append=False)
16 |
17 | if output[-1] in ["0", "4", "8"]:
18 | display("It's espresso time!", target="output3", append=False)
19 | else:
20 | display("", target="output3", append=False)
21 |
22 | await foo()
23 |
--------------------------------------------------------------------------------
/simple_clock/pyscript.toml:
--------------------------------------------------------------------------------
1 | name = "Simple Clock"
2 | description = "A simple application to display the time on the page."
3 |
--------------------------------------------------------------------------------
/tic-tac-toe/README.md:
--------------------------------------------------------------------------------
1 | # Tic-Tac-Toe
2 |
3 | This application provides a full working Tic-Tac-Toe game using PyScript where two players can start a game and play on the same computer.
--------------------------------------------------------------------------------
/tic-tac-toe/assets/css/examples.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | .pyscript {
6 | margin: 0.5rem;
7 | }
8 |
9 | html {
10 | font-family:
11 | ui-sans-serif,
12 | system-ui,
13 | -apple-system,
14 | BlinkMacSystemFont,
15 | "Segoe UI",
16 | Roboto,
17 | "Helvetica Neue",
18 | Arial,
19 | "Noto Sans",
20 | sans-serif,
21 | "Apple Color Emoji",
22 | "Segoe UI Emoji",
23 | "Segoe UI Symbol",
24 | "Noto Color Emoji";
25 | line-height: 1.5;
26 | }
27 |
28 | nav {
29 | position: sticky;
30 | width: 100%;
31 | top: 0;
32 | left: 0;
33 | z-index: 9999;
34 | }
35 |
36 | .logo {
37 | padding-right: 10px;
38 | font-size: 28px;
39 | height: 30px;
40 | max-width: inherit;
41 | }
42 |
43 | .title {
44 | text-decoration: none;
45 | text-decoration-line: none;
46 | text-decoration-style: initial;
47 | text-decoration-color: initial;
48 | font-weight: 400;
49 | font-size: 1.5em;
50 | line-height: 2em;
51 | white-space: nowrap;
52 | }
53 |
54 | .app-header {
55 | display: flex;
56 | align-items: center;
57 | padding: 0.5rem 1rem;
58 | }
59 |
--------------------------------------------------------------------------------
/tic-tac-toe/assets/css/tictactoe.css:
--------------------------------------------------------------------------------
1 | h1, h2 {
2 | font-family: 'Indie Flower', 'Comic Sans', cursive;
3 | text-align: center;
4 | }
5 |
6 | #board {
7 | font-family: 'Indie Flower', 'Comic Sans', cursive;
8 | position: relative;
9 | font-size: 120px;
10 | margin: 1% auto;
11 | border-collapse: collapse;
12 | }
13 | #board td {
14 | border: 4px solid rgb(60, 60, 60);
15 | width: 90px;
16 | height: 90px;
17 | vertical-align: middle;
18 | text-align: center;
19 | cursor: pointer;
20 | }
21 |
22 | #board td div {
23 | width: 90px;
24 | height: 90px;
25 | line-height: 90px;
26 | display: block;
27 | overflow: hidden;
28 | cursor: pointer;
29 | }
30 |
31 | .x {
32 | color: darksalmon;
33 | position: relative;
34 | font-size: 1.2em;
35 | cursor: default;
36 | }
37 | .o {
38 | color: aquamarine;
39 | position: relative;
40 | font-size: 1.0em;
41 | cursor: default;
42 | }
43 |
44 | .win {
45 | background-color: beige;
46 | }
47 |
--------------------------------------------------------------------------------
/tic-tac-toe/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/tic-tac-toe/assets/favicon.png
--------------------------------------------------------------------------------
/tic-tac-toe/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/tic-tac-toe/assets/logo.png
--------------------------------------------------------------------------------
/tic-tac-toe/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
25 |
30 |
31 | Tic Tac Toe
32 |
33 |
34 |
35 |
36 |
39 |
40 |
48 |
49 |
50 | Tic-Tac-Toe
51 |
52 |
53 |
54 |
55 |
56 | |
57 | |
58 | |
59 |
60 | |
61 | |
62 | |
63 |
64 |
65 | |
66 | |
67 | |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/tic-tac-toe/main.py:
--------------------------------------------------------------------------------
1 | from pyweb import pydom
2 |
3 | class TicTacToe:
4 | def __init__(self):
5 | self.board = pydom["table#board"]
6 | self.status = pydom["h2#status"]
7 | self.console = pydom["script#console"][0]
8 | self.init_cells()
9 | self.init_winning_combos()
10 | self.new_game(...)
11 |
12 | def set_status(self, text):
13 | self.status.html = text
14 |
15 | def init_cells(self):
16 | self.cells = []
17 | for i in (0, 1, 2):
18 | row = []
19 | for j in (0, 1, 2):
20 | cell = pydom[f"div#cell{i}{j}"][0]
21 | assert cell
22 | row.append(cell)
23 | self.cells.append(row)
24 |
25 | def init_winning_combos(self):
26 | self.winning_combos = []
27 | # winning columns
28 | for i in (0, 1, 2):
29 | combo = []
30 | for j in (0, 1, 2):
31 | combo.append((i, j))
32 | self.winning_combos.append(combo)
33 |
34 | # winning rows
35 | for j in (0, 1, 2):
36 | combo = []
37 | for i in (0, 1, 2):
38 | combo.append((i, j))
39 | self.winning_combos.append(combo)
40 |
41 | # winning diagonals
42 | self.winning_combos.append([(0, 0), (1, 1), (2, 2)])
43 | self.winning_combos.append([(0, 2), (1, 1), (2, 0)])
44 |
45 | def new_game(self, event):
46 | self.clear_terminal()
47 | print('=================')
48 | print('NEW GAME STARTING')
49 | print()
50 | for i in (0, 1, 2):
51 | for j in (0, 1, 2):
52 | self.set_cell(i, j, "")
53 |
54 | self.current_player = "x"
55 | self.set_status(f'{self.current_player} playing...')
56 |
57 | def next_turn(self):
58 | winner = self.check_winner()
59 | if winner == "tie":
60 | self.set_status("It's a tie!")
61 | self.current_player = "" # i.e., game ended
62 | return
63 | elif winner is not None:
64 | self.set_status(f'{winner} wins')
65 | self.current_player = "" # i.e., game ended
66 | return
67 |
68 | if self.current_player == "x":
69 | self.current_player = "o"
70 | else:
71 | self.current_player = "x"
72 | self.set_status(f'{self.current_player} playing...')
73 |
74 | def check_winner(self):
75 | """
76 | Check whether the game as any winner.
77 |
78 | Return "x", "o", "tie" or None. None means that the game is still playing.
79 | """
80 | # check whether we have a winner
81 | for combo in self.winning_combos:
82 | winner = self.get_winner(combo)
83 | if winner:
84 | # highlight the winning cells
85 | for i, j in combo:
86 | self.cells[i][j].add_class("win")
87 | return winner
88 |
89 | # check whether it's a tie
90 | for i in (0, 1, 2):
91 | for j in (0, 1, 2):
92 | if self.get_cell(i, j) == "":
93 | # there is at least an empty cell, it's not a tie
94 | return None # game still playing
95 | return "tie"
96 |
97 | def get_winner(self, combo):
98 | """
99 | If all the cells at the given points have the same value, return it.
100 | Else return "".
101 |
102 | Each point is a tuple of (i, j) coordinates.
103 | Example:
104 | self.get_winner([(0, 0), (1, 1), (2, 2)])
105 | """
106 | assert len(combo) == 3
107 | values = [self.get_cell(i, j) for i, j in combo]
108 | if values[0] == values[1] == values[2] and values[0] != "":
109 | return values[0]
110 | return ""
111 |
112 | def set_cell(self, i, j, value):
113 | assert value in ("", "x", "o")
114 | cell = self.cells[i][j]
115 | cell.html = value
116 | if "x" in cell.classes:
117 | cell.remove_class("x")
118 | if "o" in cell.classes:
119 | cell.remove_class("o")
120 | if "win" in cell.classes:
121 | cell.remove_class("win")
122 | if value != "":
123 | cell.add_class(value)
124 |
125 | def get_cell(self, i, j):
126 | cell = self.cells[i][j]
127 | value = cell.html
128 | assert value in ("", "x", "o")
129 | return value
130 |
131 | def click(self, event):
132 | i = int(event.target.getAttribute('data-x'))
133 | j = int(event.target.getAttribute('data-y'))
134 | print(f'Cell {i}, {j} clicked: ', end='')
135 | if self.current_player == "":
136 | print('game ended, nothing to do')
137 | return
138 | #
139 | value = self.get_cell(i, j)
140 | if value == "":
141 | print('cell empty, setting it')
142 | self.set_cell(i, j, self.current_player)
143 | self.next_turn()
144 | else:
145 | print(f'cell already full, cannot set it')
146 |
147 | def clear_terminal(self):
148 | self.console._js.terminal.clear()
149 |
150 | def toggle_terminal(self, event):
151 | hidden = self.console.parent._js.getAttribute("hidden")
152 | if hidden:
153 | self.console.parent._js.removeAttribute("hidden")
154 | else:
155 | self.console.parent._js.setAttribute("hidden", "hidden")
156 |
157 | GAME = TicTacToe()
158 |
--------------------------------------------------------------------------------
/tic-tac-toe/pyscript.toml:
--------------------------------------------------------------------------------
1 | name = "Tic Tac Toe"
2 | description = "A Tic-Tac-Toe game written in PyScript that allows to people take turns."
3 |
--------------------------------------------------------------------------------
/todo/README.md:
--------------------------------------------------------------------------------
1 | # TODO Example
2 |
3 | This application provides an example of how to create a simple To-Do list using `PyScript`.
--------------------------------------------------------------------------------
/todo/assets/css/examples.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | }
4 |
5 | .pyscript {
6 | margin: 0.5rem;
7 | }
8 |
9 | html {
10 | font-family:
11 | ui-sans-serif,
12 | system-ui,
13 | -apple-system,
14 | BlinkMacSystemFont,
15 | "Segoe UI",
16 | Roboto,
17 | "Helvetica Neue",
18 | Arial,
19 | "Noto Sans",
20 | sans-serif,
21 | "Apple Color Emoji",
22 | "Segoe UI Emoji",
23 | "Segoe UI Symbol",
24 | "Noto Color Emoji";
25 | line-height: 1.5;
26 | }
27 |
28 | nav {
29 | position: sticky;
30 | width: 100%;
31 | top: 0;
32 | left: 0;
33 | z-index: 9999;
34 | }
35 |
36 | .logo {
37 | padding-right: 10px;
38 | font-size: 28px;
39 | height: 30px;
40 | max-width: inherit;
41 | }
42 |
43 | .title {
44 | text-decoration: none;
45 | text-decoration-line: none;
46 | text-decoration-style: initial;
47 | text-decoration-color: initial;
48 | font-weight: 400;
49 | font-size: 1.5em;
50 | line-height: 2em;
51 | white-space: nowrap;
52 | }
53 |
54 | .app-header {
55 | display: flex;
56 | align-items: center;
57 | padding: 0.5rem 1rem;
58 | }
59 |
--------------------------------------------------------------------------------
/todo/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/todo/assets/favicon.png
--------------------------------------------------------------------------------
/todo/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pyscript/examples/48bd1ae9bb185fedb743938a85a6ce4a8abd8e46/todo/assets/logo.png
--------------------------------------------------------------------------------
/todo/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
21 |
26 |
27 | Todo App
28 |
29 |
30 |
35 |
36 |
37 |
38 |
41 |
42 |
50 |
51 |
52 |
53 |
54 |
55 |
To Do List
56 |
57 |
58 |
59 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/todo/main.py:
--------------------------------------------------------------------------------
1 | from datetime import datetime as dt
2 |
3 | from pyscript import document
4 | from pyweb import pydom
5 |
6 | tasks = []
7 |
8 | def q(selector, root=document):
9 | return root.querySelector(selector)
10 |
11 | # define the task template that will be use to render new templates to the page
12 | # Note: We use JS element here because pydom doesn't fully support template
13 | # elements now
14 | task_template = pydom.Element(q("#task-template").content.querySelector(".task"))
15 |
16 | task_list = pydom["#list-tasks-container"][0]
17 | new_task_content = pydom["#new-task-content"][0]
18 |
19 |
20 | def add_task(e):
21 | # ignore empty task
22 | if not new_task_content.value:
23 | return None
24 |
25 | # create task
26 | task_id = f"task-{len(tasks)}"
27 | task = {
28 | "id": task_id,
29 | "content": new_task_content.value,
30 | "done": False,
31 | "created_at": dt.now(),
32 | }
33 |
34 | tasks.append(task)
35 |
36 | # add the task element to the page as new node in the list by cloning from a
37 | # template
38 | task_html = task_template.clone()
39 | task_html.id = task_id
40 |
41 | task_html_check = task_html.find("input")[0]
42 | task_html_content = task_html.find("p")[0]
43 | task_html_content._js.textContent = task["content"]
44 | task_list.append(task_html)
45 |
46 | def check_task(evt=None):
47 | task["done"] = not task["done"]
48 | task_html_content._js.classList.toggle("line-through", task["done"])
49 |
50 | new_task_content.value = ""
51 | task_html_check._js.onclick = check_task
52 |
53 |
54 | def add_task_event(e):
55 | if e.key == "Enter":
56 | add_task(e)
57 |
58 |
59 | new_task_content.onkeypress = add_task_event
60 |
--------------------------------------------------------------------------------
/todo/pyscript.toml:
--------------------------------------------------------------------------------
1 | name = "Todo App"
2 | description = "A simple To Do application written in PyScript."
3 |
--------------------------------------------------------------------------------