You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Matplotlib子图独立缩放功能实现问题求助

解决Matplotlib多子图独立缩放的问题

我帮你找到了问题的根源,并且修正了代码,现在两个子图可以各自独立缩放和平移了!

问题分析

你的代码里有两个关键问题导致同步缩放:

  1. 在zoom_factory方法中,你重复绑定了滚动事件到整个figure(fig.canvas.mpl_connect('scroll_event', zoom)),加上之前的self.ax.canvas.mpl_connect,相当于每个子图的缩放逻辑都被绑定了两次,而且没有判断事件来源。
  2. 缩放函数zoom里没有检查事件是否发生在当前子图内,所以不管鼠标在哪个子图滚动,两个子图的缩放逻辑都会触发。

修正后的完整代码

from matplotlib.pyplot import figure, show
import matplotlib.pyplot as plt
import numpy

class ZoomPan:
    def __init__(self,ax):
        self.press = None
        self.cur_xlim = None
        self.cur_ylim = None
        self.x0 = None
        self.y0 = None
        self.x1 = None
        self.y1 = None
        self.xpress = None
        self.ypress = None
        self.ax = ax

    def zoom_factory(self, base_scale = 2.):
        def zoom(event):
            # 关键:只处理当前子图内的滚动事件
            if event.inaxes != self.ax:
                return
            
            ax = self.ax
            cur_xlim = ax.get_xlim()
            cur_ylim = ax.get_ylim()

            xdata = event.xdata # get event x location
            ydata = event.ydata # get event y location

            if event.button == 'down':
                # deal with zoom in
                scale_factor = 1 / base_scale
            elif event.button == 'up':
                # deal with zoom out
                scale_factor = base_scale
            else:
                # deal with something that should never happen
                scale_factor = 1
                print(event.button)
            new_width = (cur_xlim[1] - cur_xlim[0]) * scale_factor
            new_height = (cur_ylim[1] - cur_ylim[0]) * scale_factor

            relx = (cur_xlim[1] - xdata)/(cur_xlim[1] - cur_xlim[0])
            rely = (cur_ylim[1] - ydata)/(cur_ylim[1] - cur_ylim[0])

            ax.set_xlim([xdata - new_width * (1-relx), xdata + new_width * (relx)])
            ax.set_ylim([ydata - new_height * (1-rely), ydata + new_height * (rely)])
            ax.figure.canvas.draw()

        ax = self.ax
        # 只绑定一次到canvas,并且后续通过event.inaxes判断子图
        self.ax.canvas.mpl_connect('scroll_event',zoom)
        # 移除重复的figure绑定
        # fig.canvas.mpl_connect('scroll_event', zoom)
        return zoom

    def pan_factory(self):
        def onPress(event):
            ax = self.ax
            if event.inaxes != ax:
                return
            self.cur_xlim = ax.get_xlim()
            self.cur_ylim = ax.get_ylim()
            self.press = self.x0, self.y0, event.xdata, event.ydata
            self.x0, self.y0, self.xpress, self.ypress = self.press

        def onRelease(event):
            ax = self.ax
            self.press = None
            ax.figure.canvas.draw()

        def onMotion(event):
            ax = self.ax
            if self.press is None:
                return
            if event.inaxes != ax:
                return

            dx = event.xdata - self.xpress
            dy = event.ydata - self.ypress

            self.cur_xlim -= dx
            self.cur_ylim -= dy

            ax.set_xlim(self.cur_xlim)
            ax.set_ylim(self.cur_ylim)
            ax.figure.canvas.draw()

        ax = self.ax
        fig = ax.get_figure() # get the figure of interest
        # attach the call back
        fig.canvas.mpl_connect('button_press_event',onPress)
        fig.canvas.mpl_connect('button_release_event',onRelease)
        fig.canvas.mpl_connect('motion_notify_event',onMotion)
        #return the function
        return onMotion

def setscatter(scatter_label,ax):
    ax.set_title(scatter_label)
    x,y,s,c = numpy.random.rand(4,200)
    s *= 200
    ax.scatter(x,y,s,c)
    scale = 1.1
    zp = ZoomPan(ax)
    zp.zoom_factory(base_scale=scale)
    # 绑定平移功能确保生效
    zp.pan_factory()

def main():
    fig, axs = plt.subplots(nrows=1,ncols=2)
    setscatter('ax',axs[0])
    setscatter('bx',axs[1])
    fig.tight_layout()
    plt.show()

if __name__ == '__main__':
    main()

关键修改点说明

  1. 添加事件来源判断:在zoom函数开头加入if event.inaxes != self.ax: return,这样只有当鼠标滚动事件发生在当前子图内时,才会执行缩放逻辑,避免影响其他子图。
  2. 移除重复的事件绑定:删掉fig.canvas.mpl_connect('scroll_event', zoom)这行,因为self.ax.canvas已经指向了figure的canvas,重复绑定会导致同一个事件触发多次回调。
  3. 补充平移功能绑定:在setscatter函数里添加了zp.pan_factory(),确保平移功能稳定生效。

现在运行代码,两个子图就可以各自独立缩放和平移了,鼠标在哪个子图上操作,就只会影响那个子图~

内容的提问来源于stack exchange,提问作者chapter3

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.27 17:22:34