如何阻止IPython笔记本在matplotlib交互图滚动时整体滚动?
阻止IPython笔记本中绘图区域滚动时触发整体页面滚动
问题场景
在VS Code中使用%matplotlib widget后端,基于matplotlib图像切片查看器示例实现了鼠标滚动切换图像切片的功能,但鼠标悬停在绘图区域滚动时,整个笔记本页面也会同步滚动,导致光标移出绘图框,影响操作体验。
原实现代码如下:
import numpy as np import matplotlib.pyplot as plt import matplotlib as mpl # Fixing random state for reproducibility np.random.seed(19680801) class IndexTracker: def __init__(self, ax, X): self.ax = ax ax.set_title('use scroll wheel to navigate images') self.X = X rows, cols, self.slices = X.shape self.ind = self.slices//2 self.im = ax.imshow(self.X[:, :, self.ind]) self.update() def on_scroll(self, event): print("%s %s" % (event.button, event.step)) if event.button == 'up': self.ind = (self.ind + 1) % self.slices else: self.ind = (self.ind - 1) % self.slices self.update() def update(self): self.im.set_data(self.X[:, :, self.ind]) self.ax.set_ylabel('slice %s' % self.ind) self.im.axes.figure.canvas.draw_idle() def plot(X): mpl.rc('image', cmap='gray') fig, ax = plt.subplots(1, 1) tracker = IndexTracker(ax, X) # 原代码此处存在笔误,应为 tracker.on_scroll fig.canvas.mpl_connect('scroll_event', tracker.on_scroll) plt.show()
调用代码:
X = np.random.rand(20, 20, 40) plot(X)
解决方案
核心思路是阻止浏览器对绘图区域滚动事件的默认处理(即页面滚动),以下提供两种可行方案:
方案一:通过ipympl canvas对象绑定事件阻止默认行为
修改IndexTracker类,在滚动事件处理中添加阻止默认行为的逻辑,仅对当前绘图的canvas生效:
import numpy as np import matplotlib.pyplot as plt import matplotlib as mpl np.random.seed(19680801) class IndexTracker: def __init__(self, ax, X): self.ax = ax ax.set_title('use scroll wheel to navigate images') self.X = X rows, cols, self.slices = X.shape self.ind = self.slices//2 self.im = ax.imshow(self.X[:, :, self.ind]) self.canvas = ax.figure.canvas # 绑定滚动事件 self.canvas.mpl_connect('scroll_event', self.on_scroll) self.update() def on_scroll(self, event): print("%s %s" % (event.button, event.step)) if event.button == 'up': self.ind = (self.ind + 1) % self.slices else: self.ind = (self.ind - 1) % self.slices self.update() # 阻止浏览器默认滚动行为 self.canvas.widget.canvas.call_method('preventDefault') def update(self): self.im.set_data(self.X[:, :, self.ind]) self.ax.set_ylabel('slice %s' % self.ind) self.im.axes.figure.canvas.draw_idle() def plot(X): mpl.rc('image', cmap='gray') fig, ax = plt.subplots(1, 1) tracker = IndexTracker(ax, X) plt.show()
方案二:全局JavaScript监听阻止所有绘图区域的默认滚动
如果需要对笔记本中所有matplotlib widget绘图生效,可以执行以下JavaScript代码:
from IPython.display import display, Javascript # 对所有matplotlib widget的canvas绑定滚动阻止事件 display(Javascript(''' document.querySelectorAll('.matplotlib-widget canvas').forEach(canvas => { canvas.addEventListener('wheel', (e) => { e.preventDefault(); }); }); '''))
注意事项
- 必须使用
%matplotlib widget后端,因为该后端的绘图基于HTML5 canvas,才能通过JavaScript控制事件行为。 - 原代码中事件绑定存在笔误,需将
plot.scroll改为tracker.on_scroll才能正确绑定滚动事件。
内容的提问来源于stack exchange,提问作者akhtarm
相关产品推荐
相关产品推荐

