You want to display a Matplotlib figure within an ipywidgets HBox
using an Output
widget. See more about widgets.Output()
here in the ipywidgets documentation under 'Output widgets: leveraging Jupyter’s display system'.
Simplified example to illustrate:
%matplotlib ipympl
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets
from IPython.display import display, clear_output
def f(x, a):
return np.exp(-a * x**2)
# Create the slider widget
slider = widgets.FloatSlider(min=0.1, max=10.0, step=0.1, value=1.0, description='a')
# Create an Output widget to hold the plot
output = widgets.Output()
# Create the plot within the Output
with output:
x = np.linspace(-3, 3, 100)
fig, ax = plt.subplots()
line, = ax.plot(x, f(x, slider.value))
plt.ylim(-0.2, 1.2)
# Define the interactive function
def update(a):
with output:
clear_output(wait=True) # Clear the previous plot
line.set_ydata(f(x, a))
fig.canvas.draw_idle()
# Connect the slider to the update function
slider.observe(lambda change: update(change['new']), names='value')
# Display the slider and the plot within the HBox
hbox = widgets.HBox([slider, output])
display(hbox)
(Note this is as opposed to something more along the lines of your provided code, see below where hbox = widgets.HBox([slider, fig.canvas])
is used.)
Note that to be more explicit that you need ipympl
, the convention now is %matplotlib ipympl
. As discussed in the 'Basic Example' case here, the %maplotlib widget
is still supported though, but behind-the-scenes ipympl
is getting used.
You should be able to adapt your code to that or vice versa.
This below may do that adapting of the approach to your code? (I couldn't test it for reasons in the comment below your post.) Or at least be closer to implementing that:
%matplotlib ipympl
import tomolyzer as t
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import interact, IntSlider, FloatSlider, Dropdown, VBox, HBox, Layout
from IPython.display import display # Import display for rendering widgets
import logging
from scipy.fft import fft2, fftshiftimport scipy.ndimage as S
import os
from tqdm import tqdm
# Create an Output widget to hold the plot
output = widgets.Output()
with output:
fig, axs = plt.subplots(1, 2, figsize=(10, 5)) # 1 row and 3 columns figure size of 15 inches wide and 5 inches tall.
axs[0].axis("off")
axs[1].axis("off")
std = 0
max = 0
factor = max - std*2
i=0
image = data[i][testing_radius, :, :].copy()
axs[0].imshow(image, cmap='viridis', aspect='auto') # Display the slice
axs[0].set_title(f'{body.centers_scaled[title][i]}') # Title for the slice
axs[0].set_xlabel(f'{np.mean(image):.2e}')
axs[0].set_ylabel('YX')
axs[0].set_xticks([0,mid_x/2,mid_x])
axs[0].set_yticks([0,mid_y/2,mid_y])
axs[1].imshow(image, cmap='viridis', aspect='auto') # Display the slice
def updatePlot(val):
with output:
min = np.min(data[i][18])
max = np.max(data[i][18])
mean = np.mean(data[i][18])
std = np.std(data[i][18])
threshold= max - std*val
image = data[i][testing_radius, :, :].copy()
# positve intercept
image[np.where(image > max - std*val)] = 0
image[np.where(image < max - std*val)] = 1
axs[1].clear() # Clear previous plot
axs[1].imshow(image, cmap='viridis', aspect='auto') # Display the slice
axs[1].text(
0.5, -0.1, f"{threshold:.2e}",
transform=axs[1].transAxes, ha='center', va='top', fontsize=12
)
std_slider = FloatSlider(min=0,max=7,step=0.001,value=0,description='Std',orientation='vertical', layout=Layout(height='300px'))
x=interact(updatePlot,val = std_slider)
plots_and_slider = HBox([std_slider, output], layout=Layout(align_items='center'))
display(plots_and_slider)
Note the example that Google's Gemini gave me with the prompt "Can you give me a simple example that uses an HBox to put a slider and plot side by side. Use Ipywidgets interact()
, please.", demonstrates what you saw:
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import interact, interactive, fixed, interact_manual
import ipywidgets as widgets
def f(x, a):
return np.exp(-a * x**2)
# Create the slider widget
slider = widgets.FloatSlider(min=0.1, max=10.0, step=0.1, value=1.0, description='a')
# Create the plot
x = np.linspace(-3, 3, 100)
fig, ax = plt.subplots()
line, = ax.plot(x, f(x, slider.value))
plt.ylim(-0.2, 1.2)
# Define the interactive function
def update(a):
line.set_ydata(f(x, a))
fig.canvas.draw_idle()
# Create the HBox layout
hbox = widgets.HBox([slider, fig.canvas])
# Display the interactive widget
display(hbox)
# Connect the slider to the update function
slider.observe(lambda change: update(change['new']), names='value')
That shows the plot twice in the output area.
It only took me a couple rounds with Gemini saying what was going on/what the issues were to get it to give the version I provide above. So for getting MRE's Google Gemini can help and it can help you troubleshoot, too.