Open franktoffel opened 6 years ago
Using IPython.display.display()
and .clear_output()
the slider disappears after the first interaction.
The following code clears the old results but also the widget.
EDIT: This is expected after ipywidgets v.> 7.0 (see #1274)
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
from IPython.display import display, clear_output
import ipywidgets as widgets
fig, ax = plt.subplots(nrows=1, ncols=1);
x = np.linspace(-5, 5, 30)
y = x**0
line, = ax.plot(x, y)
ax.set_xlabel('x')
ax.set_ylabel('y')
def myplot(n):
line.set_ydata(x**n)
ax.relim()
ax.autoscale()
display(fig)
clear_output(wait=True)
#cell3
n_widget = widgets.IntSlider(
value=2,
min=0,
max=5)
def on_value_change(change):
myplot(n=n_widget.value)
n_widget.observe(on_value_change)
display(n_widget)
The actual issue/problem description may be a bit lost in the above bunches of code. So to summarize:
With ipywidgets 6.0 it was possible to diplay a widget and a resulting output from one cell. Then using clear_output
the output could be removed or updated by subsequently calling display
again.
This behaviour has changed in version 7.0. As the changelog tells us, clear_output
will now clear everything, including the widget. This makes the previous solution unusable. However, it is not clear what alternative one should use to get the same behaviour back with ipywidgets 7.0.
So this is either a question for the recommended way of adding a persistent widget which can clear the output, or a request for adding back such solution in a future version.
If I understood your issue correctly I managed to solve it by using the following approach (https://github.com/kmader/Quantitative-Big-Imaging-2018/blob/127fd3497cbf37ac2ab89f7076fc1c81dcfb9f17/Exercises/ImageEnhancementPlayground.ipynb)
display(ipw.VBox([ipw.VBox([ipw.HBox([ipw.Label(value = 'Image Name:'), image_name]),
ipw.HBox([ipw.Label(value = 'Noise Type:'), noise_func]),
ipw.HBox([ipw.Label(value = 'Noise Level:'), noise_level]),
ipw.HBox([ipw.Label(value = 'Filter Name:'), filter_func]),
ipw.HBox([ipw.Label(value = 'Filter Size:'), filter_size])
])]))
display(fig, display_id = 'nice_figure') # need to create figure before callbacks
def update_image(*args):
show_results(m_axs,
sample_images[image_name.value],
noise_func.value,
noise_level.value,
filter_func.value,
{'size': filter_size.value
})
update_display(display_id = 'nice_figure', obj = fig)
image_name.observe(update_image, names='value')
Great questions. The recommended way now to display and clear output inside a widget is to use an Output widget. We should add a note in the changelog.
Here's a brief example:
from ipywidgets import Output, IntSlider, VBox
from IPython.display import clear_output
out = Output()
slider = IntSlider()
def square(change):
with out:
clear_output()
print(change.new*change.new)
slider.observe(square, 'value')
slider.value = 50
display(VBox([slider, out]))
(this will be even easier soon, when the output widget will have a decorator: https://github.com/jupyter-widgets/ipywidgets/pull/1934)
From the comments above, it's not clear to me what's the recommended way of updating a matplotlib plot inside a trait callback.
This works:
@interact(x=(0, 10))
def plot(x):
plt.plot([1, 2, 3], [4, 5, 6])
But the equivalent using observe
has the problem @franktoffel (¡hola! :wave:) described 2 years ago of many plots appearing:
# Many plots
def plot(change):
plt.plot([1, 2, 3], [5, 4, 6])
slider = widgets.IntSlider(min=0, max=10, value=5)
slider.observe(plot, names="value")
display(slider)
and then using clear_output
at the beginning, clears the slider itself:
# Slider disappears
def plot(change):
clear_output()
plt.plot([1, 2, 3], [5, 4, 6])
slider = widgets.IntSlider(min=0, max=10, value=5)
slider.observe(plot, names="value")
display(slider)
The "Flickering and jumping output" section of the documentation explains how to combine matplotlib with interactive
, but observe
is not mentioned.
The three examples that mention matplotlib, "Exploring the Lorenz System of Differential Equations", "Exploring Beat Frequencies using the Audio Object" and "Explore Random Graphs Using NetworkX" use interact
or interactive
, but observe
is not explained.
There is some discussion at https://github.com/jupyter-widgets/ipywidgets/issues/1940 that uses Output
as a context manager, but still it's not clear to me how to use it. I tried the code below, but the code
widget is empty:
out = widgets.Output()
with out:
fig, ax = plt.subplots()
ax.plot([1, 2], [1, -1])
# ---
# Another cell, output is empty
out
Is there a chance someone posts here a super short example on how to use a callback + observe
+ a matplotlib figure? I would be willing to open a pull request to add it to the docs... When I understand how to do it.
(Edit: Add one extra example)
Sort of incomplete self-answer: I adapted some code from this SO answer, however it still has a couple of weird things I can't explain about the effect of including (or not) plt.show()
and/or display(fig)
:
import ipywidgets as widgets
import matplotlib.pyplot as plt
out = widgets.Output(layout=widgets.Layout(height='300px'))
def f(change):
with out:
fig, ax = plt.subplots()
ax.plot([0, 1],[0, 10])
ax.set_title(change['new'])
out.clear_output() # Required, otherwise output area stays the same but plots get added
# display(fig) # Doesn't work as a replacement of plt.show()
plt.show() # If not here, multiple plots are shown!
w = widgets.IntSlider(min=0, max=10, value=5)
w.observe(f, names="value")
display(w, out)
@astrojuanlu Thanks for the code, this has been causing me quite some frustration as I try to get interact to also work with a throttling / debouncing wrapper.
You code nearly reproduces the 1-liner equivalent of interact
, but I do see noticeable "flashing".
Did you find a solution for this?
And does anyone know why interact
somehow magically works without flashing? Eg.
x = np.linspace(0,1,100)
def update_plot(p=1):
plt.plot(x, x**p)
interact(update_plot, p=(0,1,0.01))
gives no flashing, while the above does...
Thanks, Gary
Hi @gsteele13 , I haven't made any progress since my last comment. Best luck!
Hi @astrojuanlu,
I've figured out how to reproduce the interactivity of "interact" without flashing: key was to use wait=True
when clearing the output. This is simple minimal example:
import ipywidgets as widgets
import matplotlib.pyplot as plt
out = widgets.Output(layout=widgets.Layout(height='300px'))
x = np.linspace(0,1,100)
def update_plot(w):
with out:
# Without clear_output(), figures get appended below each other inside
# the output widget
# Ah ha! Got it! I need wait=True!
out.clear_output(wait=True)
plt.plot(x, x**p_widget.value)
plt.show()
p_widget = widgets.FloatSlider(min=0, max=2, step=0.1, value = 1)
update_plot([])
p_widget.observe(update_plot)
display(p_widget, out)
Next step: debounce / throttle! :)
I see myself googling this a fair number of times in the near future... Would the snippet above fit somewhere in the documentation?
I'm just looking at the docs now. What would be a good place to put this info?
There is some info about "flickering" here when using "interactive output":
But this is slightly different: it is about the building the interaction yourself using an output widget. In that sense, it probably belongs as a part of the output widget examples:
https://ipywidgets.readthedocs.io/en/latest/examples/Output%20Widget.html
Any thoughts? I have checked out the codebase: I'll maybe make some suggestions and a pull request?
created my first pull request (ever...) :)
I am trying to use widget events to make an interactive graph.
Interact works as expected (it changes the figure interactively):
However the following snippet creates several figures that appear below as you interact with the slider.
Can I update the plot as if I were using widgets.interact()?
My current installation is with conda and Python 3.6 (windows machine).