matplotlib和tkinter的问题:Tkinter回调中的异常

时间:2018-06-06 08:56:49

标签: tkinter

我使用tkinter构建一个带有3个按钮的简单GUI窗口,并使用matplotlib来显示一个简单的图形。过程是:按第一个按钮启用第二个按钮。按第二个按钮显示图形,然后启用第三个按钮。按第3个按钮显示图形并保存图形。 但是,按下第二个按钮后无法启用第3个按钮。如果我关闭程序,则会发生错误。 另一个问题:为什么保存的图片是空的?感谢。

import matplotlib.pyplot as plt
from tkinter import *
import numpy as np

class MyGUI:
    def __init__(self):
        Win=Tk()
        Win.geometry('750x640')
        Win.resizable(False,False)
        Win.update()
        w=Win.winfo_width()
        h=Win.winfo_height()

        Btn1=Button(Win,text='enable button 2',command=self.Btn1Listener)
        Btn1.place(x=20,y=20)
        Btn1.update()

        self.Btn2=Button(Win,text='Plot 1',command=self.Btn2Listener)
        self.Btn2.place(x=Btn1.winfo_x()+Btn1.winfo_width()+10,y=20,width=150)
        self.Btn2.configure(state='disabled')
        self.Btn2.update()

        self.Btn3=Button(Win,text='plot 2',command=self.Btn3Listener)
        self.Btn3.place(x=self.Btn2.winfo_x()+self.Btn2.winfo_width()+10,y=20,width=150)
        self.Btn3.configure(state='disabled')
        self.Btn3.update()        

        mainloop()

    #------------------------------------------------------- 
    def Btn1Listener(self):
        self.Btn2.configure(state='normal')

    def Btn2Listener(self):
        global v,s
        v,s=self.B1()
        self.Btn3.configure(state='normal')

    def Btn3Listener(self):
        self.B2(s,v)

    #-------------------------------------------------------------
    def B1(self):                  
        x=np.arange(0,5,0.1)
        y1=np.sin(x)
        plt.plot(x,y1)
        plt.show()

        return 0,5 

    def B2(self, s,v):
        x=np.arange(s,v,0.1)
        y1=np.sin(x)
        plt.plot(x,y1)
        plt.show()   
        plt.savefig('aaa.png')


#------------------------------------------------------------------        
if __name__ =='__main__':   
    MyApp=MyGUI()

1 个答案:

答案 0 :(得分:0)

docs of plt.show()您可以看到此功能的作用是:

  

显示所有数字并阻止

由于Tkinter依赖mainloop运行,因此对plt.show()等阻止功能不起作用。

再次来自文档:

  

单个实验性关键字参数block可以设置为True或False以覆盖上述阻塞行为。

这意味着您可以调用plt.show(block=False)使其不阻止Tkinter主循环。

但是,有一种更简洁的方法可以将matplotlib集成到Tkinter中,可以找到一个例子here。在最简单的实现中,这将转换为您的代码:

from tkinter import *
import numpy as np

from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2TkAgg)

class MyGUI:
    def __init__(self):
        win = Tk()
        win.geometry('750x640')
        win.resizable(False,False)
        win.update()
        w = win.winfo_width()
        h = win.winfo_height()

        btn1 = Button(win,text='Enable button 2',command=self.btn1Listener)
        btn1.place(x=20, y=20)
        btn1.update()

        self.btn2 = Button(win,text='Plot 1',command=self.btn2Listener)
        self.btn2.place(x=btn1.winfo_x()+btn1.winfo_width()+10, y=20, width=150)
        self.btn2.configure(state='disabled')
        self.btn2.update()

        self.btn3 = Button(win,text='Plot 2',command=self.btn3Listener)
        self.btn3.place(x=self.btn2.winfo_x()+self.btn2.winfo_width()+10, y=20, width=150)
        self.btn3.configure(state='disabled')
        self.btn3.update()


        self.fig = Figure(figsize=(5, 4), dpi=100)
        self.ax = self.fig.add_subplot(111)

        self.canvas = FigureCanvasTkAgg(self.fig, master=win)  # A tk.DrawingArea.
        self.canvas.draw()
        self.canvas.get_tk_widget().place(x=20, y=80)

        win.mainloop()

    #------------------------------------------------------- 
    def btn1Listener(self):
        self.btn2.configure(state='normal')

    def btn2Listener(self):
        self.s, self.v = self.B1()
        self.btn3.configure(state='normal')

    def btn3Listener(self):
        self.B2(self.s, self.v)

    #-------------------------------------------------------------
    def B1(self):                  
        x=np.arange(0, 5, 0.1)
        y1=np.sin(x)
        self.ax.plot(x, y1)
        self.canvas.draw()
        return 0,5 

    def B2(self, s, v):
        x=np.arange(s, v, 0.1)
        y1=np.cos(x)
        # Use this if you want to remove the first plot
        # self.ax.clear()
        self.ax.plot(x, y1)
        self.canvas.draw()
        self.fig.savefig('aaa.png')


#------------------------------------------------------------------        
if __name__ =='__main__':   
    MyApp=MyGUI()

P.S。我不建议使用place,因为从长远来看它很难管理,并且不喜欢调整窗口大小。尝试使用grid和/或pack来设置GUI。