├── .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 | 33 |

Loading...

34 |
35 | 36 | 44 | 45 |
46 |
47 | 48 |
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 | 33 |

Loading...

34 |
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 | 43 |

Loading...

44 |
45 | 46 | 54 | 55 |
56 |
57 | 58 |
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 | 53 |

Loading...

54 |
55 | 56 | 64 | 65 |
66 | 67 | Based on 68 | Learn D3: Shapes 69 | 70 | tutorial. 71 | 72 |
73 |
74 |
JavaScript version
75 |
76 |
77 |
78 |
79 |
80 |
PyScript version
81 |
82 |
83 |
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 | 33 |

Loading...

34 |
35 | 36 | 44 | 45 |
46 |
47 | 48 |
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 | 57 |

Loading...

58 |
59 | 60 | 68 | 69 |
70 |
71 | 72 |
73 |
Mandelbrot set
74 |
75 |
76 | 77 |
78 |
79 | 80 |
81 |
Julia set
82 |
83 |
84 | 85 |
86 |
87 | 88 |
89 |

Play with the below parameters to re-render the Newton Set

90 |
Newton set
91 |
92 |
93 | p(z) = 94 | 95 |
96 |
97 | a = 98 | 99 |
100 |
101 | x = [ 102 | 103 | 104 | , 105 | 106 | 107 | ] 108 |
109 |
110 | y = [ 111 | 112 | 113 | , 114 | 115 | 116 | ] 117 |
118 |
119 |
120 | 121 | convergence 122 |
123 |
124 | 125 | iterations 126 |
127 |
128 |
129 |
130 |
131 | 132 |
133 | 134 |
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 | 33 |

Loading...

34 |
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 | 36 |

Loading...

37 |
38 | 39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
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 | 33 |

Loading...

34 |
35 | 36 | 44 | 45 |
46 |
47 | 48 |
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 | 34 |

Loading...

35 |
36 | 37 | 47 | 48 |
49 | 50 | 51 |
52 | 53 |
54 |

Data Source

55 | 56 | 64 |
65 | 66 | 70 | 71 | 75 | 76 |
77 | 78 |
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 | 44 |

Loading...

45 |
46 | 47 | 55 | 56 |
57 |
58 | 59 |
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 | 65 |

Loading...

66 |
67 | 68 | 76 | 77 |
78 |
79 | 84 |
85 |
86 |
87 |
88 | 89 |
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 | 70 |

Loading...

71 |
72 | 73 | 81 | 82 |
83 |
84 | 91 |
92 |
93 |
94 |
95 |
96 |
97 | 98 | 99 |
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 | 58 |

Loading...

59 |
60 | 61 | 69 | 70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | 81 | 82 |
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 | 54 |

Loading...

55 |
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 | 49 |

Loading...

50 |
51 | 52 | 60 | 61 |
62 |

<py>jokes

63 |

One line jokes for programmers (jokes as a service)

64 | 65 |
66 | 67 | PyJokes.es 68 | 69 | 70 |
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 | 33 |

Loading...

34 |
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 | 37 |

Loading...

38 |
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 | 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 | 39 |

Loading...

40 |
41 | 42 | 50 | 51 |
52 |
53 |
54 |
55 |

To Do List

56 |
57 |
58 | 59 | 62 |
63 | 64 |
65 | 66 | 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 | --------------------------------------------------------------------------------