Hom*_*lli 6 python matplotlib emscripten webassembly pyodide
我最近遇到了Pyodide项目。
我已经使用Pyodide构建了一个小样,但是尽管我花了很多时间来查看源代码,但是对我来说,如何重定向printpython的输出(除了修改CPython源代码之外)还不是很明显(而且)。,如何将输出从matplotlib.pyplot重定向到浏览器。
从源代码来看,FigureCanvasWasm确实有一个show()带有适当后端的方法可以绘制到浏览器画布上-但是,我不清楚如何实例化此类并调用它的show()方法,或者是否还有另一种更明显的重定向方式画到画布上。
因此,我的问题是:
print()消息这是我的测试页:
<!doctype html>
<meta charset="utf-8">
<html lang="en">
<html>
<head>
<title>Demo</title>
<script src="../../pyodide/build/pyodide.js"></script>
</head>
<body>
</body>
<script type="text/javascript">
languagePluginLoader.then(() => {
pyodide.loadPackage(['matplotlib']).then(() => {
pyodide.runPython(`
import matplotlib.pyplot as plt
plt.plot([1, 2, 3, 4])
plt.ylabel('some numbers')
#fig = plt.gcf()
#fig.savefig(imgdata, format='png')
print('Done from python!')`
);
//var image = pyodide.pyimport('imgdata');
//console.log(image);
});});
</script>
<html>
Run Code Online (Sandbox Code Playgroud)
Imp*_*est 12
首先让我们看看我们是否可以在浏览器中显示任何内容;例如一个普通的字符串。Python 变量存储在pyodide.globals属性中。因此,我们可以从那里获取 python 对象并将其放入<div>页面上的元素中。
<!doctype html>
<meta charset="utf-8">
<html>
<head>
<title>Demo</title>
<script src="../pyodide/pyodide.js"></script>
</head>
<body>
</body>
<script type="text/javascript">
languagePluginLoader.then(() => {
pyodide.runPython(`my_string = "This is a python string." `);
document.getElementById("textfield").innerText = pyodide.globals.my_string;
});
</script>
<div id="textfield"></div>
<html>
Run Code Online (Sandbox Code Playgroud)
现在我想我们可以用 matplotlib 图形做同样的事情。下面将显示文档中保存的 png 图像。
<!doctype html>
<meta charset="utf-8">
<html lang="en">
<html>
<head>
<title>Demo</title>
<script src="../pyodide/pyodide.js"></script>
</head>
<body>
</body>
<script type="text/javascript">
languagePluginLoader.then(() => {
pyodide.loadPackage(['matplotlib']).then(() => {
pyodide.runPython(`
import matplotlib.pyplot as plt
import io, base64
fig, ax = plt.subplots()
ax.plot([1,3,2])
buf = io.BytesIO()
fig.savefig(buf, format='png')
buf.seek(0)
img_str = 'data:image/png;base64,' + base64.b64encode(buf.read()).decode('UTF-8')`
);
document.getElementById("pyplotfigure").src=pyodide.globals.img_str
});});
</script>
<div id="textfield">A matplotlib figure:</div>
<div id="pyplotdiv"><img id="pyplotfigure"/></div>
<html>
Run Code Online (Sandbox Code Playgroud)
我还没有研究过backends.wasm_backend,因此可以采用更自动化的上述方式。
使用 wasm 后端时,图形的 canvas 属性是FigureCanvasWasm 的一个实例。调用show()画布的方法应该足以在浏览器中显示图形。不幸的create_root_element()是,画布方法中的一个小错误阻止了图形的显示。此方法创建一个div将包含图形的元素。它首先尝试创建一个 iodide 输出 div 元素。如果失败,则会创建一个纯 HTML div 元素。然而,这个元素永远不会附加到文档中,因此保持不可见。
下面是代码行FigureCanvasWasm是它发生
def create_root_element(self):
# Designed to be overridden by subclasses for use in contexts other
# than iodide.
try:
from js import iodide
return iodide.output.element('div')
except ImportError:
return document.createElement('div')
Run Code Online (Sandbox Code Playgroud)
该注释表明非碘化物代码是一个需要通过覆盖该方法来扩展的存根。这将需要继承 FigureCanvasWasm,将其安装为 pyodide 模块并配置 matplotlib 以使用该后端。
但是,有一个快捷方式,因为根据问题394770,python 允许覆盖实例的方法,而无需修改类。将以下代码放在您的 HTML 文档中会在浏览器中显示一个图形
import numpy as np
from matplotlib import pyplot as plt
from js import document
x = np.linspace(0, 2*np.pi, 100)
y = np.sin(x)
f = plt.figure()
plt.plot(x,y)
# ordinary function to create a div
def create_root_element1(self):
div = document.createElement('div')
document.body.appendChild(div)
return div
#ordinary function to find an existing div
#you'll need to put a div with appropriate id somewhere in the document
def create_root_element2(self):
return document.getElementById('figure1')
#override create_root_element method of canvas by one of the functions above
f.canvas.create_root_element = create_root_element.__get__(
create_root_element1, f.canvas.__class__)
f.canvas.show()
Run Code Online (Sandbox Code Playgroud)
最初工具栏没有显示图标。我必须下载、解压缩并安装fontawesome和 pyodide 并在标题中包含以下行以获取这些
<link rel="stylesheet" href="font-awesome-4.7.0/css/font-awesome.min.css">
Run Code Online (Sandbox Code Playgroud)
编辑:关于你的问题的第一部分,将输出流重定向到浏览器,你可以看看它是如何在 pyodide 的console.html 中完成的。
它将 sys.stdout 替换为 StringIO 对象
pyodide.runPython(`
import sys
import io
sys.stdout = io.StringIO()
`);
Run Code Online (Sandbox Code Playgroud)
然后运行 python 代码(可以完全忽略它在 wasm 上下文中运行的事实)
pyodide.runPython(`
print("Hello, world!")
`);
Run Code Online (Sandbox Code Playgroud)
最后,将 stdout 缓冲区的内容发送到输出元素
var stdout = pyodide.runPython("sys.stdout.getvalue()")
var div = document.createElement('div');
div.innerText = stdout;
document.body.appendChild(div);
Run Code Online (Sandbox Code Playgroud)
要显示 pyodide 中的 print() 调用,您可以使用 loadPyodide 上的参数来重定向 stdout:
var paragraph = document.getElementById("p");
pyodide = await loadPyodide({
indexURL : "https://cdn.jsdelivr.net/pyodide/v0.18.1/full/",
stdin: window.prompt,
stdout: (text) => {paragraph.textContent += text;},
stderr: (text) => {paragraph.textContent += text;}
});
Run Code Online (Sandbox Code Playgroud)
https://github.com/pyodide/pyodide/blob/main/src/js/pyodide.js