Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

a better method for configuring where matplotlib figures are added to the DOM #19

Open
kmdupr33 opened this issue Oct 5, 2022 · 3 comments

Comments

@kmdupr33
Copy link
Contributor

kmdupr33 commented Oct 5, 2022

Currently, matplotlib figures are placed in an element returned by FigureCanvasWasm.create_root_element. Our options for modifying this behavior are limited, and I'd like to propose something better.

Current options

@personalizedrefrigerator, proposed here that we can build pyodide ourselves and subclass FigureCanvasWasm to get the behavior we want.

@bgailleton demonstrated a monkey-patch-based approach here where he modifies create_root_element on a Canvas instance. (@gzuidhof does something similar here in starboard-notebook)

A better approach?

FigureCanvasWasm.create_root_element can return js.document.pyodideMatplotlibPlotTarget if it refers to a DOM element and fallback on its default behavior of creating an unattached div if that element isn't present. The code would look something like this:

def create_root_element(self):
  if document.pyodideMatplotlibPlotTarget:
    return document.pyodideMatplotlibPlotTarget
  else:
    return document.createElement("div")

With this in place, clients could control where plots are rendered simply by populating document.pyodideMatplotlibPlotTarget. I think this is much easier than doing a custom build of pyodide and much cleaner than monkey-patching instances. If we did this, we also wouldn't need to have subclasses override the method. They could control where plots are rendered in the same way that external clients do. I'm happy to contribute a PR if we think this is a good approach.

@ryanking13
Copy link
Member

Thanks for opening the discussion! I 100% agree that we need some more flexibility on where to place figures.

I'm happy to contribute a PR if we think this is a good approach.

I think it is a good idea. It would need some test and proper documentation of how to use it. Feel free to open a PR, then we can do some more discussion there.

Currently, there are no devs who are actively maintaining matplotlib backend for Pyodide. So I really appreciate if you contribute to this repository.

@kmdupr33
Copy link
Contributor Author

kmdupr33 commented Oct 6, 2022

Happy to contribute a PR. Give me a week or so and I'll have something.

@CesMak
Copy link

CesMak commented Dec 4, 2022

Hey there,
all I want is to use this backend to plot in individual doms. Lets say you have:

      <div id="canvas1"><img id="canvas1"/></div><!-- here first img of matplotlib -->
      <div id="canvas2"><img id="canvas2"/></div><!-- here sec. img of matplotlib -->

I tried already:
https://stackoverflow.com/questions/56583696/how-to-redirect-render-pyodide-output-in-browser:

# 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_element1.__get__(
    create_root_element1, f.canvas.__class__)

f.canvas.show()

But it did not work.....

Is this possible at the moment or do we need the above mentioned adaption? - When is it ready?
Answer
Found a solution for me:

f = plt.figure()
plt.plot(T.T, yout.T)

from js import 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_element2.__get__(
    create_root_element2, f.canvas.__class__)
f.canvas.show()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants