如何在不同的tkinter notebook标签中正确嵌入matplotlib图形?

0 投票
1 回答
30 浏览
提问于 2025-04-14 18:03

我的图形用户界面(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 个回答

0

明白了。在这个叫 plot_xy 的函数里,我不再使用 plt.clf(),因为这个命令会清空当前的图形,但有时候我想清空的并不是这个图形。于是我改用了 figure.clf()。这样问题就解决了!figure.clf() 明确地清空了在函数中引用的那个图形。

撰写回答