如何解决Matplotlib中轴外Artist的Blit重绘失效问题?
解决Matplotlib带轴外标注的Blit十字光标失效问题
问题场景
基于Matplotlib实现的Blit十字光标,当右侧轴的Y值标注(文本Artist)超出轴区域时,Blit技术失效;若每次鼠标移动调用canvas.draw(),因图表数据量大,每次绘制耗时0.5-0.7秒,光标操作严重卡顿。根源在于保存背景时仅使用ax.bbox,只覆盖轴内区域,轴外部分未被纳入背景缓存,导致Blit无法正确清除旧标注。
解决方案
核心是扩展背景缓存的范围,覆盖标注可能出现的轴外区域,具体修改如下:
import matplotlib.pyplot as plt import numpy as np class BlittedCursor: """ A cross hair cursor using blitting for faster redraw. """ def __init__(self, ax): self.ax = ax self.background = None self.horizontal_line = ax.axhline(color='k', lw=0.8, ls='--') self.vertical_line = ax.axvline(color='k', lw=0.8, ls='--') # text location in axes coordinates self.text = ax.text(0.95, 0, '', transform=ax.get_yaxis_transform()) self._creating_background = False ax.figure.canvas.mpl_connect('draw_event', self.on_draw) def on_draw(self, event): self.create_new_background() def set_cross_hair_visible(self, visible): need_redraw = self.horizontal_line.get_visible() != visible self.horizontal_line.set_visible(visible) self.vertical_line.set_visible(visible) self.text.set_visible(visible) return need_redraw def create_new_background(self): if self._creating_background: # discard calls triggered from within this function return self._creating_background = True self.set_cross_hair_visible(False) self.ax.figure.canvas.draw() # 修改:保存整个画布的背景,而非仅轴区域 self.background = self.ax.figure.canvas.copy_from_bbox( self.ax.figure.canvas.get_window_extent() ) self.set_cross_hair_visible(True) self._creating_background = False def on_mouse_move(self, event): if self.background is None: self.create_new_background() if not event.inaxes: need_redraw = self.set_cross_hair_visible(False) if need_redraw: self.ax.figure.canvas.restore_region(self.background) # 修改:Blit整个画布范围 self.ax.figure.canvas.blit(self.ax.figure.canvas.get_window_extent()) else: self.set_cross_hair_visible(True) # update the line positions x, y = event.xdata, event.ydata self.horizontal_line.set_ydata(y) self.vertical_line.set_xdata(x) self.text.set_text('y=%1.2f' % (y)) self.text.set_y(y) self.ax.figure.canvas.restore_region(self.background) self.ax.draw_artist(self.horizontal_line) self.ax.draw_artist(self.vertical_line) self.ax.draw_artist(self.text) # 修改:Blit整个画布范围 self.ax.figure.canvas.blit(self.ax.figure.canvas.get_window_extent()) x = np.arange(0, 1, 0.01) y = np.sin(2 * 2 * np.pi * x) fig, ax = plt.subplots() ax.set_title('Blitted cursor') ax.plot(x, y, 'o') blitted_cursor = BlittedCursor(ax) fig.canvas.mpl_connect('motion_notify_event', blitted_cursor.on_mouse_move) plt.show()
关键修改说明
- 将背景缓存的范围从
ax.bbox(仅轴内区域)改为canvas.get_window_extent()(整个画布区域),确保轴外的标注区域也被纳入背景缓存 - Blit更新时同步使用整个画布范围,保证旧标注能被背景正确覆盖,新标注正常绘制
内容的提问来源于stack exchange,提问作者Beginner
相关产品推荐
相关产品推荐

