【问题标题】:Distinguish button_press_event from drag and zoom clicks in matplotlib将 button_press_event 与 matplotlib 中的拖动和缩放点击区分开来
【发布时间】:2018-01-25 15:24:26
【问题描述】:

我有一个显示两个子图的简单代码,并让用户在记录这些点击的x,y 坐标时左键单击第二个子图。

问题是单击选择要缩放的区域和拖动子图也被识别为左键。

有没有办法区分和过滤掉这些左键?

import numpy as np
import matplotlib.pyplot as plt


def onclick(event, ax):
    # Only clicks inside this axis are valid.
    if event.inaxes == ax:
        if event.button == 1:
            print(event.xdata, event.ydata)
            # Draw the click just made
            ax.scatter(event.xdata, event.ydata)
            ax.figure.canvas.draw()
        elif event.button == 2:
            # Do nothing
            print("scroll click")
        elif event.button == 3:
            # Do nothing
            print("right click")
        else:
            pass


fig, (ax1, ax2) = plt.subplots(1, 2)
# Plot some random scatter data
ax2.scatter(np.random.uniform(0., 10., 10), np.random.uniform(0., 10., 10))

fig.canvas.mpl_connect(
    'button_press_event', lambda event: onclick(event, ax2))
plt.show()

【问题讨论】:

    标签: python matplotlib


    【解决方案1】:

    您可以检查鼠标按钮是否在之前移动鼠标后被释放。由于对于缩放和平移,只有在之前没有发生任何移动时,您才可以调用该函数来绘制新点。

    import numpy as np
    import matplotlib.pyplot as plt
    
    class Click():
        def __init__(self, ax, func, button=1):
            self.ax=ax
            self.func=func
            self.button=button
            self.press=False
            self.move = False
            self.c1=self.ax.figure.canvas.mpl_connect('button_press_event', self.onpress)
            self.c2=self.ax.figure.canvas.mpl_connect('button_release_event', self.onrelease)
            self.c3=self.ax.figure.canvas.mpl_connect('motion_notify_event', self.onmove)
    
        def onclick(self,event):
            if event.inaxes == self.ax:
                if event.button == self.button:
                    self.func(event, self.ax)
        def onpress(self,event):
            self.press=True
        def onmove(self,event):
            if self.press:
                self.move=True
        def onrelease(self,event):
            if self.press and not self.move:
                self.onclick(event)
            self.press=False; self.move=False
    
    
    def func(event, ax):
        print(event.xdata, event.ydata)
        ax.scatter(event.xdata, event.ydata)
        ax.figure.canvas.draw()
    
    fig, (ax1, ax2) = plt.subplots(1, 2)
    # Plot some random scatter data
    ax2.scatter(np.random.uniform(0., 10., 10), np.random.uniform(0., 10., 10))
    click = Click(ax2, func, button=1)
    plt.show()
    

    【讨论】:

    • 根据我的经验,这在使用触控板时效果很好。然而,在实践中,当我用敏感的鼠标运行我的 tkinter 应用程序时,如果在释放按钮之前不加入微妙的动作,就很难点击。有什么建议吗?
    • @brandontober 考虑一下这个问题的其他答案?
    • 是的,我能做到。不过,我喜欢您的解决方案,并试图找到一种方法来使其发挥作用。
    • @brandontober 如果您愿意,您当然可以将最大时间条件的概念纳入此答案。
    【解决方案2】:

    我意识到这是一个老问题,但我也遇到了同样的问题,我想我找到了一个很好的解决方案;但是它目前仅适用于 Qt 后端(其他后端也可能存在类似的解决方案)。这个想法是 matplotlib 在缩放或平移时会改变光标的形状,所以你可以检查一下。这是修改后的代码:

    import numpy as np
    import matplotlib
    matplotlib.use('qt5agg')
    import matplotlib.pyplot as plt
    
    
    def onclick(event, ax):
        # Only clicks inside this axis are valid.
        try: # use try/except in case we are not using Qt backend
            zooming_panning = ( fig.canvas.cursor().shape() != 0 ) # 0 is the arrow, which means we are not zooming or panning.
        except:
            zooming_panning = False
        if zooming_panning: 
            print("Zooming or panning")
            return
        if event.inaxes == ax:
            if event.button == 1:
                print(event.xdata, event.ydata)
                # Draw the click just made
                ax.scatter(event.xdata, event.ydata)
                ax.figure.canvas.draw()
            elif event.button == 2:
                # Do nothing
                print("scroll click")
            elif event.button == 3:
                # Do nothing
                print("right click")
            else:
                pass
    
    
    fig, (ax1, ax2) = plt.subplots(1, 2)
    # Plot some random scatter data
    ax2.scatter(np.random.uniform(0., 10., 10), np.random.uniform(0., 10., 10))
    
    fig.canvas.mpl_connect(
        'button_press_event', lambda event: onclick(event, ax2))
    plt.show()
    
    

    【讨论】:

    • 这就像一个魅力。我的 Qt4 实现如此简单。
    • 使用 tk 后端,我目前正在测试 toolbar.mode(如果选择了平移/缩放按钮,则返回 True,否则返回 False),但不确定它是否支持,
    【解决方案3】:

    区分单击和拖动/缩放(右键单击或左键单击)的一种方法是测量按钮按下和按钮释放之间的时间,然后在按钮释放时执行操作,而不是按钮按。

    import numpy as np
    import matplotlib.pyplot as plt
    import time
    
    MAX_CLICK_LENGTH = 0.1 # in seconds; anything longer is a drag motion
    
    def onclick(event, ax):
        ax.time_onclick = time.time()
    
    def onrelease(event, ax):
        # Only clicks inside this axis are valid.
        if event.inaxes == ax:
            if event.button == 1 and ((time.time() - ax.time_onclick) < MAX_CLICK_LENGTH):
                print(event.xdata, event.ydata)
                # Draw the click just made
                ax.scatter(event.xdata, event.ydata)
                ax.figure.canvas.draw()
            elif event.button == 2:
                print("scroll click")
            elif event.button == 3:
                print("right click")
            else:
                pass
    
    
    fig, (ax1, ax2) = plt.subplots(1, 2)
    # Plot some random scatter data
    ax2.scatter(np.random.uniform(0., 10., 10), np.random.uniform(0., 10., 10))
    
    fig.canvas.mpl_connect('button_press_event', lambda event: onclick(event, ax2))
    fig.canvas.mpl_connect('button_release_event', lambda event: onrelease(event, ax2))
    plt.show()
    

    【讨论】:

    • 我选择这个答案是因为它看起来更容易实现。谢谢你们!
    • @ImportanceOfBeingErnest 的方法要优雅得多。我的是一个有效的黑客。我觉得你应该重新考虑。此外,他还为您提供了一个完整的课程,您只需将其放入您的代码中,它就会执行此操作(无论 func 最终做什么)。没有什么可以让您实施。
    • 我同意你的观点,我只是更喜欢你的“hack”,因为它根本不会破坏我现有的功能。如果你认为这个答案值得被接受,我会改变它。干杯。
    【解决方案4】:

    我的答案不适用于所有后端,但是,它适用于 jupyter notebook 后端 nbAgg,类似于@Rincewind 答案

    
    %matplotlib notebook
    import numpy as np
    import matplotlib.pyplot as plt
    import ipywidgets as wdg 
    
    def onclick(event, ax):
        # Only clicks inside this axis are valid.
        if event.inaxes != ax:
            return
        
        # Not adding points when zooming or paning
        ptr_type = str(fig.canvas.toolbar.cursor)
        pick = 'Cursors.POINTER'
        if ptr_type != pick:
            return
        
        if event.button == 1:
            print(event.xdata, event.ydata)
            # Draw the click just made
            ax.scatter(event.xdata, event.ydata)
            ax.figure.canvas.draw()
        elif event.button == 2:
            # Do nothing
            print("scroll click")
        elif event.button == 3:
            # Do nothing
            print("right click")
        else:
            pass
    
    
    fig, (ax1, ax2) = plt.subplots(1, 2)
    # Plot some random scatter data
    ax2.scatter(np.random.uniform(0., 10., 10), np.random.uniform(0., 10., 10))
    
    ka = fig.canvas.mpl_connect(
        'button_press_event', lambda event: onclick(event, ax2))
    plt.show()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-02-10
      • 2014-12-02
      • 1970-01-01
      • 1970-01-01
      • 2011-08-27
      • 1970-01-01
      • 1970-01-01
      • 2015-04-05
      相关资源
      最近更新 更多