1
0
mirror of https://github.com/pyscript/pyscript.git synced 2022-05-01 19:47:48 +03:00

Merge branch 'main' into rb/prettier

This commit is contained in:
Fabio Pliger
2022-04-25 22:03:37 -05:00
committed by GitHub
7 changed files with 500 additions and 30 deletions

View File

@@ -0,0 +1,60 @@
<html>
<head>
<title>Altair</title>
<meta charset="utf-8">
<link rel="stylesheet" href="../build/pyscript.css" />
<script defer src="../build/pyscript.js"></script>
<py-env>
- altair
- pandas
- vega_datasets
</py-env>
</head>
<body>
<div id="altair" style="width: 100%; height: 100%"></div>
<py-script output="altair">
import altair as alt
from vega_datasets import data
source = data.movies.url
pts = alt.selection(type="single", encodings=['x'])
rect = alt.Chart(data.movies.url).mark_rect().encode(
alt.X('IMDB_Rating:Q', bin=True),
alt.Y('Rotten_Tomatoes_Rating:Q', bin=True),
alt.Color('count()',
scale=alt.Scale(scheme='greenblue'),
legend=alt.Legend(title='Total Records')
)
)
circ = rect.mark_point().encode(
alt.ColorValue('grey'),
alt.Size('count()',
legend=alt.Legend(title='Records in Selection')
)
).transform_filter(
pts
)
bar = alt.Chart(source).mark_bar().encode(
x='Major_Genre:N',
y='count()',
color=alt.condition(pts, alt.ColorValue("steelblue"), alt.ColorValue("grey"))
).properties(
width=550,
height=200
).add_selection(pts)
alt.vconcat(
rect + circ,
bar
).resolve_legend(
color="independent",
size="independent"
)
</py-script>
</body>
</html>

View File

@@ -0,0 +1,72 @@
<html>
<head>
<title>Antigravity</title>
<meta charset="utf-8">
<link rel="stylesheet" href="../build/pyscript.css" />
<script defer src="../build/pyscript.js"></script>
<style>
.loading {
display: inline-block;
width: 50px;
height: 50px;
border: 3px solid rgba(255, 255, 255, 0.3);
border-radius: 50%;
border-top-color: black;
animation: spin 1s ease-in-out infinite;
}
@keyframes spin {
to {
transform: rotate(360deg);
}
}
</style>
</head>
<body>
<b>
Based on xkcd: antigravity https://xkcd.com/353/.
</b>
<div style="display: flex; flex-direction: row">
<div id="antigravity" style="width: 400px; height: 400px">
<div class="loading"></div>
</div>
</div>
<py-script output="antigravity">
import random
from js import document, setInterval
from pyodide import create_proxy
from pyodide.http import open_url
class Antigravity():
def __init__(self, node):
self.node = node
self.xoffset, self.yoffset = 0, 0
setInterval(create_proxy(self.move), 10)
def move(self):
char = document.getElementById('python')
console.log(char)
char.setAttribute('transform', f'translate({self.xoffset}, {-self.yoffset})')
self.xoffset += random.normalvariate(0, 1)/20
if self.yoffset < 50:
self.yoffset += 0.1
else:
self.yoffset += random.normalvariate(0, 1)/20
def _repr_svg_(self):
return open_url('./antigravity.svg').read()
node = document.getElementById('antigravity')
node.replaceChildren()
Antigravity(node)
</py-script>
</body>
</html>

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 212 KiB

View File

@@ -0,0 +1,49 @@
<html>
<head>
<title>Folium</title>
<meta charset="utf-8">
<link rel="stylesheet" href="../build/pyscript.css" />
<script defer src="../build/pyscript.js"></script>
<py-env>
- folium
- pandas
</py-env>
</head>
<body>
<div id="folium" style="width: 100%; height: 100%"></div>
<py-script output="folium">
import folium
import json
import pandas as pd
from pyodide.http import open_url
url = (
"https://raw.githubusercontent.com/python-visualization/folium/master/examples/data"
)
state_geo = f"{url}/us-states.json"
state_unemployment = f"{url}/US_Unemployment_Oct2012.csv"
state_data = pd.read_csv(open_url(state_unemployment))
geo_json = json.loads(open_url(state_geo).read())
m = folium.Map(location=[48, -102], zoom_start=3)
folium.Choropleth(
geo_data=geo_json,
name="choropleth",
data=state_data,
columns=["State", "Unemployment"],
key_on="feature.id",
fill_color="YlGn",
fill_opacity=0.7,
line_opacity=0.2,
legend_name="Unemployment Rate (%)",
).add_to(m)
folium.LayerControl().add_to(m)
m
</py-script>
</body>
</html>

View File

@@ -0,0 +1,50 @@
<html>
<head>
<title>Matplotlib</title>
<meta charset="utf-8">
<link rel="stylesheet" href="../build/pyscript.css" />
<script defer src="../build/pyscript.js"></script>
<py-env>
- matplotlib
</py-env>
</head>
<body>
<div id="mpl"></div>
<py-script output="mpl">
import matplotlib.pyplot as plt
import matplotlib.tri as tri
import numpy as np
# First create the x and y coordinates of the points.
n_angles = 36
n_radii = 8
min_radius = 0.25
radii = np.linspace(min_radius, 0.95, n_radii)
angles = np.linspace(0, 2 * np.pi, n_angles, endpoint=False)
angles = np.repeat(angles[..., np.newaxis], n_radii, axis=1)
angles[:, 1::2] += np.pi / n_angles
x = (radii * np.cos(angles)).flatten()
y = (radii * np.sin(angles)).flatten()
z = (np.cos(radii) * np.cos(3 * angles)).flatten()
# Create the Triangulation; no triangles so Delaunay triangulation created.
triang = tri.Triangulation(x, y)
# Mask off unwanted triangles.
triang.set_mask(np.hypot(x[triang.triangles].mean(axis=1),
y[triang.triangles].mean(axis=1))
< min_radius)
fig1, ax1 = plt.subplots()
ax1.set_aspect('equal')
tpc = ax1.tripcolor(triang, z, shading='flat')
fig1.colorbar(tpc)
ax1.set_title('tripcolor of Delaunay triangulation, flat shading')
fig1
</py-script>
</body>
</html>

View File

@@ -12,6 +12,95 @@ import io, base64, sys
loop = asyncio.get_event_loop()
MIME_METHODS = {
'__repr__': 'text/plain',
'_repr_html_': 'text/html',
'_repr_markdown_': 'text/markdown',
'_repr_svg_': 'image/svg+xml',
'_repr_png_': 'image/png',
'_repr_pdf_': 'application/pdf',
'_repr_jpeg_': 'image/jpeg',
'_repr_latex': 'text/latex',
'_repr_json_': 'application/json',
'_repr_javascript_': 'application/javascript',
'savefig': 'image/png'
}
def render_image(mime, value, meta):
data = f'data:{mime};charset=utf-8;base64,{value}'
attrs = ' '.join(['{k}="{v}"' for k, v in meta.items()])
return f'<img src="{data}" {attrs}</img>'
def identity(value, meta):
return value
MIME_RENDERERS = {
'text/plain': identity,
'text/html' : identity,
'image/png' : lambda value, meta: render_image('image/png', value, meta),
'image/jpeg': lambda value, meta: render_image('image/jpeg', value, meta),
'image/svg+xml': identity,
'application/json': identity,
'application/javascript': lambda value, meta: f'<script>{value}</script>'
}
def eval_formatter(obj, print_method):
"""
Evaluates a formatter method.
"""
if hasattr(obj, print_method):
if print_method == 'savefig':
buf = io.BytesIO()
obj.savefig(buf, format='png')
buf.seek(0)
return base64.b64encode(buf.read()).decode('utf-8')
return getattr(obj, print_method)()
if print_method == '_repr_mimebundle_':
return {}, {}
return None
def format_mime(obj):
"""
Formats object using _repr_x_ methods.
"""
if isinstance(obj, str):
return obj, 'text/plain'
mimebundle = eval_formatter(obj, '_repr_mimebundle_')
if isinstance(mimebundle, tuple):
format_dict, md_dict = mimebundle
else:
format_dict = mimebundle
md_dict = {}
output, not_available = None, []
for method, mime_type in reversed(MIME_METHODS.items()):
if mime_type in format_dict:
output = format_dict[mime_type]
else:
output = eval_formatter(obj, method)
if output is None:
continue
elif mime_type not in MIME_RENDERERS:
not_available.append(mime_type)
continue
break
if output is None:
if not_available:
console.warning(f'Rendered object requested unavailable MIME renderers: {not_available}')
output = repr(output)
mime_type = 'text/plain'
elif isinstance(output, tuple):
output, meta = output
else:
meta = {}
return MIME_RENDERERS[mime_type](output, meta), mime_type
class PyScript:
loop = loop
@@ -28,17 +117,13 @@ class PyScript:
element_id = child.id = f"{element_id}-{exec_id}";
element.appendChild(child);
if hasattr(value, "savefig"):
console.log(f"FIGURE: {value}")
buf = io.BytesIO()
value.savefig(buf, format='png')
buf.seek(0)
img_str = 'data:image/png;base64,' + base64.b64encode(buf.read()).decode('UTF-8')
document.getElementById(element_id).innerHTML = f'<div><img id="plt" src="{img_str}"/></div>'
elif hasattr(value, "startswith") and value.startswith("data:image"):
document.getElementById(element_id).innerHTML = f'<div><img id="plt" src="{value}"/></div>'
element = document.getElementById(element_id)
html, mime_type = format_mime(value)
if mime_type in ('application/javascript', 'text/html'):
scriptEl = document.createRange().createContextualFragment(html)
element.appendChild(scriptEl)
else:
document.getElementById(element_id).innerHTML = value;
element.innerHTML = html
@staticmethod
def run_until_complete(f):
@@ -104,10 +189,9 @@ class Element:
# Inject it into the DOM
self.element.after(clone);
return Element(clone.id, clone)
def remove_class(self, classname):
if isinstance(classname, list):
for cl in classname:
@@ -144,7 +228,7 @@ class PyItemTemplate(Element):
def __init__(self, data, labels=None, state_key=None, parent=None):
self.data = data
self.register_parent(parent)
if not labels:
@@ -167,7 +251,7 @@ class PyItemTemplate(Element):
console.log('creating section')
new_child = create('section', self._id, "task bg-white my-1")
console.log('creating values')
console.log('creating innerHtml')
new_child._element.innerHTML = f"""
<label for="flex items-center p-2 ">
@@ -275,7 +359,6 @@ class PyListTemplate:
"""Overwrite me to define logic"""
pass
class OutputCtxManager:
def __init__(self, out=None, output_to_console=True, append=True):

View File

@@ -1,9 +1,99 @@
from js import document, setInterval, console
import micropip
import asyncio
import io, base64
import io, base64, sys
loop = asyncio.get_event_loop()
MIME_METHODS = {
'__repr__': 'text/plain',
'_repr_html_': 'text/html',
'_repr_markdown_': 'text/markdown',
'_repr_svg_': 'image/svg+xml',
'_repr_png_': 'image/png',
'_repr_pdf_': 'application/pdf',
'_repr_jpeg_': 'image/jpeg',
'_repr_latex': 'text/latex',
'_repr_json_': 'application/json',
'_repr_javascript_': 'application/javascript',
'savefig': 'image/png'
}
def render_image(mime, value, meta):
data = f'data:{mime};charset=utf-8;base64,{value}'
attrs = ' '.join(['{k}="{v}"' for k, v in meta.items()])
return f'<img src="{data}" {attrs}</img>'
def identity(value, meta):
return value
MIME_RENDERERS = {
'text/plain': identity,
'text/html' : identity,
'image/png' : lambda value, meta: render_image('image/png', value, meta),
'image/jpeg': lambda value, meta: render_image('image/jpeg', value, meta),
'image/svg+xml': identity,
'application/json': identity,
'application/javascript': lambda value, meta: f'<script>{value}</script>'
}
def eval_formatter(obj, print_method):
"""
Evaluates a formatter method.
"""
if hasattr(obj, print_method):
if print_method == 'savefig':
buf = io.BytesIO()
obj.savefig(buf, format='png')
buf.seek(0)
return base64.b64encode(buf.read()).decode('utf-8')
return getattr(obj, print_method)()
if print_method == '_repr_mimebundle_':
return {}, {}
return None
def format_mime(obj):
"""
Formats object using _repr_x_ methods.
"""
if isinstance(obj, str):
return obj, 'text/plain'
mimebundle = eval_formatter(obj, '_repr_mimebundle_')
if isinstance(mimebundle, tuple):
format_dict, md_dict = mimebundle
else:
format_dict = mimebundle
md_dict = {}
output, not_available = None, []
for method, mime_type in reversed(MIME_METHODS.items()):
if mime_type in format_dict:
output = format_dict[mime_type]
else:
output = eval_formatter(obj, method)
if output is None:
continue
elif mime_type not in MIME_RENDERERS:
not_available.append(mime_type)
continue
break
if output is None:
if not_available:
console.warning(f'Rendered object requested unavailable MIME renderers: {not_available}')
output = repr(output)
mime_type = 'text/plain'
elif isinstance(output, tuple):
output, meta = output
else:
meta = {}
return MIME_RENDERERS[mime_type](output, meta), mime_type
class PyScript:
loop = loop
@@ -14,23 +104,19 @@ class PyScript:
if append:
child = document.createElement('div');
element = document.querySelector(f'#{element_id}');
if not element:
return
exec_id = exec_id or element.childElementCount + 1
element_id = child.id = f"{element_id}-{exec_id}";
element.appendChild(child);
if hasattr(value, "savefig"):
console.log(f"FIGURE: {value}")
buf = io.BytesIO()
value.savefig(buf, format='png')
buf.seek(0)
img_str = 'data:image/png;base64,' + base64.b64encode(buf.read()).decode('UTF-8')
document.getElementById(element_id).innerHTML = f'<div><img id="plt" src="{img_str}"/></div>'
elif hasattr(value, "startswith") and value.startswith("data:image"):
console.log(f"DATA/IMAGE: {value}")
document.getElementById(element_id).innerHTML = f'<div><img id="plt" src="{value}"/></div>'
element = document.getElementById(element_id)
html, mime_type = format_mime(value)
if mime_type in ('application/javascript', 'text/html'):
scriptEl = document.createRange().createContextualFragment(html)
element.appendChild(scriptEl)
else:
document.getElementById(element_id).innerHTML = repr(value);
console.log(f"ELSE: {append} ==> {element_id} --> {value}")
element.innerHTML = html
@staticmethod
def run_until_complete(f):
@@ -86,5 +172,3 @@ class Element:
self.element.after(clone);
return Element(clone.id, clone)
pyscript = PyScript()