【问题标题】:Hiding lines after showing a pyplot figure显示 pyplot 图后隐藏线条
【发布时间】:2015-10-03 07:00:27
【问题描述】:

我正在使用 pyplot 来显示最多 30 行的折线图。我想添加一种方法来快速显示和隐藏图表上的单条线。 Pyplot 确实有一个菜单,您可以在其中编辑线条属性以更改颜色或样式,但是当您想隐藏线条以隔离您感兴趣的线条时,它相当笨重。理想情况下,我想在图例上使用复选框显示和隐藏线条。 (类似于在 Paint.Net 等图像编辑器中显示和隐藏图层)我不确定 pyplot 是否可行,所以我对其他模块持开放态度,只要它们易于分发。

【问题讨论】:

  • 只是为了丰富您的问题,您可以在gnuplot中找到这种行为(通过单击图例字幕选择将在图表上显示的行),例如gnuplot.sourceforge.net/demo_svg_5.0/simple.html我不是确定您有多愿意从 pyplot 更改为另一个工具(gnuplot),但如果您想跟上 python 并使用 gnuplot,则有一个“gnuplot.py”界面。
  • 这正是我正在寻找的功能。我会深入研究它,看看它在我的程序中的效果如何。
  • 更新:我放弃了 gnuplot。对我来说效果不佳。

标签: python matplotlib


【解决方案1】:

如果您愿意,您可以将回调连接到图例,当它们被点击时将显示/隐藏线条。这里有一个简单的例子:http://matplotlib.org/examples/event_handling/legend_picking.html

这是一个“更高级”的示例,无需手动指定线条和图例标记之间的关系(还有更多功能)。

(2019 年 8 月的更新版本,作为对有关此功能无法正常工作的重复报告的回应;现在应该!对于旧版本,请参阅 version history

import numpy as np
import matplotlib.pyplot as plt


def main():
    x = np.arange(10)
    fig, ax = plt.subplots()
    for i in range(1, 31):
        ax.plot(x, i * x, label=r'$y={}x$'.format(i))

    ax.legend(loc='upper left', bbox_to_anchor=(1.05, 1),
              ncol=2, borderaxespad=0)
    fig.subplots_adjust(right=0.55)
    fig.suptitle('Right-click to hide all\nMiddle-click to show all',
                 va='top', size='large')

    leg = interactive_legend()
    return fig, ax, leg

def interactive_legend(ax=None):
    if ax is None:
        ax = plt.gca()
    if ax.legend_ is None:
        ax.legend()

    return InteractiveLegend(ax.get_legend())

class InteractiveLegend(object):
    def __init__(self, legend):
        self.legend = legend
        self.fig = legend.axes.figure

        self.lookup_artist, self.lookup_handle = self._build_lookups(legend)
        self._setup_connections()

        self.update()

    def _setup_connections(self):
        for artist in self.legend.texts + self.legend.legendHandles:
            artist.set_picker(10) # 10 points tolerance

        self.fig.canvas.mpl_connect('pick_event', self.on_pick)
        self.fig.canvas.mpl_connect('button_press_event', self.on_click)

    def _build_lookups(self, legend):
        labels = [t.get_text() for t in legend.texts]
        handles = legend.legendHandles
        label2handle = dict(zip(labels, handles))
        handle2text = dict(zip(handles, legend.texts))

        lookup_artist = {}
        lookup_handle = {}
        for artist in legend.axes.get_children():
            if artist.get_label() in labels:
                handle = label2handle[artist.get_label()]
                lookup_handle[artist] = handle
                lookup_artist[handle] = artist
                lookup_artist[handle2text[handle]] = artist

        lookup_handle.update(zip(handles, handles))
        lookup_handle.update(zip(legend.texts, handles))

        return lookup_artist, lookup_handle

    def on_pick(self, event):
        handle = event.artist
        if handle in self.lookup_artist:

            artist = self.lookup_artist[handle]
            artist.set_visible(not artist.get_visible())
            self.update()

    def on_click(self, event):
        if event.button == 3:
            visible = False
        elif event.button == 2:
            visible = True
        else:
            return

        for artist in self.lookup_artist.values():
            artist.set_visible(visible)
        self.update()

    def update(self):
        for artist in self.lookup_artist.values():
            handle = self.lookup_handle[artist]
            if artist.get_visible():
                handle.set_visible(True)
            else:
                handle.set_visible(False)
        self.fig.canvas.draw()

    def show(self):
        plt.show()

if __name__ == '__main__':
    fig, ax, leg = main()
    plt.show()

这允许您单击图例项目以打开/关闭其相应的艺术家。例如,您可以从这里开始:

到这里:

【讨论】:

  • 这正是我正在寻找的功能!我试图找到奇怪的解决方法,例如使用带有复选框的 Tkinter GUI,每次更改选择时都会重新绘制图形。这更容易
  • 但还是显示了隐藏剧情的传说!
  • 虽然这是一个非常有用的答案,但我不得不求助于原始链接代码(进行了一些修改),因为上面的代码几乎破坏了我尝试的所有标签文本,而不是此处指定的文本.
  • @jhin 我更新了这个例子中的代码;它现在应该也可以与其他标签一起使用。可以验证吗?
  • @Basj 我工作了,您只需再次单击工具按钮取消选择放大镜工具(与平移相同)。
【解决方案2】:

感谢您的帖子!我扩展了上面的类,使其可以处理多个图例 - 例如,如果您正在使用子图。 (我在这里分享它,因为我在其他地方找不到任何其他示例......它可能对其他人很方便......)

    class InteractiveLegend(object):
 def __init__(self):

  self.legends = []
  self.figures = []
  self.lookup_artists = []
  self.lookup_handles = []

  self.host = socket.gethostname()

def add_legends(self, legend):

  self.legends.append(legend)

def init_legends(self):

    for legend in self.legends:

      self.figures.append(legend.axes.figure)

      lookup_artist, lookup_handle = self._build_lookups(legend)

      #print("init", type(lookup))

      self.lookup_artists.append(lookup_artist)
      self.lookup_handles.append(lookup_handle)

    self._setup_connections()
    self.update()

def _setup_connections(self):

    for legend in self.legends:
      for artist in legend.texts + legend.legendHandles:
          artist.set_picker(10) # 10 points tolerance

    for figs in self.figures:
      figs.canvas.mpl_connect('pick_event', self.on_pick)
      figs.canvas.mpl_connect('button_press_event', self.on_click)

def _build_lookups(self, legend):
    labels = [t.get_text() for t in legend.texts]

    handles = legend.legendHandles
    label2handle = dict(zip(labels, handles))
    handle2text = dict(zip(handles, legend.texts))

    lookup_artist = {}
    lookup_handle = {}
    for artist in legend.axes.get_children():
        if artist.get_label() in labels:
            handle = label2handle[artist.get_label()]
            lookup_handle[artist] = handle
            lookup_artist[handle] = artist
            lookup_artist[handle2text[handle]] = artist

    lookup_handle.update(zip(handles, handles))
    lookup_handle.update(zip(legend.texts, handles))

    #print("build", type(lookup_handle))

    return lookup_artist, lookup_handle

def on_pick(self, event):

    #print event.artist
    handle = event.artist
    for lookup_artist in self.lookup_artists:
      if handle in lookup_artist:
          artist = lookup_artist[handle]
          artist.set_visible(not artist.get_visible())
          self.update()

def on_click(self, event):
    if event.button == 3:
        visible = False
    elif event.button == 2:
        visible = True
    else:
        return

    for lookup_artist in self.lookup_artists:
      for artist in lookup_artist.values():
          artist.set_visible(visible)
    self.update()

def update(self):
    for idx, lookup_artist in enumerate(self.lookup_artists):
      for artist in lookup_artist.values():
          handle = self.lookup_handles[idx][artist]
          if artist.get_visible():
              handle.set_visible(True)
          else:
              handle.set_visible(False)
      self.figures[idx].canvas.draw()

def show(self):
    plt.show()

如下使用:

leg1 = ax1.legend(loc='upper left', bbox_to_anchor=(1.05, 1), ncol=2, borderaxespad=0)
leg2 = ax2.legend(loc='upper left', bbox_to_anchor=(1.05, 1), ncol=2, borderaxespad=0)
fig.subplots_adjust(right=0.7)

interactive_legend = InteractiveLegend()

interactive_legend.add_legends(leg1)
interactive_legend.add_legends(leg2)
interactive_legend.init_legends()
interactive_legend.show()

【讨论】:

    【解决方案3】:

    受@JoeKington 的回答启发,这是我使用的(稍作修改的版本,不需要ax, fig,但可以直接与plt.plot(...) 一起使用;plt.legend() 也被排除在主要范围之外对象):

    即用型示例pltinteractivelegend.py:

    import numpy as np
    import matplotlib.pyplot as plt
    
    class InteractiveLegend(object):
        def __init__(self, legend=None):
            if legend == None:
                legend = plt.gca().get_legend()
            self.legend = legend
            self.fig = legend.axes.figure
            self.lookup_artist, self.lookup_handle = self._build_lookups(legend)
            self._setup_connections()
            self.update()
        def _setup_connections(self):
            for artist in self.legend.texts + self.legend.legendHandles:
                artist.set_picker(10) # 10 points tolerance
            self.fig.canvas.mpl_connect('pick_event', self.on_pick)
            self.fig.canvas.mpl_connect('button_press_event', self.on_click)
        def _build_lookups(self, legend):
            labels = [t.get_text() for t in legend.texts]
            handles = legend.legendHandles
            label2handle = dict(zip(labels, handles))
            handle2text = dict(zip(handles, legend.texts))
            lookup_artist = {}
            lookup_handle = {}
            for artist in legend.axes.get_children():
                if artist.get_label() in labels:
                    handle = label2handle[artist.get_label()]
                    lookup_handle[artist] = handle
                    lookup_artist[handle] = artist
                    lookup_artist[handle2text[handle]] = artist
            lookup_handle.update(zip(handles, handles))
            lookup_handle.update(zip(legend.texts, handles))
            return lookup_artist, lookup_handle
        def on_pick(self, event):
            handle = event.artist
            if handle in self.lookup_artist:
                artist = self.lookup_artist[handle]
                artist.set_visible(not artist.get_visible())
                self.update()
        def on_click(self, event):
            if event.button == 3:
                visible = False
            elif event.button == 2:
                visible = True
            else:
                return
            for artist in self.lookup_artist.values():
                artist.set_visible(visible)
            self.update()
        def update(self):
            for artist in self.lookup_artist.values():
                handle = self.lookup_handle[artist]
                if artist.get_visible():
                    handle.set_visible(True)
                else:
                    handle.set_visible(False)
            self.fig.canvas.draw()
    
    if __name__ == '__main__':
        for i in range(20):
            plt.plot(np.random.randn(1000), label=i)
        plt.legend()    
        leg = InteractiveLegend()
        plt.show()
    

    用作库:

    import numpy as np
    import matplotlib.pyplot as plt
    import pltinteractivelegend
    
    for i in range(20):
        plt.plot(np.random.randn(1000), label=i)
    plt.legend()    
    leg = pltinteractivelegend.InteractiveLegend()  # mandatory: keep the object with leg = ...; else it won't work
    plt.show()
    

    【讨论】:

      猜你喜欢
      • 2015-06-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多