Skip to content

Commit 9936d85

Browse files
committed
Render Python Plotly using JSON MIME bundle
Fixes #79.
1 parent 1f3e4f7 commit 9936d85

File tree

4 files changed

+58
-31
lines changed

4 files changed

+58
-31
lines changed

_extensions/live/live.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -529,6 +529,9 @@ function setupPyodide(doc)
529529
-- Initial Pyodide startup options
530530
local pyodide_options = {
531531
indexURL = "https://cdn.jsdelivr.net/pyodide/v0.27.0/full/",
532+
env = {
533+
PLOTLY_RENDERER = 'plotly_mimetype',
534+
}
532535
}
533536
if (pyodide["engine-url"]) then
534537
pyodide_options["indexURL"] = pandoc.utils.stringify(pyodide["engine-url"])

_extensions/live/resources/live-runtime.js

Lines changed: 29 additions & 26 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/interactive/dashboards.qmd

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ def solve_lorenz(N=10, max_time=4.0, sigma=10.0, beta=8./3, rho=28.0):
7373
return x_t
7474
7575
x_t = solve_lorenz(N, sigma = sigma, beta = beta, rho = rho)
76-
fig = go.FigureWidget()
76+
fig = go.Figure()
7777
for i in range(N):
7878
fig.add_trace(
7979
go.Scatter3d(
@@ -86,10 +86,8 @@ for i in range(N):
8686
)
8787
)
8888
fig.update_layout(
89-
autosize=False,
9089
scene_camera_eye=dict(x=1.5, y=-1.5, z=1.5),
9190
width=800,
92-
height=800,
9391
margin=dict(l=0, r=0, b=0, t=0)
9492
)
9593
fig

live-runtime/src/evaluate-pyodide.ts

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,15 @@ import { loadScriptAsync, replaceScriptChildren } from './utils';
1414

1515
declare global {
1616
interface Window {
17+
Plotly: any;
1718
_ojs: {
1819
ojsConnector: any;
1920
}
2021
}
2122
}
2223

2324
let stateElement: HTMLScriptElement | undefined;
25+
let plotly: boolean = false;
2426

2527
export class PyodideEvaluator implements ExerciseEvaluator {
2628
container: OJSEvaluateElement;
@@ -186,7 +188,7 @@ export class PyodideEvaluator implements ExerciseEvaluator {
186188

187189
const resultObject = await this.pyodide.runPythonAsync(
188190
atob(require('./scripts/Python/capture.py'))
189-
, { locals });
191+
, { locals });
190192
locals.destroy();
191193

192194
const value = await resultObject.get("value");
@@ -294,6 +296,24 @@ export class PyodideEvaluator implements ExerciseEvaluator {
294296
dispatchEvent(new Event('load'));
295297
}
296298

299+
const appendPlotlyFigure = async (figure: PyProxy) => {
300+
const locals = await this.pyodide.toPy({ figure });
301+
const figureJson = await this.pyodide.runPythonAsync(`
302+
import json
303+
json.dumps(figure)
304+
`, { locals });
305+
306+
if (!plotly) {
307+
await loadScriptAsync("https://cdn.plot.ly/plotly-2.35.2.min.js");
308+
plotly = true;
309+
}
310+
var figureObj = JSON.parse(figureJson);
311+
312+
const figureDiv = document.createElement('div');
313+
window.Plotly.newPlot(figureDiv, figureObj.data, figureObj.layout);
314+
container.appendChild(figureDiv);
315+
}
316+
297317
const appendHtml = async (html: string) => {
298318
if (options.output) {
299319
const outputDiv = document.createElement("div");
@@ -342,11 +362,12 @@ export class PyodideEvaluator implements ExerciseEvaluator {
342362
container.appendChild(errorDiv);
343363
}
344364

345-
for(let i = 0; i < await result.outputs.length; i++) {
365+
for (let i = 0; i < await result.outputs.length; i++) {
346366
const item = await result.outputs.get(i);
347367
const imagebitmap = await item._repr_mime_("application/html-imagebitmap");
348368
const html = await item._repr_mime_("text/html");
349369
const widget = await item._repr_mime_("application/vnd.jupyter.widget-view+json");
370+
const plotly = await item._repr_mime_("application/vnd.plotly.v1+json");
350371
const plain = await item._repr_mime_("text/plain");
351372
const png = await item._repr_mime_("image/png");
352373
const jpeg = await item._repr_mime_("image/jpeg");
@@ -358,6 +379,8 @@ export class PyodideEvaluator implements ExerciseEvaluator {
358379
appendHtml(html);
359380
} else if (widget) {
360381
appendJupyterWidget(widget);
382+
} else if (plotly) {
383+
appendPlotlyFigure(plotly);
361384
} else if (png) {
362385
appendDataUrlImage("image/png", png);
363386
} else if (jpeg) {

0 commit comments

Comments
 (0)