有没有一个类似于MATLAB的datacursormode的matplotlib替代品?

46
在MATLAB中,用户可以使用 datacursormode 在鼠标悬停时向图形添加注释。在matplotlib中是否有类似的功能?或者我需要使用 matplotlib.text.Annotation 编写自己的事件?
1个回答

64

最新编辑/自己插一脚: 现在可以通过mpldatacursor来使用此功能(并带有更多的功能)。只需调用mpldatacursor.datacursor()即可为所有matplotlib图形对象启用它(包括对图像中z值的基本支持等)。


据我所知,还没有实现这个功能的库,但编写类似的代码并不难:

import matplotlib.pyplot as plt

class DataCursor(object):
    text_template = 'x: %0.2f\ny: %0.2f'
    x, y = 0.0, 0.0
    xoffset, yoffset = -20, 20
    text_template = 'x: %0.2f\ny: %0.2f'

    def __init__(self, ax):
        self.ax = ax
        self.annotation = ax.annotate(self.text_template, 
                xy=(self.x, self.y), xytext=(self.xoffset, self.yoffset), 
                textcoords='offset points', ha='right', va='bottom',
                bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.5),
                arrowprops=dict(arrowstyle='->', connectionstyle='arc3,rad=0')
                )
        self.annotation.set_visible(False)

    def __call__(self, event):
        self.event = event
        # xdata, ydata = event.artist.get_data()
        # self.x, self.y = xdata[event.ind], ydata[event.ind]
        self.x, self.y = event.mouseevent.xdata, event.mouseevent.ydata
        if self.x is not None:
            self.annotation.xy = self.x, self.y
            self.annotation.set_text(self.text_template % (self.x, self.y))
            self.annotation.set_visible(True)
            event.canvas.draw()

fig = plt.figure()
line, = plt.plot(range(10), 'ro-')
fig.canvas.mpl_connect('pick_event', DataCursor(plt.gca()))
line.set_picker(5) # Tolerance in points

matplotlib中的数据游标状物

似乎至少有一些人在使用这个,我已经添加了下面更新过的版本。

新版本使用起来更简单,文档也更详细(至少有一点点)。

基本上你可以像这样使用它:

plt.figure()
plt.subplot(2,1,1)
line1, = plt.plot(range(10), 'ro-')
plt.subplot(2,1,2)
line2, = plt.plot(range(10), 'bo-')

DataCursor([line1, line2])

plt.show()
主要区别在于:a)不需要手动调用line.set_picker(...),b)不需要手动调用fig.canvas.mpl_connect,c)此版本处理多个轴和多个图形。
from matplotlib import cbook

class DataCursor(object):
    """A simple data cursor widget that displays the x,y location of a
    matplotlib artist when it is selected."""
    def __init__(self, artists, tolerance=5, offsets=(-20, 20), 
                 template='x: %0.2f\ny: %0.2f', 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
        "template" is the format string to be used. Note: For compatibility
            with older versions of python, this uses the old-style (%) 
            formatting specification.
        "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.template = template
        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 annotate(self, ax):
        """Draws and hides the annotation box for the given axis "ax"."""
        annotation = ax.annotate(self.template, 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 __call__(self, event):
        """Intended to be called through "mpl_connect"."""
        # Rather than trying to interpolate, just display the clicked coords
        # This will only be called if it's within "tolerance", anyway.
        x, y = event.mouseevent.xdata, event.mouseevent.ydata
        annotation = self.annotations[event.artist.axes]
        if x is not None:
            if not self.display_all:
                # Hide any other annotation boxes...
                for ann in self.annotations.values():
                    ann.set_visible(False)
            # Update the annotation in the current axis..
            annotation.xy = x, y
            annotation.set_text(self.template % (x, y))
            annotation.set_visible(True)
            event.canvas.draw()

if __name__ == '__main__':
    import matplotlib.pyplot as plt
    plt.figure()
    plt.subplot(2,1,1)
    line1, = plt.plot(range(10), 'ro-')
    plt.subplot(2,1,2)
    line2, = plt.plot(range(10), 'bo-')

    DataCursor([line1, line2])

    plt.show()

Joe,我注释掉了xdata, ydata = event.artist.get_data(),因为它似乎没有被使用,并提出了一个问题(https://dev59.com/sl_Va4cB1Zd3GeqPUYoC)。希望这没问题。 - unutbu
当然,谢谢!我本不应该把它留在那里。而且,我应该更新这个...传入一个特定的艺术家而不是一个轴可能更有意义。 - Joe Kington
2
你可以通过 artist.axes 访问它。稍等一下,我会添加它的。如果人们发现它有用,我可能会尝试将新的、清理过的版本提交到 matplotlib.widgets 中进行包含。我不知道开发人员是否认为这是一个好主意,但我想无论如何都会问一下。 - Joe Kington
1
这对于使用imshow显示的2D图像有效吗?即它是否显示所显示图像的z值?目前,查看器仅显示x和y坐标,这有些不幸... - rubenvb
@rubenvb - 请看这里的 mpldatacursor.ImageDataCursor: https://github.com/joferkington/mpldatacursor - Joe Kington
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接