matplotlib-pyodide icon indicating copy to clipboard operation
matplotlib-pyodide copied to clipboard

plotting fails with `document.pyodideMplTarget` set

Open scbarton opened this issue 1 year ago • 9 comments

I attempted to use document.pyodideMplTarget to set a target element for a plot, and obtained this error message:

  File "<exec>", line 59, in tplot
  File "/lib/python3.11/site-packages/matplotlib/pyplot.py", line 389, in show
    return _get_backend_mod().show(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/lib/python3.11/site-packages/matplotlib_pyodide/html5_canvas_backend.py", line 450, in show
    plt.gcf().canvas.show()
  File "/lib/python3.11/site-packages/matplotlib_pyodide/browser_backend.py", line 117, in show
    div = self._create_root_element()
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/lib/python3.11/site-packages/matplotlib_pyodide/browser_backend.py", line 317, in _create_root_element
    mpl_target.appendChild(div)
    ^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'appendChild'
    M https://cdn.jsdelivr.net/pyodide/v0.25.0/full/pyodide.asm.js:9
    new_error https://cdn.jsdelivr.net/pyodide/v0.25.0/full/pyodide.asm.js:9
    _pythonexc2js https://cdn.jsdelivr.net/pyodide/v0.25.0/full/pyodide.asm.js:9
    callPyObjectKwargs https://cdn.jsdelivr.net/pyodide/v0.25.0/full/pyodide.asm.js:9
    callPyObject https://cdn.jsdelivr.net/pyodide/v0.25.0/full/pyodide.asm.js:9
    apply https://cdn.jsdelivr.net/pyodide/v0.25.0/full/pyodide.asm.js:9
    apply https://cdn.jsdelivr.net/pyodide/v0.25.0/full/pyodide.asm.js:9
    replot http://localhost:8000/lumped_capacitance.html:98
    async* http://localhost:8000/lumped_capacitance.html:103
    EventListener.handleEvent* http://localhost:8000/lumped_capacitance.html:102

I've attached my html code for this error. For now, I'm just using the default append behavior. lumped_capacitance.html.txt

scbarton avatar Feb 11 '24 14:02 scbarton

Ok, I fixed the plotting fail by waiting for DOMContentLoaded before setting document.pyodideMplTarget.

Now, plotting works but the target div is not reused and I still get the default append behavior. html attached.

lumped_capacitance.html.txt

scbarton avatar Feb 11 '24 15:02 scbarton

Now, plotting works but the target div is not reused and I still get the default append behavior. html attached.

The target div is used, but what happens is that it does not clean up the old plot when showing a new plot. There is an open issue about this behavior (https://github.com/pyodide/matplotlib-pyodide/issues/49).

Due to the lack of manpower, this repository is not actively maintained, so I would appreciate it if you could contribute to this. I think @yu0A is working on this issue too.

ryanking13 avatar Feb 15 '24 09:02 ryanking13

FigureCanvasBase and child FigureCanvas of Matplotlib cannot be reused. It is like, you should call pyplot.close() method or manually close picture viewer generated by Python if you call pyplot.show() method on Windows env. So my way is manually call pyplot.close() method to destroy old FigureCanvas (divs).

yu0A avatar Feb 15 '24 11:02 yu0A

FigureCanvasBase and child FigureCanvas of Matplotlib cannot be reused. It is like, you should call pyplot.close() method or manually close picture viewer generated by Python if you call pyplot.show() method on Windows env. So my way is manually call pyplot.close() method to destroy old FigureCanvas (divs).

When I call pyplot.close() method, due to FigureCanvas should realize destroy() method, I'm working on this method.

yu0A avatar Feb 15 '24 11:02 yu0A

Have you ever got to rendering plot in a custom div?

This is what I got and I always see the div attached to the end:

<!DOCTYPE html>
<html>

<head>
  <script src="https://cdn.jsdelivr.net/pyodide/v0.23.2/full/pyodide.js"></script>
</head>

<body>
  <div id="app">
    <canvas id="target" width="400" height="400"></canvas>
  </div>

  <script src="./index.js"></script>
</body>

</html>

And js:

async function main() {
  let pyodide = await loadPyodide();
  document.pyodideMplTarget = document.getElementById("target");

  await pyodide.loadPackage("matplotlib");

  pyodide.runPython(`
import matplotlib
matplotlib.use("module://matplotlib_pyodide.html5_canvas_backend")
import matplotlib.pyplot as plt
import numpy as np
# matplotlib.use("module://matplotlib.backends.html5_canvas_backend")
plt.plot([1, 2, 3, 4])
plt.ylabel('some numbers')
plt.show()
      `);
}
main();

The wasm backend never works

tomitrescak avatar Feb 27 '24 12:02 tomitrescak

Have you ever got to rendering plot in a custom div? The new divs really generates as children of div id="target". you can find the div id="target" in my code.

yu0A avatar Feb 28 '24 08:02 yu0A

This is what I got and I always see the div attached to the end:

That's the problem. The "module://matplotlib_pyodide.html5_canvas_backend" only generates divs in <div></div> tags. Try to change your html to <div id="target" style="width:400px; height:400px;"></div>

yu0A avatar Feb 28 '24 08:02 yu0A

Hello, no love for me. I did change the source as you described but still getting the result ina separate div:

image

tomitrescak avatar Mar 04 '24 10:03 tomitrescak

Hello, no love for me. I did change the source as you described but still getting the result ina separate div:

I give my development html project to you. In my project the divs can always generate under the target div, and supports manually show and close figure. This project is for you image

yu0A avatar Mar 05 '24 02:03 yu0A