如何在不同的tkinter notebook标签中正确嵌入matplotlib图形?
我的图形用户界面(GUI)使用了tkinter的选项卡功能,有两个选项卡(我们称之为A选项卡和B选项卡)。在每个选项卡里,我有一个框架,用来放各种按钮。用户可以按一个按钮来绘制图形。当按下另一个按钮时,图形应该用新数据更新。我可以让这个功能在A选项卡或B选项卡中正常工作,但不能同时在两个选项卡中都正常工作。
问题是,当我在A选项卡上按下一个新按钮时(比如说),新的数据会绘制在figure_a上;但是,旧的数据并没有被清除。我仍然能看到之前图形的x轴和y轴的值。请看下面我plot_xy函数的整体结构。
我是不是没有在正确的位置使用canvas.draw()?我把figure和canvas作为输入传给我的plot_xy函数,这可能会导致问题吗?我是不是在某个地方没有正确清除旧的图形或坐标轴?
如果我注释掉block B(见下文),这个问题就消失了。但如果这样做,用户显然在B选项卡上就无法绘制任何东西了。A选项卡和B选项卡之间似乎有某种互动。也许我没有理解matplotlib是如何跟踪不同的图形和画布的。此外,如果我把block B放在block A之前,问题(旧图形在新图形绘制之前没有清除)就会从A选项卡转移到B选项卡。
你会注意到我通过同一个函数创建了两个figure实例:self.figure_a = draw_figure()和self.figure_b = draw_figure()。如果我把图形创建为plt.figure(1)和plt.figure(2),我也会遇到同样的问题。
另外要注意的是,我的plot_xy函数有各种if, else语句块,用来检查数据并决定是绘制直方图、散点图还是其他类型的图形。在每个if语句后面,我插入了plt.clf(),在每个if块的末尾,我调用了canvas.draw()和toolbar.update()。
这个GUI在启动时默认调用plot_xy函数,以生成一个默认的图形。
import ...
from plot_file import plot_xy
class GUI():
def __init__(self, root):
self.nb = ttk.Notebook(root)
self.nb.pack(fill=tk.BOTH, expand=True)
self.frameA = tk.Frame(self.nb)
self.frameA.pack()
self.frameB = tk.Frame(self.nb)
self.frameB.pack()
self.nb.add(self.frameA, text='tab A')
self.nb.add(self.frameB, text='tab B')
self.a_widgets = A_Widgets(self.frameA)
self.b_widgets = B_Widgets(self.frameB)
self.a_widgets.plot_button_a.configure(command=self.create_plot_a)
self.b_widgets.plot_button_b.configure(command=self.create_plot_b)
--------------Beginning of block A--------------------
self.figure_a = self.draw_figure()
self.canvas_a = FigureCanvasTkAgg(self.figure_a, master=self.a_widgets.frame1)
self.canvas_a.get_tk_widget().grid(....)
self.toolbar_a = NavigationToolbar2Tk(self.canvas_a, self.a_widgets.frame1)
self.toolbar_a.grid(...)
--------------End of block A--------------------------
--------------Beginning of block B--------------------
self.figure_b = self.draw_figure()
self.canvas_b = FigureCanvasTkAgg(self.figure_b, master=self.b_widgets.frame1)
self.canvas_b.get_tk_widget().grid(....)
self.toolbar_b = NavigationToolbar2Tk(self.canvas_b, self.b_widgets.frame1)
self.toolbar_b.grid(...)
--------------End of block B--------------------------
def draw_figure(self):
figure = plt.figure()
return figure
.....
def create_plot_a(self):
...
...
plot_xy(self.canvas_a, self.figure_a, etc.)
def create_plot_b(self):
...
...
plot_xy(self.canvas_b, self.figure_b, etc.)
....
....
if __name__ == "__main__":
root = tk.Tk()
GUI(root)
root.mainloop()
在一个单独的文件plot_file.py中,我有一个绘图函数,它接受canvas、figure以及数据等作为输入:
def plot_xy(canvas, figure, toolbar, data, ...):
if ...:
plt.clf()
ax = figure.add_axes(...)
ax.box_plot(data, ....)
.
.
canvas.draw()
toolbar.update()
else..:
plt.clf()
ax = figure.add_axes(...)
..
ax.scatter(data, ....)
..
canvas.draw()
toolbar.update()
...
1 个回答
明白了。在这个叫 plot_xy 的函数里,我不再使用 plt.clf(),因为这个命令会清空当前的图形,但有时候我想清空的并不是这个图形。于是我改用了 figure.clf()。这样问题就解决了!figure.clf() 明确地清空了在函数中引用的那个图形。