【问题标题】:How to change the default position of a ipywidget slider to the side of a matplotlib figure?如何将 ipywidget 滑块的默认位置更改为 matplotlib 图的一侧?
【发布时间】:2017-08-02 15:02:02
【问题描述】:

我正在寻找一种方法来将垂直 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))

这就是我所拥有的:

这就是我想要的:

可能有一个非常简单的方法可以做到这一点,但我自己想不通。

我尝试将figinteractive 小部件都放入VBox,然后用IPython.display 包装VBox,但没有成功。

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

编辑1:

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

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

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

【问题讨论】:

  • 您是否碰巧尝试了我的解决方案?
  • 是的,我认为您走在正确的轨道上。如果您找到一种方法通过从 update_plot 函数中解开图形和轴的构造来使其工作,我将接受您的回答。在 Output() 对象上花很多时间。我认为这将解决我们的问题。

标签: python matplotlib jupyter ipywidgets


【解决方案1】:

我决定使用 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 代码行。

我将保留接受的 James 答案,因为它回答了原始问题。

【讨论】:

  • 这似乎不适用于 0.11.9,任何线索为什么?运行此 sn-p 后显示几乎保持空白。
【解决方案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))

更新:

这是来自 ipywidgets gitter 的 Jason Grout 的解决方案。

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

【讨论】:

  • 你部分解决了这个问题,但是将 matplotlib 图形的构造包含在 update_plot 函数中会导致这种糟糕的刷新行为,而且,当你第一次更新滑块时会构建图形。可能有一种方法可以使用其中的 matplotlib 捕获输出。检查这个:github.com/jupyter-widgets/ipywidgets/blob/master/ipywidgets/…
  • @BrunoRuasDePinho 闪烁可能与您使用的浏览器或您使用的 ipywidgets 版本有关,如this issue 中所述。您可能会看到使用 %matplotlib inline 而不是 %matplotlib notebook 的微小改进,但您使用了一些交互功能。所以 IDK 这可能和现在一样好。我会继续寻找,但至少要为部分解决方案投赞成票:)
  • 当然是詹姆斯!谢谢!!
  • 没有问题! ipywidgets 也是解决相关问题的另一个重要资源。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-28
  • 1970-01-01
  • 2014-12-18
  • 1970-01-01
相关资源
最近更新 更多