简体   繁体   English

单击外部绘图范围时,如何使matplotlib注释消失?

[英]How to make matplotlib annotations disappear when clicking outside plot bounds?

I'm using the DataCursor code from unutbu to show annotations on a matplotlib figure when I click on a data point. 单击数据点时,我正在使用DataCursor代码在matplotlib figure上显示注释。 (I can't find the link to this for the life of me, and the one I used to cite in my code doesn't point to the right place...) The problem is, I want them to go away when I click outside the plot. (我一生都找不到与此相关的链接,而我以前在我的代码中引用的链接没有指向正确的位置...)问题是,我希望它们在我离开时消失在图外单击。

def fmt(x, xlabel, y, ylabel)
    return xlabel+': {x:0.0f}\n'.format(x = x)+ylabel+': {y:0.3f}'.format(y = y)

class DataCursor(object):
    """A data cursor widget that displays the x,y of a matplotlib artist/plot when it is selected"""

    def annotate(self, ax)
        """ Draws and hides the annotations box for the given axis "ax". """
        annotation = ax.annotate(self.formatter, xy = (0,0), ha = 'right',
                                 xytext = self.offsets, textcoords = 'offset points', va = 'bottom',
                                 bbox = dict(boxstyle = 'round,pad=0.5', fc = 'yellow', alpha = 0.5),
                                 arrowprops = dict(arrowstyle = '->', connectionstyle = 'arc3,rad=0'))
        annotation.set_visible(False)
        return annotation

    def __init__(self, artists, x, xlabel, y, ylabel, tolerance = 5, offsets = (-20,20), formatter = fmt, display_all = False):
        """Create the data cursor and connect it to the relevant figure.
        "artists" is the matplotlib artist or sequence of artists that will be selected.
        "tolerance" is the radius (in points) that the mouse click must be within to select the artist.
        "offsets" is a tuple of (x,y) offsets in points from the selected point to the displayed annotation box.
        "formatter" is a callback function which takes 2 numeric arguments and returns a string.
        "display_all" controls whether more than one annotation box will be shown if there are multiple axes. Only one will be shown per axis, regardless.
        """

        self._points = np.column_stack((x,y))
        self.xlabel = xlabel
        self.ylabel = ylabel

        self.formatter = formatter
        self.offsets = offsets
        self.display_all = display_all

        if not cbook.iterable(artists):
            artists = [artists]

        self.artists = artists
        self.axes = tuple(set(art.axes for art in self.artists))
        self.figures = tuple(set(ax.figure for ax in self.axes))

        self.annotations = {}
        for ax in self.axes:
            self.annotations[ax] = self.annotate(ax)

        for artist in self.artists:
            artist.set_picker(tolerance)

        for fig in self.figures:
            fig.canvas.mpl_connect('pick_event', self)


    def snap(self, x, y):
        """ Return the value of in self._points closest to (x,y). """
        values = self.points
        idx = np.nanargmin(((values - (x,y))**2).sum(axis = -1)
        return self._points[idx,:]


    def __call__(self, event):
        """ Intended to be called through "mpl_connect" above. """
        x, y = event.mouseevent.xdata, event.mouseevent.ydata
        annotation = self.annotations[event.artist.axes]

        if x is None:
            for ann in self.annotations.values():
                ann.set_visible(False)
                event.canvas.draw()

        if x is not None:
            if not self.display_all:
                for ann in self.annotations.values():
                    ann.set_visible(False)

            x, y = self.snap(x, y)

            annotation.xy = x, y
            annotation.set_text(self.formatter(x, self.xlabel, y, self.ylabel))
            annotation.set_visible(True)
            annotation.draggable(True)
            event.canvas.draw()

According to documentation, I could use a function in artist.set_picker(tolerance) instead of tolerance . 根据文件,我可以用一个函数artist.set_picker(tolerance) ,而不是tolerance How does this work? 这是如何运作的? The documentation doesn't do a great job of explaining this, and I'm having trouble finding examples. 该文档在解释这一点上做得不好,而且我在查找示例时遇到了麻烦。

The way the code works now, it will make the annotation disappear if I click outside the box, but within tolerance of a data point. 代码现在的工作方式,如果在框外单击,但在数据点的公差范围内,它将使注释消失。 This leaves me out of luck if there is no data within tolerance of the edge. 如果在边缘的公差范围内没有数据,这会让我很不走运。 How can I make it so that if I click anywhere in the gray area of the plot the annotation will disappear? 我如何才能做到这一点,以便在绘图的灰色区域中单击任何位置时注释将消失?

The code that I though would make it disappear: 我会使其消失的代码:

    def __call__(self, event):
        """ Intended to be called through "mpl_connect" above. """
        x, y = event.mouseevent.xdata, event.mouseevent.ydata
        annotation = self.annotations[event.artist.axes]

        if x is None:
            for ann in self.annotations.values():
                ann.set_visible(False)
                event.canvas.draw()

Your problem is that your event-handling function only listens to 'pick_event' which are inherently associated with an artist (AFAIK). 您的问题是事件处理函数仅监听与艺术家(AFAIK)固有相关的'pick_event'

To achieve what you want, you should add another listener that handles 'button_press_event' 要实现所需的功能,您应该添加另一个处理'button_press_event'侦听器

This is untested code, but it should give you an idea: 这是未经测试的代码,但应该可以给您一个提示:

# in the __init__ code, add:
fig.canvas.mpl_connect('button_press_event', self.clearAnn)

# in the class body:
def clearAnn(self, event):
    if event.inaxes is None:  # event.inaxes returns None if you click outside of the plot area, i.e. in the grey part surrounding the axes
        for ann in self.annotations.values():
            ann.set_visible(False)
            event.canvas.draw()

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM