我的目标是清除并重新绘制 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'
我对这里发生的情况的最佳猜测是,有一些剩余的艺术家不再附加图形/画布,当我们尝试附加悬停光标时,它们会生成错误。但是,我不知道如何清除它。任何帮助将不胜感激。
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()