Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/user-interface/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
User interface Matplotlib使用滚轮缩放绘图_User Interface_Plot_Matplotlib_Zooming - Fatal编程技术网

User interface Matplotlib使用滚轮缩放绘图

User interface Matplotlib使用滚轮缩放绘图,user-interface,plot,matplotlib,zooming,User Interface,Plot,Matplotlib,Zooming,当光标悬停在matplotlib绘图上时,是否可以绑定滚轮进行放大/缩小?这应该可以。滚动时,它会将图形重新居中于指针位置 import matplotlib.pyplot as plt def zoom_factory(ax,base_scale = 2.): def zoom_fun(event): # get the current x and y limits cur_xlim = ax.get_xlim() cur_ylim

当光标悬停在matplotlib绘图上时,是否可以绑定滚轮进行放大/缩小?

这应该可以。滚动时,它会将图形重新居中于指针位置

import matplotlib.pyplot as plt


def zoom_factory(ax,base_scale = 2.):
    def zoom_fun(event):
        # get the current x and y limits
        cur_xlim = ax.get_xlim()
        cur_ylim = ax.get_ylim()
        cur_xrange = (cur_xlim[1] - cur_xlim[0])*.5
        cur_yrange = (cur_ylim[1] - cur_ylim[0])*.5
        xdata = event.xdata # get event x location
        ydata = event.ydata # get event y location
        if event.button == 'up':
            # deal with zoom in
            scale_factor = 1/base_scale
        elif event.button == 'down':
            # deal with zoom out
            scale_factor = base_scale
        else:
            # deal with something that should never happen
            scale_factor = 1
            print event.button
        # set new limits
        ax.set_xlim([xdata - cur_xrange*scale_factor,
                     xdata + cur_xrange*scale_factor])
        ax.set_ylim([ydata - cur_yrange*scale_factor,
                     ydata + cur_yrange*scale_factor])
        plt.draw() # force re-draw

    fig = ax.get_figure() # get the figure of interest
    # attach the call back
    fig.canvas.mpl_connect('scroll_event',zoom_fun)

    #return the function
    return zoom_fun
假设您有一个axis对象
ax

 ax.plot(range(10))
 scale = 1.5
 f = zoom_factory(ax,base_scale = scale)
可选参数
base\u scale
允许您将比例因子设置为所需的值

确保随身携带一份
f
。回调使用弱引用,因此如果不保留
f
的副本,它可能会被垃圾收集

在写了这个答案之后,我觉得这实际上非常有用,并把它放在了一个


这个稍加修改的代码的目的是跟踪光标相对于新缩放中心的位置。这样,如果您在中心以外的点放大或缩小图片,您将保持在同一点上

谢谢大家,这些例子很有帮助。为了使用散点图,我不得不做一些修改,并用左键拖动添加了平移。希望有人会觉得这很有用

from matplotlib.pyplot import figure, show
import numpy

class ZoomPan:
    def __init__(self):
        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


    def zoom_factory(self, ax, base_scale = 2.):
        def zoom(event):
            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()

        fig = ax.get_figure() # get the figure of interest
        fig.canvas.mpl_connect('scroll_event', zoom)

        return zoom

    def pan_factory(self, ax):
        def onPress(event):
            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):
            self.press = None
            ax.figure.canvas.draw()

        def onMotion(event):
            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()

        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


fig = figure()

ax = fig.add_subplot(111, xlim=(0,1), ylim=(0,1), autoscale_on=False)

ax.set_title('Click to zoom')
x,y,s,c = numpy.random.rand(4,200)
s *= 200

ax.scatter(x,y,s,c)
scale = 1.1
zp = ZoomPan()
figZoom = zp.zoom_factory(ax, base_scale = scale)
figPan = zp.pan_factory(ax)
show()

非常感谢。这很有效。但是,对于比例不再是线性的图(例如,对数图),这会发生故障。我已经为此编写了一个新版本。我希望它能帮助别人

基本上,我放大了轴坐标,这些坐标被归一化为[0,1]。所以,如果我在x轴上放大两倍,我现在想在[25,.75]范围内。 我还添加了一个功能,仅当您位于x轴正上方或正下方时,才放大x;当您位于y轴正左侧或右侧时,才放大y。如果不需要,只需设置zoomx=True和zoomy=True并忽略If语句

对于希望了解matplotlib如何在不同坐标系之间转换的人,此参考非常有用:

此函数位于包含指向轴的指针(self.ax)的对象内

def缩放(自身,事件):
''此功能在滚动鼠标滚轮时缩放图像。
在绘图中滚动可缩放绘图。在屏幕上方或下方滚动
绘图滚动x轴。滚动到绘图的左侧或右侧
滚动y轴。模棱两可的地方什么也不会发生。
注意:如果将图扩展到子图,则需要添加额外的
检查以确保您不在任何其他绘图中。目前尚不清楚如何解决这一问题
去吧。
因为我们也希望它在loglog plot中工作,所以我们在axes中工作
坐标,并使用适当的缩放变换转换为数据
“限制”
x=事件.x
y=事件。y
#将像素转换为轴
tranP2A=self.ax.transAxes.inversed().transform
#将轴转换为数据限制
tranA2D=self.ax.transLimits.inversed().transform
#转换比例(对于对数图)
tranSclA2D=self.ax.transScale.inversed().transform
如果event.button==“向下”:
#处理放大
比例系数=自缩放比例
elif event.button==“向上”:
#处理缩小
比例系数=1/self.zoom\u比例
其他:
#处理一些不应该发生的事情
比例系数=1
#获取轴的位置,以了解相对于轴的位置
xa,ya=tranP2A((x,y))
zoomx=False
zoomy=False
如果(ya<0):
如果(xa>=0和xa我真的很喜欢图中的“仅x”或“仅y”模式。您可以绑定x和y键,以便仅在一个方向上进行缩放。请注意,如果单击输入框或其他东西,您可能还必须将焦点放回画布上-

canvas.mpl\u connect('button\u press\u event',lambda event:canvas.\u tkcanvas.focus\u set())

修改后的代码的其余部分如下所示:

from matplotlib.pyplot import figure, show
import numpy

class ZoomPan:
    def __init__(self):
        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.xzoom = True
        self.yzoom = True
        self.cidBP = None
        self.cidBR = None
        self.cidBM = None
        self.cidKeyP = None
        self.cidKeyR = None
        self.cidScroll = None

    def zoom_factory(self, ax, base_scale = 2.):
        def zoom(event):
            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(xdata is None):
                return()
            if(ydata is None):
                return()

            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])

            if(self.xzoom):
                ax.set_xlim([xdata - new_width * (1-relx), xdata + new_width * (relx)])
            if(self.yzoom):
                ax.set_ylim([ydata - new_height * (1-rely), ydata + new_height * (rely)])
            ax.figure.canvas.draw()
            ax.figure.canvas.flush_events()

        def onKeyPress(event):
            if event.key == 'x':
                self.xzoom = True
                self.yzoom = False
            if event.key == 'y':
                self.xzoom = False
                self.yzoom = True

        def onKeyRelease(event):
            self.xzoom = True
            self.yzoom = True

        fig = ax.get_figure() # get the figure of interest

        self.cidScroll = fig.canvas.mpl_connect('scroll_event', zoom)
        self.cidKeyP = fig.canvas.mpl_connect('key_press_event',onKeyPress)
        self.cidKeyR = fig.canvas.mpl_connect('key_release_event',onKeyRelease)

        return zoom

    def pan_factory(self, ax):
        def onPress(event):
            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):
            self.press = None
            ax.figure.canvas.draw()

        def onMotion(event):
            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.figure.canvas.flush_events()

        fig = ax.get_figure() # get the figure of interest

        self.cidBP = fig.canvas.mpl_connect('button_press_event',onPress)
        self.cidBR = fig.canvas.mpl_connect('button_release_event',onRelease)
        self.cidBM = fig.canvas.mpl_connect('motion_notify_event',onMotion)
        # attach the call back

        #return the function
        return onMotion

这是对上面代码的一个轻微修改的建议-它使缩放更易于管理

    cur_xrange = (cur_xlim[1] - cur_xlim[0])*.5
    cur_yrange = (cur_ylim[1] - cur_ylim[0])*.5
    xmouse = event.xdata # get event x location                                                                                                                                                                                                                            
    ymouse = event.ydata # get event y location                                                                                                                                                                                                                            
    cur_xcentre = (cur_xlim[1] + cur_xlim[0])*.5
    cur_ycentre = (cur_ylim[1] + cur_ylim[0])*.5
    xdata = cur_xcentre+ 0.25*(xmouse-cur_xcentre)
    ydata = cur_ycentre+ 0.25*(ymouse-cur_ycentre)

让塔卡斯韦尔的回答“流畅”

def zoom_工厂(ax,base_scale=2):
prex=0
猎物=0
预扩展数据=0
preydata=0
def zoom_fun(活动):
非局部prex,猎物,prexdata,preydata
curx=event.x
cury=event.y
#如果没有更改鼠标位置(或更改得太少)
#保持预缩放中心
如果abs(curx-prex)<10且abs(cury-prex)<10:
#不变
扩展数据=预扩展数据
ydata=前ydata
#如果更改了鼠标位置,还可以更改当前比例中心
其他:
#改变
扩展数据=event.xdata#获取事件x位置
ydata=event.ydata#获取事件y位置
#更新以前的位置数据
prex=event.x
猎物=event.y
预扩展数据=扩展数据
preydata=ydata
#获取当前的x和y限制
cur_xlim=ax.get_xlim()
cur_ylim=ax.get_ylim()
cur_xrange=(cur_xlim[1]-cur_xlim[0])*。5
cur_yrange=(cur_ylim[1]-cur_ylim[0])*。5
#log.debug((扩展数据,ydata))
如果event.button==“向上”:
#处理放大
比例系数=1/基准比例
elif event.button==“向下”:
#处理缩小
比例系数=基准比例
其他:
#处理一些不应该发生的事情
比例系数=1
打印(事件按钮)
#设定新的限制
ax.set\u xlim([
扩展数据-电流范围*比例系数,
扩展数据+电流范围*比例系数
])
ax.set_ylim([
ydata-电流范围*比例系数,
ydata+电流范围*比例系数
])
plt.draw()#强制重新绘制
fig=ax.get_figure()#获取感兴趣的数字
#接回电话
图canvas.mpl\u connect('scroll\u event',zoom\u fun)
#返回函数
返回zoom_fun

使用
ax.set\u xlim()
ax.set\u ylim()
的其他答案并没有为figure提供令人满意的用户体验
from matplotlib.pyplot import figure, show
import numpy

class ZoomPan:
    def __init__(self):
        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.xzoom = True
        self.yzoom = True
        self.cidBP = None
        self.cidBR = None
        self.cidBM = None
        self.cidKeyP = None
        self.cidKeyR = None
        self.cidScroll = None

    def zoom_factory(self, ax, base_scale = 2.):
        def zoom(event):
            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(xdata is None):
                return()
            if(ydata is None):
                return()

            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])

            if(self.xzoom):
                ax.set_xlim([xdata - new_width * (1-relx), xdata + new_width * (relx)])
            if(self.yzoom):
                ax.set_ylim([ydata - new_height * (1-rely), ydata + new_height * (rely)])
            ax.figure.canvas.draw()
            ax.figure.canvas.flush_events()

        def onKeyPress(event):
            if event.key == 'x':
                self.xzoom = True
                self.yzoom = False
            if event.key == 'y':
                self.xzoom = False
                self.yzoom = True

        def onKeyRelease(event):
            self.xzoom = True
            self.yzoom = True

        fig = ax.get_figure() # get the figure of interest

        self.cidScroll = fig.canvas.mpl_connect('scroll_event', zoom)
        self.cidKeyP = fig.canvas.mpl_connect('key_press_event',onKeyPress)
        self.cidKeyR = fig.canvas.mpl_connect('key_release_event',onKeyRelease)

        return zoom

    def pan_factory(self, ax):
        def onPress(event):
            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):
            self.press = None
            ax.figure.canvas.draw()

        def onMotion(event):
            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.figure.canvas.flush_events()

        fig = ax.get_figure() # get the figure of interest

        self.cidBP = fig.canvas.mpl_connect('button_press_event',onPress)
        self.cidBR = fig.canvas.mpl_connect('button_release_event',onRelease)
        self.cidBM = fig.canvas.mpl_connect('motion_notify_event',onMotion)
        # attach the call back

        #return the function
        return onMotion
    cur_xrange = (cur_xlim[1] - cur_xlim[0])*.5
    cur_yrange = (cur_ylim[1] - cur_ylim[0])*.5
    xmouse = event.xdata # get event x location                                                                                                                                                                                                                            
    ymouse = event.ydata # get event y location                                                                                                                                                                                                                            
    cur_xcentre = (cur_xlim[1] + cur_xlim[0])*.5
    cur_ycentre = (cur_ylim[1] + cur_ylim[0])*.5
    xdata = cur_xcentre+ 0.25*(xmouse-cur_xcentre)
    ydata = cur_ycentre+ 0.25*(ymouse-cur_ycentre)
def mousewheel_move( event):
    ax=event.inaxes
    ax._pan_start = types.SimpleNamespace(
            lim=ax.viewLim.frozen(),
            trans=ax.transData.frozen(),
            trans_inverse=ax.transData.inverted().frozen(),
            bbox=ax.bbox.frozen(),
            x=event.x,
            y=event.y)
    if event.button == 'up':
        ax.drag_pan(3, event.key, event.x+10, event.y+10)
    else: #event.button == 'down':
        ax.drag_pan(3, event.key, event.x-10, event.y-10)
    fig=ax.get_figure()
    fig.canvas.draw_idle()
fig.canvas.mpl_connect('scroll_event',mousewheel_move)
    def __init(self):
        ...

        self.cid_zoom = self.canvas.mpl_connect('scroll_event', self.zoom)

    def zoom(self, event):
        if event.inaxes == self.ax:
            scale_factor = np.power(self.zoom_factor, -event.step)*event.step
            self.ax.get_xaxis().zoom(scale_factor)
            self.ax.get_yaxis().zoom(scale_factor)
            self.ax.invert_yaxis()
            self.canvas.draw_idle()
    def __init(self):
        ...

        self.cid_motion = self.canvas.mpl_connect(
            'motion_notify_event', self.pan_move
        )
        self.cid_button = self.canvas.mpl_connect(
            'button_press_event', self.pan_press
        )


    def pan_press(self, event):
        if event.inaxes == self.ax:
            self.x_press = event.xdata
            self.y_press = event.ydata

    def pan_move(self, event):
        if event.button == 1 and event.inaxes == self.ax:
            xdata = event.xdata
            ydata = event.ydata
            dx = (xdata - self.x_press)/np.diff(self.ax.get_xlim())
            dy = (ydata - self.y_press)/np.diff(self.ax.get_ylim())
            self.ax.get_xaxis().pan(-dx)
            self.ax.get_yaxis().pan(-dy)
            self.ax.drag_pan(event.button, event.key, dx, dy)
            self.canvas.draw()