使用matplotlib widget时ipywidgets VBox内控件显示顺序异常如何解决
问题根因
这个问题是%matplotlib widget模式的默认交互渲染逻辑导致的:默认开启交互模式时,新建的matplotlib画布会尝试自动挂载到当前活跃的输出上下文,而画布的初始化渲染是异步的。你显示VBox、再更新widget_03内容时,widget_02的画布才完成初始化,此时活跃上下文已经切换,就会出现画布挂载位置错误,看起来像是VBox排列顺序被打乱。
解决方法
方案1:限制matplotlib画布仅渲染到指定Output上下文
在widget_02的Output上下文内绘图时,临时关闭matplotlib自动显示逻辑,手动控制画布的渲染位置,修复后的代码如下:
import IPython.display from IPython.display import display from ipywidgets import widgets, Layout %matplotlib widget import matplotlib as mpl import matplotlib.pyplot as plt widget_01 = widgets.HTML("<p>Hello, world</p>") widget_02 = widgets.Output(layout=Layout(width='100%')) widget_03 = widgets.Output(layout=Layout(width='100%')) with widget_02: IPython.display.clear_output(True) # 临时关闭自动显示,避免画布挂载到错误位置 with plt.ioff(): fig = plt.figure(figsize=(10,1), dpi=90) fig.canvas.toolbar_visible = False ax = fig.add_subplot(111) ax.plot([0,1,2], [0,1,2]) # 手动将画布渲染到当前Output display(fig.canvas) myvbox = widgets.VBox([ widget_01, widget_02, widget_03 ],) display(myvbox) with widget_03: IPython.display.clear_output(True) display(widgets.HTMLMath("<p>In the next episode...</p>"))
方案2:直接将matplotlib画布作为组件加入VBox
不需要用Output组件包裹matplotlib画布,fig.canvas本身就是标准ipywidgets组件,可以直接加入VBox的子组件列表,完全规避上下文挂载错误的问题,性能也更优,示例如下:
from IPython.display import display from ipywidgets import widgets, Layout %matplotlib widget import matplotlib.pyplot as plt widget_01 = widgets.HTML("<p>Hello, world</p>") # 直接创建画布作为中间组件,不需要套Output with plt.ioff(): fig = plt.figure(figsize=(10,1), dpi=90) fig.canvas.toolbar_visible = False ax = fig.add_subplot(111) ax.plot([0,1,2], [0,1,2]) widget_02 = fig.canvas widget_03 = widgets.Output(layout=Layout(width='100%')) myvbox = widgets.VBox([ widget_01, widget_02, widget_03 ],) display(myvbox) with widget_03: IPython.display.clear_output(True) display(widgets.HTMLMath("<p>In the next episode...</p>"))
两种方案都可以保证matplotlib组件的排列顺序和VBox子组件列表的顺序完全一致,后续更新VBox内其他组件的内容也不会打乱排序。
内容的提问来源于stack exchange,提问作者sdbbs
相关产品推荐
相关产品推荐

