从动态 matplotlib 图形中清除错误栏艺术家时出现问题

问题描述 投票:0回答:1

我的目标是清除并重新绘制 matplotlib 图上的误差条数据,同时重用相同的背景和轴。这意味着我不能仅仅清除这个数字。我可以通过存储和删除艺术家来使用

plot
轻松完成此操作,但是一旦我开始使用
errorbar
,我就会遇到问题。

作为示例,下面的脚本将生成随机数据图,并提供一个按钮来清除该数据而不清除图形并生成/显示新的随机数据。

import mplcursors
import matplotlib.pyplot as plt
from matplotlib.widgets import Button

class RandomDataPlotter:
    def __init__(self):
        self.fig, self.ax = plt.subplots()
        self.fig.subplots_adjust(bottom=0.2)
        self.current_data = None
        self.current_errorbars = None
        self.cursor = None

        # Add a button to toggle data
        self.button_ax = self.fig.add_axes([0.7, 0.05, 0.1, 0.075])
        self.button = Button(self.button_ax, 'Toggle Data')
        self.button.on_clicked(self.toggle_data)

        self.plot_random_data()

    def plot_random_data(self):
        # Clear existing artists
        if self.current_data:
            self.current_data.remove()
        if self.current_errorbars:
            for artist in self.current_errorbars:
                artist.remove()
        if self.cursor:
            self.cursor.remove()

        # Generate random data
        x = np.linspace(0, 10, 100)
        y = np.random.rand(100)
        y_err = np.random.rand(100) * 0.1

        # Plot data with error bars
        self.current_data, _, self.current_errorbars = self.ax.errorbar(x, y, yerr=y_err, fmt='o', color='blue')

        # Attach hover cursor
        self.cursor = mplcursors.cursor(self.ax, hover=True)
        self.cursor.connect("add", lambda sel: sel.annotation.set_text(f"x: {sel.target[0]:.2f}\ny: {sel.target[1]:.2f}"))

        # Redraw the canvas
        self.ax.relim()
        self.ax.autoscale_view()
        self.fig.canvas.draw_idle()

    def toggle_data(self, event):
        self.plot_random_data()

if __name__ == "__main__":
    plotter = RandomDataPlotter()
    plt.show()

这适用于第一次迭代/按下按钮,但是,之后会发生某些事情,从而生成以下错误:

AttributeError: 'NoneType' object has no attribute 'canvas'
Traceback (most recent call last):
  File "/Users/atom/hemanpro/HeMan/.venv/lib/python3.13/site-packages/matplotlib/cbook.py", line 361, in process
    func(*args, **kwargs)
    ~~~~^^^^^^^^^^^^^^^^^
  File "/Users/atom/hemanpro/HeMan/.venv/lib/python3.13/site-packages/matplotlib/widgets.py", line 244, in <lambda>
    return self._observers.connect('clicked', lambda event: func(event))
                                                            ~~~~^^^^^^^
  File "/Users/atom/hemanpro/HeMan/test_files/test2.py", line 49, in toggle_data
    self.plot_random_data()
    ~~~~~~~~~~~~~~~~~~~~~^^
  File "/Users/atom/hemanpro/HeMan/test_files/test2.py", line 40, in plot_random_data
    self.cursor = mplcursors.cursor(self.ax, hover=True)
                  ~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^
  File "/Users/atom/hemanpro/HeMan/.venv/lib/python3.13/site-packages/mplcursors/_mplcursors.py", line 744, in cursor
    return Cursor(artists, **kwargs)
  File "/Users/atom/hemanpro/HeMan/.venv/lib/python3.13/site-packages/mplcursors/_mplcursors.py", line 264, in __init__
    for canvas in {artist.figure.canvas for artist in artists}]
                   ^^^^^^^^^^^^^^^^^^^^
AttributeError: 'NoneType' object has no attribute 'canvas'

我对这里发生的情况的最佳猜测是,有一些剩余的艺术家不再附加图形/画布,当我们尝试附加悬停光标时,它们会生成错误。但是,我不知道如何清除它。任何帮助将不胜感激。

python matplotlib
1个回答
0
投票

errorbar
方法返回一个
ErrorbarContainer
实例,它本身就是一个包含线条等的艺术家。目前,您只删除子艺术家而不是
ErrorbarContainer
,我认为这是导致错误的剩余容器。 在容器上调用
remove
方法 should 会删除其自身及其所有子项,这样您就可以跟踪它,这使事情变得更简单。 然而,目前 Matplotlib 中存在一个错误,并且该方法无法正常工作。 我们可以通过直接从轴的 containers 列表中删除它来解决这个问题。
import mplcursors
import matplotlib.pyplot as plt
from matplotlib.widgets import Button
import numpy as np

class RandomDataPlotter:
    def __init__(self):
        self.fig, self.ax = plt.subplots()
        self.fig.subplots_adjust(bottom=0.2)
        self.current_errorbar_container = None

        # Add a button to toggle data
        self.button_ax = self.fig.add_axes([0.7, 0.05, 0.1, 0.075])
        self.button = Button(self.button_ax, 'Toggle Data')
        self.button.on_clicked(self.toggle_data)

        self.plot_random_data()

    def plot_random_data(self):
        # Clear existing artists
        if self.current_errorbar_container:
            self.current_errorbar_container.remove()
            
            # Explicitly remove errorbar from containers list to workaround bug
            # https://github.com/matplotlib/matplotlib/issues/25274
            self.ax.containers.remove(self.current_errorbar_container)

        # Generate random data
        x = np.linspace(0, 10, 100)
        y = np.random.rand(100)
        y_err = np.random.rand(100) * 0.1

        # Plot data with error bars
        self.current_errorbar_container = self.ax.errorbar(x, y, yerr=y_err, fmt='o', color='blue')

        # Attach hover cursor
        self.cursor = mplcursors.cursor(self.ax, hover=True)
        self.cursor.connect("add", lambda sel: sel.annotation.set_text(f"x: {sel.target[0]:.2f}\ny: {sel.target[1]:.2f}"))

        # Redraw the canvas
        self.ax.relim()
        self.ax.autoscale_view()
        self.fig.canvas.draw_idle()

    def toggle_data(self, event):
        self.plot_random_data()

if __name__ == "__main__":
    plotter = RandomDataPlotter()
    plt.show()

	
© www.soinside.com 2019 - 2024. All rights reserved.