Issue
This is so damn irritating... using:
Python 3.8.10 (default, Sep 28 2021, 16:10:42) [GCC 9.3.0]
matplotlib 3.5.0
ipywidgets 7.6.5
jupyter 1.0.0
So, consider this example:
import IPython.display
from IPython.display import display
from ipywidgets import widgets, Layout
%matplotlib widget
import matplotlib as mpl
import matplotlib.pyplot as plt
widget_01 = widgets.HTML("<p>Hello, world</p>")
widget_02 = widgets.Output(layout=Layout(width='100%'))
widget_03 = widgets.Output(layout=Layout(width='100%'))
with widget_02:
IPython.display.clear_output(True)
display(widgets.HTMLMath("<p>Try equation: $x_y = y_x$</p>"))
myvbox = widgets.VBox([
widget_01,
widget_02,
widget_03
],)
display(myvbox)
with widget_03:
IPython.display.clear_output(True)
display(widgets.HTMLMath("<p>In the next episode...</p>"))
So, I have a "Vbox", which has three elements, "widget_01", "widget_02", "widget_03"; these three elements are put in a list, which (unlike dict) has an order, so I can count on "widget_01" being "first", "widget_02" being "second", and so on. And since this is a "VBox", that is, Vertical Box, I'd expect the "first" item in the list to be rendered on top, the "second" item below it (that is, in the middle), and so on.
And indeed, that is how the output looks like in the simplest case:
Ok, cool - now, let's just make "widget_02" into matplotlib widget
:
import IPython.display
from IPython.display import display
from ipywidgets import widgets, Layout
%matplotlib widget
import matplotlib as mpl
import matplotlib.pyplot as plt
widget_01 = widgets.HTML("<p>Hello, world</p>")
widget_02 = widgets.Output(layout=Layout(width='100%'))
widget_03 = widgets.Output(layout=Layout(width='100%'))
with widget_02:
IPython.display.clear_output(True)
fig = plt.figure(figsize=(10,1), dpi=90)
fig.canvas.toolbar_visible = False
ax = fig.add_subplot(111)
ax.plot([0,1,2], [0,1,2])
myvbox = widgets.VBox([
widget_01,
widget_02,
widget_03
],)
display(myvbox)
with widget_03:
IPython.display.clear_output(True)
display(widgets.HTMLMath("<p>In the next episode...</p>"))
Well, now - for whatever reason - "widget_02" (which became a plot) is not in the middle anymore, but is instead last (at bottom):
I mean, I sure update "widget_03" after I update the plot - but I'd expect the VBox to keep the order of widgets?
So - how can I have a Matplotlib widget rendered in the order it is listed into an ipywidgets VBox, even if I update a different widget in the same VBox after I have rendered the plot?
Solution
Ok, I found a solution here: Displaying a previously created Matplotlib figure in a widget · Issue #203 · matplotlib/ipympl, it seems ...
The problem turns out to be not so much "controlling placement" per se, but:
- The fact that
fig = plt.figure...
call actually produces output by itself, regardless of (with
) output context; that can be prevented with disabling matplotlib interactive mode viaplt.ioff()
- The fact that to display an interactive matplotlib widget, we should
display(fig.canvas)
(not justdisplay(fig)
)
So, in the OP, there was not even a display(fig)
- so the entire output of the matplotlib widget was due to the fig = plt.figure...
call.
Therefore, to obtain the desired output, as specified in the VBox (widget_01 text on top, widget_02 plot in middle, widget_03 text on bottom):
... the following corrected code can be used:
import IPython.display
from IPython.display import display
from ipywidgets import widgets, Layout
%matplotlib widget
import matplotlib as mpl
import matplotlib.pyplot as plt
widget_01 = widgets.HTML("<p>Hello, world</p>")
widget_02 = widgets.Output(layout=Layout(width='100%'))
widget_03 = widgets.Output(layout=Layout(width='100%'))
with widget_02:
widget_02.clear_output(wait=True)
plt.ioff() # "turn off interactive mode so figure doesn't show"
fig = plt.figure(figsize=(10,1), dpi=90)
fig.canvas.toolbar_visible = False
ax = fig.add_subplot(111)
ax.plot([0,1,2], [0,1,2])
plt.ion() # "figure still doesn't show"
display(fig.canvas) # "It's the canvas attribute that is the interactive widget, not the figure"
myvbox = widgets.VBox([
widget_01,
widget_02,
widget_03
],)
display(myvbox)
with widget_03:
IPython.display.clear_output(True)
display(widgets.HTMLMath("<p>In the next episode...</p>"))
Answered By - sdbbs
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.