I am looking into a way to change the position of the vertical IntSlider to the right of the matplotlib fig. Here is the code:
from ipywidgets import interact, fixed, IntSlider
import numpy as np
from matplotlib import pyplot as plt
%matplotlib notebook
fig = plt.figure(figsize=(8,4))
xs = np.random.random_integers(0, 5000, 50)
ys = np.random.random_integers(0, 5000, 50)
ax = fig.add_subplot(111)
scat, = ax.plot(xs, ys, 'kx', markersize=1)
ax.grid(which='both', color='.25', lw=.1)
ax.set_aspect('equal'), ax.set_title('Rotate')
def rotate(theta, xs, ys):
new_xs = xs * np.cos(np.deg2rad(theta)) - ys * np.sin(np.deg2rad(theta))
new_xs -= new_xs.min()
new_ys = xs * np.sin(np.deg2rad(theta)) + ys * np.cos(np.deg2rad(theta))
new_ys -= new_ys.min()
return new_xs, new_ys
def update_plot(theta, xs, ys):
new_xs, new_ys = rotate(theta, xs, ys)
scat.set_xdata(new_xs), scat.set_ydata(new_ys)
ax.set_xlim(new_xs.min() - 500, new_xs.max() + 500)
ax.set_ylim(new_ys.min() - 500, new_ys.max() + 500)
w = interact(update_plot,
theta=IntSlider(min=-180, max=180, step=5,value=0, orientation='vertical'),
xs=fixed(xs),
ys=fixed(ys))
This is what I have:
This is what I want:
There might be a very simple way to do this, but I can't figure out myself.
I tried to place both the fig
and the interactive
widget into a VBox
then wrapping the VBox
with IPython.display
and it didn't work.
Could not find a straight solution to this in the examples.
EDIT1:
ipywidgets provides an Output()
class that captures the output area and use it inside the widget context.
I will try to figure out how to use it.
This is the object: https://github.com/jupyter-widgets/ipywidgets/blob/master/ipywidgets/widgets/widget_output.py
I decided to try this example using bqplot instead of matplotlib and it turned out to be way more simple.
import numpy as np
from bqplot import pyplot as plt
from IPython.display import display
from ipywidgets import interactive, fixed, IntSlider, HBox, Layout
plt.figure(min_aspect_ratio=1, max_aspect_ratio=1)
xs = np.random.randint(0, 5000 + 1, 100)
ys = np.random.randint(0, 5000 + 1, 100)
scat = plt.scatter(xs, ys)
def rotate(theta, xs, ys):
new_xs = xs * np.cos(np.deg2rad(theta)) - ys * np.sin(np.deg2rad(theta))
new_xs -= new_xs.min()
new_ys = xs * np.sin(np.deg2rad(theta)) + ys * np.cos(np.deg2rad(theta))
new_ys -= new_ys.min()
return new_xs, new_ys
def update_plot(theta, xs, ys):
new_xs, new_ys = rotate(theta, xs, ys)
scat.x, scat.y = new_xs, new_ys
w = interactive(update_plot,
theta=IntSlider(min=-180, max=180, step=5,value=0, orientation='vertical'),
xs=fixed(xs),
ys=fixed(ys))
box_layout = Layout(display='flex', flex_flow='row', justify_content='center', align_items='center')
display(HBox([plt.current_figure(), w], layout=box_layout))
bqplot
is designed to be an interactive widget. This is way it could be simply added to the output without having to wrap it into the update_plot
function.
From the bqplot
documentation:
In bqplot, every single attribute of the plot is an interactive widget. This allows the user to integrate any plot with IPython widgets to create a complex and feature rich GUI from just a few simple lines of Python code.
I will keep the accepted James answer because it answered the original question.
You can solve this by creating an interactive widget and then loading the children
into a HBox
. The child widgets of an interactive follow this convention; (widget_0, widget_1 ..., output) where the last member of the tuple is the output of the control widgets. You can define the layout of the HBox before or after you declare it. Read more on the layouts available here.
The following solution has a couple caveats; the graph may not show up initially you may have to tweak the control before it appears, second when using the %matplotlib notebook
magic the control may lead to a lot of flickering on when updating. Other than that I think that this should work like you want;
from IPython.display import display
from ipywidgets import interactive, fixed, IntSlider, HBox, Layout
import numpy as np
import matplotlib.pylab as plt
%matplotlib notebook
def rotate(theta, xs, ys):
new_xs = xs * np.cos(np.deg2rad(theta)) - ys * np.sin(np.deg2rad(theta))
new_xs -= new_xs.min()
new_ys = xs * np.sin(np.deg2rad(theta)) + ys * np.cos(np.deg2rad(theta))
new_ys -= new_ys.min()
return new_xs, new_ys
def update_plot(theta, xs, ys):
fig = plt.figure(figsize=(8,4))
ax = fig.add_subplot(111)
scat, = ax.plot(xs, ys, 'kx', markersize=1)
ax.grid(which='both', color='.25', lw=.1)
ax.set_aspect('equal'), ax.set_title('Rotate')
new_xs, new_ys = rotate(theta, xs, ys)
scat.set_xdata(new_xs), scat.set_ydata(new_ys)
ax.set_xlim(new_xs.min() - 500, new_xs.max() + 500)
ax.set_ylim(new_ys.min() - 500, new_ys.max() + 500)
xs = np.random.randint(0, 5000, 50)
ys = np.random.randint(0, 5000, 50)
w = interactive(update_plot,
theta=IntSlider(min=-180, max=180, step=5, value=0,orientation='vertical'),
xs=fixed(xs),
ys=fixed(ys))
# Define the layout here.
box_layout = Layout(display='flex', flex_flow='row', justify_content='space-between', align_items='center')
display(HBox([w.children[1],w.children[0]], layout=box_layout))
Update:
This is Jason Grout's solution from the ipywidgets gitter.
from IPython.display import display, clear_output
from ipywidgets import interact, fixed, IntSlider, HBox, Layout, Output, VBox
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
def rotate(theta, xs, ys):
new_xs = xs * np.cos(np.deg2rad(theta)) - ys * np.sin(np.deg2rad(theta))
new_xs -= new_xs.min()
new_ys = xs * np.sin(np.deg2rad(theta)) + ys * np.cos(np.deg2rad(theta))
new_ys -= new_ys.min()
return new_xs, new_ys
out = Output(layout={'width': '300px', 'height': '300px'})
def update_plot(change):
theta = change['new'] # new slider value
with out:
clear_output(wait=True)
fig = plt.figure(figsize=(4,4))
ax = fig.add_subplot(111)
scat, = ax.plot(xs, ys, 'kx', markersize=1)
ax.grid(which='both', color='.25', lw=.1)
ax.set_aspect('equal'), ax.set_title('Rotate')
new_xs, new_ys = rotate(theta, xs, ys)
scat.set_xdata(new_xs), scat.set_ydata(new_ys)
ax.set_xlim(new_xs.min() - 500, new_xs.max() + 500)
ax.set_ylim(new_ys.min() - 500, new_ys.max() + 500)
plt.show()
xs = np.random.randint(0, 5000, 50)
ys = np.random.randint(0, 5000, 50)
slider = IntSlider(min=-180, max=180, step=5, value=0, orientation='vertical')
slider.observe(update_plot, 'value')
update_plot({'new': slider.value})
display(HBox([out, slider]))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With