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

Plot output with ipywidgets formatting incorrectly #436

Open
drs378 opened this issue Mar 4, 2022 · 5 comments
Open

Plot output with ipywidgets formatting incorrectly #436

drs378 opened this issue Mar 4, 2022 · 5 comments

Comments

@drs378
Copy link

drs378 commented Mar 4, 2022

Describe the issue

I built a small GUI within a notebook and had two figures in an ipywidgets HBox object. the latest version of ipympl made it so I could not have both figures in an HBox and generally disrupted the formatting of the object. I was able to fix the issue by reverting both matplotlib and the ipympl versions.

a short script that generates the broken output is as follows

%matplotlib widget
from IPython.display import display, HTML
display(HTML("<style>.container { width:100%!important;}</style>"))

import ipywidgets as ipy
import matplotlib.pyplot as plt
import numpy as np
out1 = ipy.Output()
b1 = ipy.Button(description = 'button1')
b2 = ipy.Button(description = 'button2')
x = np.linspace(0,7,21)
y = np.sin(x)
with out1:
    fig,ax = plt.subplots()
    ax.plot(x,y)
    
display(ipy.HBox([out1,ipy.VBox([b1,b2])]))

Versions

(working version)

ipympl == 0.5.8
matplotlib == 3.3.3

(broken version)

matplotlib == 3.5.1
@mklingn
Copy link

mklingn commented Nov 15, 2022

I am also seeing this behaviour with ipympl 0.9.2 and matplotlib 3.6.2.: The plot does not get put into the Output widget.

This breaks the top search result for "ipywidgets plot": https://kapernikov.com/ipywidgets-with-matplotlib/

@drs378 : Did you check 0.5.8 was the last version that worked or did you just happen to have it at hand?

@ekimd
Copy link

ekimd commented Feb 20, 2023

Just want to confirm the same problem for me with ipympl 0.9.2 and matplotlib 3.6.2

@ianhi
Copy link
Collaborator

ianhi commented Feb 21, 2023

I take that the issue is the ordering of the plot relative to the buttons?

This behavior changed slightly as part of #343. While unfortunate that this changed it was necessary as part of fixing the longstanding and important issue of plot. The core of what's happening here is that the plot is being displayed before the output widget has an opportunity to capture it. (sidenote @martinRenou is that actually expected, it would maybe be nice if output widgets captured the plots).

Fortunately there is a workaround - which is to be more explicit about what you are doing. You can read more about it in the docs here: https://matplotlib.org/ipympl/examples/full-example.html#interactions-with-other-widgets-and-layouting

But the short of it is:

  1. Wrap the plt.subplots call in an ioff block
  2. Don't capture with an output block
  3. instead embed fig.canvas into the widget layout.

Combining those, you should make the final lines like so:

with plt.ioff():
    fig, ax = plt.subplots()
ax.plot(x,y)
    
display(ipy.HBox([fig.canvas,ipy.VBox([b1,b2])]))

@ekimd
Copy link

ekimd commented Feb 21, 2023

Thanks, @ianhi ! That works as expected. I have a separate but related question. I used to be able to create a pop-out browser window of a figure via the following code, but now it doesn't work anymore and I'm guessing a similar small change is necessary with the final two output lines. Thanks in advance.

    def _popout_fig(fig, name="name"):
        buf = BytesIO()
        fig.savefig(buf, format="png")
        fig_size_pix = fig.get_size_inches() * fig.dpi
        s = '<script type="text/Javascript">'
        s += f'var win = window.open("", "{name}", "toolbar=no, location=no, status=no, menubar=no, '\
             f'scrollbars=yes, resizable=yes, width={1.12*fig_size_pix[0]}, height={1.12*fig_size_pix[1]}, '\
             'top="+(screen.height-400)+", left="+(screen.width-840));'  # This part sets where it shows on screen
        s += f'win.document.title = "{name}";'
        s += 'win.document.body.innerHTML = \''
        img = base64.b64encode(buf.getbuffer()).decode('ascii')
        s += f'<img src="data:image/png;base64,{img}"/>'
        s += '\';'
        s += '</script>'
        with widgets.Output():
            IPython.display.display(IPython.display.HTML(s))  # noqa

@ekimd
Copy link

ekimd commented Feb 22, 2023

Never mind, I solved it. I still need a widgets.Output object that's a child of the original displayed widget, and then use it in place of the with widgets.Output(): above.

All my concerns are now resolved.

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

4 participants