Reputation: 1
I'm using a widgets.Output to display a matplotlib figure. I would like to be able to update the data shown in the figure/ axis by clicking a button, without generating a new figure.
Ultimately, the button function will retrieve data from a selected file - the graph does not need to update automatically like an animation.
In a previous version, I used:
import ipywidgets as wg
import matplotlib.pyplot as plt
output = wg.Output()
def make_plot(b):
with output:
f, ax = plt.subplots(figsize=figsize)
## details of plot here
output.on_displayed(make_plot)
but now get the message that wg.Output() has no attribute on_displayed.
I've tried the below, but it does not change the axis after clicking the button (the "confirm triggering function") does print correctly.
class GUI(object):
def __init__(self):
import ipywidgets as wg
import matplotlib.pyplot as plt
import numpy as np
self.display_button = wg.Button(description="Display graph")
self.graph_display = wg.Output()
## Shows empty axis but does not update with function when included here:
with self.graph_display:
self.fig, self.ax = plt.subplots()
def on_display_button_clicked(b):
print("confirm triggering function")
with self.graph_display:
## Does generate graph on button click when line below is uncommented, *but* appends a new graph each time
## self.fig, self.ax = plt.subplots()
self.ax.scatter(np.random.rand(10), np.random.rand(10))
self.display_button.on_click(on_display_button_clicked)
self.vbox = wg.VBox([self.display_button,
self.graph_display])
gui = GUI()
gui.vbox
I also tried modifying the example at https://stackoverflow.com/a/51060721, which works for displaying the specified graph when run once.
## modified from https://stackoverflow.com/a/51060721,
import matplotlib.pyplot as plt
import pandas as pd
import ipywidgets as widgets
import numpy as np
out1 = widgets.Output()
data1 = pd.DataFrame(np.random.normal(size = 50))
tab = widgets.Tab(children = [out1])
tab.set_title(0, 'First')
display(tab)
with out1:
fig1, axes1 = plt.subplots()
axes1.imshow('image.png')
data1.hist(ax = axes1)
plt.show(fig1)
However if I then run e.g. axes1.set_title("Graph title")
in another jupyter cell, this does not modify the axes1 display.
Upvotes: 0
Views: 32
Reputation: 9984
This upper section was meant to have an answer/suggestion for first part of the post; however, OP found a solution that worked before I got back to that part. (The priority was to provide expanded information and code to go along with my comment and the below section is for that.) ...
I commented on the part that begins "I also tried modifying the example at stackoverflow.com/a/51060721,". I need SO's 'answer' space to provide a possibility with code. (So think of this section as an expansion on my comments about the bottom section of your post in relation to the referenced answer at '51060721'.) You can do the adding of the title in a second cell, but it won't work in the simple way you try because on a Jupyter notebook environment, calling plt.show()
within an output widget like out1
actually closes the figure within that specific output cell. Any subsequent modifications to the figure (like setting the title) after calling plt.show()
won't have any effect because the figure is no longer active.
The following would work:
import matplotlib.pyplot as plt
import numpy as np
import ipywidgets as widgets
out1 = widgets.Output()
data = np.random.rand(10, 10)
tab = widgets.Tab(children=[out1])
tab.set_title(0, 'First')
display(tab)
with out1:
fig1, axes1 = plt.subplots()
axes1.imshow(data, cmap='viridis')
plt.show(fig1) # Display the initial plot
Then in the next cell:
with out1:
out1.clear_output(wait=True) # Clear the previous output
fig1, axes1 = plt.subplots() # Create a new figure and axes
axes1.imshow(data, cmap='viridis')
axes1.set_title("New Title")
plt.show(fig1)
Of course, that goes against your hope of, "I would like to be able to update the data shown in the figure/ axis by clicking a button, without generating a new figure." However, my understanding an Output widget doesn't inherently provide a mechanism to directly access and modify the internal state of a figure object created within a previous cell.
Upvotes: 0
Reputation: 1
The below integrates the clear_output
function from Wayne's answer above, and is triggered by a button click.
Although it does generate a new figure each time, the previous one is closed and replaced so only one figure is displayed at a time.
matplotlib nbagg
enables the figure to be plotted within the VBox
and doesn't require the plt.show(fig)
line (using matplotlib inline
plots the new figure below the VBox
%matplotlib nbagg
class Axis(object):
def __init__(axis_self):
from IPython.display import display
import ipywidgets as wg
import numpy as np
import matplotlib.pyplot as plt
axis_self.show_graph_button = wg.Button(description="Display graph")
axis_self.output = wg.Output() ## For output to display within tab, need %matplotlib nbagg at beginning
axis_self.B_manual_select_area = wg.Button(description="Manually select area")
axis_self.vbox = wg.Tab([wg.VBox([axis_self.show_graph_button,
axis_self.output])
])
def on_show_graph_button_clicked(b):
with axis_self.output:
axis_self.output.clear_output(wait=True)
fig, ax = plt.subplots()
ax.plot(np.random.rand(10), np.random.rand(10))
axis_self.show_graph_button.on_click(on_show_graph_button_clicked)
axis_self = Axis()
axis_self.vbox
Upvotes: 0