如何将ipywidget滑块的默认位置更改为matplotlib图的一侧?

时间:2017-08-02 15:02:02

标签: python matplotlib jupyter ipywidgets

我正在研究如何将垂直IntSlider的位置更改为matplotlib图的右侧。这是代码:

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))

这就是我所拥有的:

enter image description here

这就是我想要的:

enter image description here

可能有一种非常简单的方法可以做到这一点,但我无法弄清楚自己。

我尝试将figinteractive窗口小部件放入VBox,然后将VBoxIPython.display包裹起来,并且它没有工作

在示例中无法找到直接的解决方案。

EDIT1:

ipywidgets提供了一个Output()类,用于捕获输出区域并在小部件上下文中使用它。

我会试着弄清楚如何使用它。

这是对象: https://github.com/jupyter-widgets/ipywidgets/blob/master/ipywidgets/widgets/widget_output.py

2 个答案:

答案 0 :(得分:2)

您可以通过创建交互式窗口小部件然后将children加载到HBox来解决此问题。互动的儿童小部件遵循这一惯例; (widget_0,widget_1 ...,output)其中元组的最后一个成员是控件小部件的输出。您可以在声明之前或之后定义HBox的布局。 Read more on the layouts available here

以下解决方案有几点需要注意;图表最初可能不会显示,您可能需要在控件出现之前进行调整,第二次使用%matplotlib notebook魔法时控件可能会在更新时导致大量闪烁。除此之外,我认为这应该像你想要的那样工作;

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))

更新

这是Jason Grout的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]))

答案 1 :(得分:2)

我决定使用bqplot代替matplotlib来尝试此示例,结果证明它更简单。

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旨在成为一个交互式小部件。这种方式可以简单地添加到输出中,而不必将其包装到update_plot函数中。

来自bqplot文档:

  

在bqplot中,绘图的每个属性都是交互式的   小部件。这允许用户将任何绘图与IPython集成   从小部件创建复杂且功能丰富的GUI的小部件   简单的Python代码行。

我会接受詹姆斯接受的答案,因为它回答了原来的问题。