ipywidget与matplotlib图表总是显示两个坐标轴

3

我正尝试创建一个使用 ipywidgetmatplotlib 图形的界面,它会在滑块改变时更新。原理上它是可用的,但总是多创建了一个图形。

以下是代码:

import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import widgets
from IPython.display import display, clear_output

# make a grid

x = np.linspace(0, 5, 100)
X, Y = np.meshgrid(x, x)

# create the layout with slider

out = widgets.Output(layout=widgets.Layout(height='300px', width = '400px', border='solid'))
slider = widgets.IntSlider(value=1, min=1, max=5)
w = widgets.VBox(children=(out, slider))

# axes to plot into

ax = plt.axes()

display(w)

def update(value):
    i = slider.value
    Z = np.exp(-(X / i)**2 - (Y / i)**2)
    ax.pcolormesh(x, x, Z, vmin=0, vmax=1, shading='auto')
    with out:
        clear_output(wait=True)
        display(ax.figure)

slider.observe(update)
update(None)

以下是不期望的输出

output

这个小部件能够正常工作,并且只有上半部分输出被更新,但我不明白为什么下半部分的输出也存在或如何摆脱它。我是否忽略了一些显而易见的东西?

3个回答

1
您可以使用小部件后端%matplotlib widget,我认为它是为此设计的。您需要将%matplotlib widget放在顶部(或在引入matplotlib内容之前)。
更新:Matplotlib here 还提供了一些指导,强调如下。
注意:要获得此处描述的交互功能,必须使用交互式后端。笔记本中的默认后端即内联后端不支持此功能。backend_inline 在执行单元格时会渲染图形并将静态图像插入笔记本。因为图像是静态的,所以无法平移/缩放、接受用户输入或从其他单元格更新。
您可以将示例简化为:
%matplotlib widget
import numpy as np
import matplotlib.pyplot as plt
from ipywidgets import widgets
from IPython.display import display, clear_output

x = np.linspace(0, 5, 100)
X, Y = np.meshgrid(x, x)

slider = widgets.IntSlider(value=1, min=1, max=5)

ax = plt.axes()
display(slider)
def update(value):
    i = slider.value
    Z = np.exp(-(X / i)**2 - (Y / i)**2)
    ax.pcolormesh(x, x, Z, vmin=0, vmax=1, shading='auto')

slider.observe(update)
update(None)

enter image description here


谢谢,确实总是只显示一个数字,但似乎取决于小部件后端。如果没有它,它就不会更新。我在原始帖子中的做法总是显示两个数字,但即使没有调用小部件后端,它似乎也可以在经典笔记本和Jupyter lab中工作(但我已安装了ipywidgets扩展)。 - John Smith
你能澄清一下“没有它就不会更新”吗?我希望通过%matplotlib widget,你可以得到一个单独的图形,并且它会随着滑块的移动而更新。 - jayveesea
1
是的,使用小部件后端它可以正常工作,但没有它(例如内联后端)它不起作用,但也不会引发异常。我希望能够在笔记本和Jupyter Lab中的所有后端下都能正常工作。那么,我想检查正确的后端并在其他情况下发出警告是正确的方法。 - John Smith

0

jayveesa的回答是避免此输出的一种非常好的方法。我想在你的问题的why方面添加一个答案。

小部件起作用,只有上面的输出被更新,但我不明白为什么下面的输出也存在或如何摆脱它。我错过了什么明显的东西吗?

在这里要做的一件有益的事情是注释掉update中display图形的行。


def update(value):
    i = slider.value
    Z = np.exp(-(X / i)**2 - (Y / i)**2)
    ax.pcolormesh(x, x, Z, vmin=0, vmax=1, shading='auto')
    # with out:
        # clear_output(wait=True)
        # display(ax.figure)
update(None)

如果你这样做,你将只看到一个图形输出。这是因为当你在jupyter笔记本中使用%matplotlib inline后端时,任何在单元格中创建的图形都将在单元格运行完成时关闭并显示。(请参见此处核心matplotlib开发人员的解释: https://github.com/matplotlib/matplotlib/issues/18248#issuecomment-675177108)。因此,被黑色轮廓包围的图形来自于display(ax.figure),而下面的图形来自于之前创建的图形。


0
我之前遇到了完全相同的问题,我使用了%matplotlib notebook。不仅我可以得到单个图形,还可以进行交互(获取x、y位置,旋转和改变3D图的角度)。 enter image description here
import ipywidgets as widgets
%matplotlib notebook
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')

ax.set_xlim(0, xij.max())
ax.set_ylim(0, yij.max())
ax.set_zlim(min_value, max_value)

def update_plot(frame=0):
    ax.clear()
    ax.set_xlim(0, xij.max())
    ax.set_ylim(0, yij.max())
    ax.set_zlim(min_value, max_value)
    surf = ax.plot_surface(xij, yij, data[frame], cmap=cm.coolwarm,
                   linewidth=0, antialiased=False)
 
widgets.interact(update_plot, frame=widgets.IntSlider(min=0, max=100, step=1, value=0))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接