23

I am having trouble displaying plots inside of Jupyter tab widgets. Consider the following snippet:

import matplotlib.pyplot as plt
import pandas as pd
import ipywidgets as widgets
import numpy as np

out1 = widgets.Output()
out2 = widgets.Output()
data1 = pd.DataFrame(np.random.normal(size = 50))
data2 = pd.DataFrame(np.random.normal(size = 100))

with out1:
    fig1, axes1 = plt.subplots()
    data1.hist(ax = axes1)
    display(fig1)

with out2:
    fig2, axes2 = plt.subplots()
    data2.hist(ax = axes2)
    display(fig2)

tab = widgets.Tab(children = [out1, out2])
tab.set_title(0, 'First')
tab.set_title(1, 'Second')
display(tab)

(I am running Python 3.5.2, Jupyter 4.4.0, ipywidgets 7.2.1 on Ubuntu 16.04 inside a virtual environment.)

If I put this code on the first row of the notebook and run it, I see a tab widget with two tabs, each one of which displays a string, but not the plot:

No plots

If I run it for a second time, or if I rerun it putting everything after the import of matplotlib in a second cell, I see a tab widget with one plot on each tab, but I get the two plots displayed a second time outside of the tabs.

Too many plots

I can get rid of the additional displays by wrapping my code inside calls to plt.ioff and plt.ion, but it has been suggested to me that this is a hack. And in any case, it does not make matplotlib display the plots in the first cell.

Question: What is the proper way of displaying plots inside tabs?

Arthur Azevedo De Amorim
  • 23,012
  • 3
  • 33
  • 39

3 Answers3

36

I added a couple of things to make your code work as you would like

  • Add %matplotlib inline at the top of the cell
  • Replace your display(fig) calls with plt.show(fig) calls.
%matplotlib inline
import matplotlib.pyplot as plt
import pandas as pd
import ipywidgets as widgets
import numpy as np

out1 = widgets.Output()
out2 = widgets.Output()
data1 = pd.DataFrame(np.random.normal(size = 50))
data2 = pd.DataFrame(np.random.normal(size = 100))

tab = widgets.Tab(children = [out1, out2])
tab.set_title(0, 'First')
tab.set_title(1, 'Second')
display(tab)

with out1:
    fig1, axes1 = plt.subplots()
    data1.hist(ax = axes1)
    plt.show(fig1)

with out2:
    fig2, axes2 = plt.subplots()
    data2.hist(ax = axes2)
    plt.show(fig2)
ac24
  • 5,325
  • 1
  • 16
  • 31
  • 3
    What's the logic behind "Replace your display(fig) calls with plt.show(fig) calls" please? – ILoveCoding May 16 '20 at 12:43
  • Isn't display a method of ipywidget, which is not really compatible with matplotlib? I am not sure, but think. – user2679290 Sep 22 '21 at 00:39
  • I have used this for [a related answer here](https://stackoverflow.com/a/60157495/8508004). I'm linking it here because I have links there that launch active, temporary sessions served via MyBinder that should run just in your browser without installing anything. – Wayne Mar 07 '22 at 22:10
  • 1
    @ILoveCoding, I think the reason for using `plt.show()` and not `display` is that in the OP example there were two plots getting made and you can channel the `plt.show()` generated output into each widget. So maybe the thinking there is it's simpler to use `plt.show()` & target the output to the tab rather than suppress the output that is made in the course of producing the plot in the first place and then display the associated figure again? If you go that route & use `display()`, you add another line of code to each `with` block .... – Wayne Mar 08 '22 at 20:43
  • 1
    see part #1 example at the top [here](https://mybinder.org/v2/gh/fomightez/communication_voila/master?filepath=notebooks%2Fuse_display_with_tabs.ipynb). (Go there and run all the cells first.) If you remove the `plt.close()` and `display()` lines from each and run the code fresh, you'll see the widgets get made without the plots and the two plots show up below the tab widget. You need `plt.show()` added there to channel it into the context manager that gets used in the `widgets.Output()`, it seems. – Wayne Mar 08 '22 at 20:44
  • @user2679290, while I don't claim to have a handle on all of it, `display` is more general than widgets alone. You can use `display` here if you have figure objects. matplotlib subplots can make figure objects. There's use of it for content for one of the two tabs among the companion notebook I shared; however [the examples in part #1 and part #2](https://mybinder.org/v2/gh/fomightez/communication_voila/master?filepath=notebooks%2Fuse_display_with_tabs.ipynb) make it much more clear. (Go there and run all the cells first.) – Wayne Mar 08 '22 at 20:45
  • `plt.show()` also works for `matplotlib.image.AxesImage`-type objects whereas `display()` doesn't, unless you add `.figure`. For example, `plt.show(my_plot)` or `display(my_plot.figure)`. – Wayne Mar 08 '22 at 21:57
-1

plt.show(fig) in the answer above from ac24 is now deprecated:

In [1]: import matplotlib.pyplot as plt                                                                                                                                   

In [2]: fig = plt.figure()                                                                                                                                                

In [3]: plt.show(fig)                                                                                                                                                     
<ipython-input-3-d1fd62acb551>:1: MatplotlibDeprecationWarning: Passing the 
block parameter of show() positionally is deprecated since Matplotlib 3.1; the 
parameter will become keyword-only in 3.3.
    plt.show(fig)

plt.show(block=True) (or plt.show(block=False)) is the keyword-only call.

RA Prism
  • 59
  • 6
-3

from IPython.display import display

Dharman
  • 30,962
  • 25
  • 85
  • 135