Tkinter 中的 Matplotlib 绘图 - 每次更新都会添加新的 NavigationToolbar?

Posted

技术标签:

【中文标题】Tkinter 中的 Matplotlib 绘图 - 每次更新都会添加新的 NavigationToolbar?【英文标题】:Matplotlib plot in Tkinter - every update adds new NavigationToolbar? 【发布时间】:2012-10-07 11:08:57 【问题描述】:

我正在开发一个 Tkinter-GUI 以交互方式生成 Matplotlib 图,具体取决于用户输入。为此,它需要在用户更改输入后重新绘制。

原则上我已经让它工作了,但想包括NavigationToolbar。但是,我似乎无法让NavigationToolbar 的更新正常工作。

这是代码的基本工作版本(没有用户输入条目):

# Import modules
from Tkinter import *
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg

# global variable: do we already have a plot displayed?
show_plot = False

# plotting function
def plot(x, y):
    fig = plt.figure()
    ax1 = fig.add_subplot(1,1,1)
    ax1.plot(x,y)
    return fig

def main():
    x = np.arange(0.0,3.0,0.01)
    y = np.sin(2*np.pi*x)
    fig = plot(x, y)

    canvas = FigureCanvasTkAgg(fig, master=root)
    toolbar = NavigationToolbar2TkAgg(canvas, toolbar_frame)

    global show_plot
    if show_plot:
        #remove existing plot and toolbar widgets
        canvas.get_tk_widget().grid_forget()
        toolbar_frame.grid_forget()

    toolbar_frame.grid(row=1,column=1)
    canvas.get_tk_widget().grid(row=0,column=1)
    show_plot=True

# GUI
root = Tk()

draw_button = Button(root, text="Plot!", command = main)
draw_button.grid(row=0, column=0)

fig = plt.figure()
canvas = FigureCanvasTkAgg(fig, master=root)
canvas.get_tk_widget().grid(row=0,column=1)
toolbar_frame = Frame(root)
toolbar_frame.grid(row=1,column=1)

root.mainloop()

按下“绘图!” 一次 生成绘图和NavigationToolbar。 再次按下它会重新绘制,但会生成 second NavigationToolbar(每次按下“Plot!”时都会生成另一个)。这听起来像grid_forget() 不起作用。 但是,当我改变时

    if show_plot:
        #remove existing plot and toolbar widgets
        canvas.get_tk_widget().grid_forget()
        toolbar_frame.grid_forget()

    toolbar_frame.grid(row=1,column=1)
    canvas.get_tk_widget().grid(row=0,column=1)
    show_plot=True

    if show_plot:
        #remove existing plot and toolbar widgets
        canvas.get_tk_widget().grid_forget()
        toolbar_frame.grid_forget()
    else:
        toolbar_frame.grid(row=1,column=1)
    canvas.get_tk_widget().grid(row=0,column=1)
    show_plot=True

然后NavigationToolbar 确实在“Plot!”时消失了第二次按下(但正如预期的那样,没有新的NavigationToolbar 来替换旧的)。所以grid_forget() 正在工作,只是没有像预期的那样。

我做错了什么?有没有更好的方法来更新NavigationToolbar

非常感谢任何帮助! 拉斯塔尔达

编辑:

我发现如果您销毁 NavigationToolbar 而不是忘记它,这将起作用。但是你必须在之后完全重新创建小部件,当然:

canvas = FigureCanvasTkAgg(fig, master=root)
toolbar_frame = Frame(root)

global show_plot
if show_plot: # if plot already present, remove plot and destroy NavigationToolbar

    canvas.get_tk_widget().grid_forget()
    toolbar_frame.destroy()

toolbar_frame = Frame(root)
toolbar = NavigationToolbar2TkAgg(canvas, toolbar_frame)
toolbar_frame.grid(row=21,column=4,columnspan=3)
canvas.get_tk_widget().grid(row=1,column=4,columnspan=3,rowspan=20)

show_plot = True

但是,下面 Hans 展示的更新方法要好得多,因为您不必破坏和重新创建任何东西。我只是想强调我的方法的问题(除了不优雅和性能)可能是我没有使用destroy()

【问题讨论】:

【参考方案1】:

当你按下“绘图!”它调用main 的按钮。 main 创建导航工具栏。所以,每次你按下按钮你都会得到一个工具栏。我对 matplot 了解不多,但很明显为什么会有多个工具栏。

顺便说一句,grid_forget 不会破坏小部件,它只是将其从视图中移除。即使你没有看到它,它仍然在记忆中。

通常在 GUI 中,您只创建一次所有小部件,而不是一遍又一遍地重新创建相同的小部件。

【讨论】:

我也是这么想的。但是,NavigationToolbar 似乎只有在重新绘制后“刷新”后才有效(因此它与新情节保持连接)。而且我还没有找到任何有效的类似更新的功能。 (如果有人能指出我那里,请做!)所以我认为销毁和重新创建它们应该可以工作,我试图用grid_forget() 来做 - 有没有更好的方法呢?此外,我对画布小部件执行相同操作,但没有得到相同的加倍行为。我不明白为什么。【参考方案2】:

一种稍微不同的方法可能是通过清除和重绘来重用该图形以用于后续绘图。这样,您不必销毁和重新生成图形和工具栏:

from Tkinter import Tk, Button
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg

# plotting function: clear current, plot & redraw
def plot(x, y):
    plt.clf()
    plt.plot(x,y)
    # just plt.draw() won't do it here, strangely
    plt.gcf().canvas.draw()

# just to see the plot change
plotShift = 0
def main():
    global plotShift

    x = np.arange(0.0,3.0,0.01)
    y = np.sin(2*np.pi*x + plotShift)
    plot(x, y)

    plotShift += 1

# GUI
root = Tk()

draw_button = Button(root, text="Plot!", command = main)
draw_button.grid(row=0, column=0)

# init figure
fig = plt.figure()

canvas = FigureCanvasTkAgg(fig, master=root)
toolbar = NavigationToolbar2TkAgg(canvas, root)
canvas.get_tk_widget().grid(row=0,column=1)
toolbar.grid(row=1,column=1)

root.mainloop()

【讨论】:

谢谢!我在各种来源看到了对plt.draw() 的引用,但它不起作用,这就是我采用破坏和重建方法的原因。但这当然要好得多。谢谢!

以上是关于Tkinter 中的 Matplotlib 绘图 - 每次更新都会添加新的 NavigationToolbar?的主要内容,如果未能解决你的问题,请参考以下文章

深入浅出matplotlib(79):在tkinter应用程序里嵌入matplotlib绘图

matplotlib 在 tkinter 画布中的缩放功能

使用 matplotlib 清除 Tkinter Canvas 小部件的问题

使用带有 pylab/matplotlib 嵌入的 Tkinter 播放、暂停、停止功能的彩色绘图动画:无法更新图形/画布?

如何将 Seaborn 绘图集成到 Tkinter GUI

如何让 Matplotlib 图形在 Tkinter GUI 中正确滚动+调整大小