如何更新 Tkinter canvas 上的绘图?

How to update a plot on Tkinter canvas?

我有一个 tkinter 应用程序,其中有不同的框架,每个框架都有不同的情节。 我有一个导入功能,允许我 select 我想要绘制的数据文件。

现在,如果我在程序开始时导入文件,一切都运行良好,即一旦在 canvas 上创建子图,就会显示数据。

但是,如果事先创建的子图没有任何数据,当我导入数据并调用函数绘制它时,canvas 不会更新图。但是,如果我调整 window 的大小(或最大化它),绘图就会更新。

下面是代码。任何有关代码结构的建议都将受到赞赏。

import numpy as np
import pandas as pd
import matplotlib
import matplotlib.pyplot as plt
matplotlib.use("TkAgg")

from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg
from matplotlib.figure import Figure
import matplotlib.animation as animation


import Tkinter as tk
import ttk
from tkFileDialog import askopenfilename

LARGE_FONT= ("Verdana", 12)
plot_colors = plt.rcParams['axes.color_cycle']
width, height = plt.figaspect(1)

fig_nyquist = Figure(figsize=(width, height), dpi=100)
plot_axes_nyquist = fig_nyquist.add_subplot(111)

fig_bode = Figure(figsize=(width, height), dpi=100)
plot_axes_bode = fig_bode.add_subplot(111)

fig_randles = Figure(figsize=(width, height), dpi=100)
plot_axes_randles = fig_randles.add_subplot(111)


class EISapp(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        tk.Tk.wm_title(self, "EIS + CV Analyser")


        container = tk.Frame(self)
        container.pack(pady=10,padx=10, side="top", fill="both", expand = True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}


        for F in (menu_page, nyquist_page, bode_page, randles_page):

            frame = F(container, self)

            self.frames[F] = frame

            frame.grid(row=0, column=0, sticky="nsew")

        self.show_frame(menu_page)

    def show_frame(self, cont):

        frame = self.frames[cont]
        frame.tkraise()




class menu_page(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self,parent)
        label = tk.Label(self, text="Menu", font=LARGE_FONT)
        label.pack(pady=10,padx=10)

        import_button = ttk.Button(self, text="Import EIS data file", command=lambda: import_EIS_data())
        import_button.pack()

        nyquist_button = ttk.Button(self, text="Nyquist Plot", command=lambda: controller.show_frame(nyquist_page))
        nyquist_button.pack()

        bode_button = ttk.Button(self, text="Bode Plot", command=lambda: controller.show_frame(bode_page))
        bode_button.pack()

        randles_button = ttk.Button(self, text="Randles Plot", command=lambda: controller.show_frame(randles_page))
        randles_button.pack()



class nyquist_page(tk.Frame):

    def __init__(self, parent, controller):        
        tk.Frame.__init__(self, parent)
        label = tk.Label(self, text="Nyquist Plot", font=LARGE_FONT)
        label.pack(pady=10,padx=10)

        menu_button = ttk.Button(self, text="Menu", command=lambda: controller.show_frame(menu_page))
        menu_button.pack()

        refresh_button = ttk.Button(self, text="Refresh", command=lambda: refresh_plots())
        refresh_button.pack()

        canvas = FigureCanvasTkAgg(fig_nyquist, self)
        canvas.show()
        canvas.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)

        toolbar = NavigationToolbar2TkAgg(canvas, self)
        toolbar.update()

class bode_page(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        label = tk.Label(self, text="Bode Plot", font=LARGE_FONT)
        label.pack(pady=10,padx=10)

        menu_button = ttk.Button(self, text="Menu", command=lambda: controller.show_frame(menu_page))
        menu_button.pack()

        canvas = FigureCanvasTkAgg(fig_bode, self)
        canvas.show()
        canvas.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)

        toolbar = NavigationToolbar2TkAgg(canvas, self)
        toolbar.update()

class randles_page(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        label = tk.Label(self, text="Randles Plot", font=LARGE_FONT)
        label.pack(pady=10,padx=10)

        menu_button = ttk.Button(self, text="Menu", command=lambda: controller.show_frame(menu_page))
        menu_button.pack()

        canvas = FigureCanvasTkAgg(fig_randles, self)
        canvas.show()
        canvas.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True)

        toolbar = NavigationToolbar2TkAgg(canvas, self)
        toolbar.update()

def import_EIS_data():
    global EIS_df    
    try:
        filename = askopenfilename(defaultextension='.txt', filetypes=[('txt file','*.txt'), ('All files','*.*')]) # show an "Open" dialog box and return the path to the selected file 

        data_table = pd.read_table(filename, index_col=False, skiprows=headers_footers(filename)[0], skip_footer=headers_footers(filename)[1], names=['Temperature', 'Frequency', 'Raw Amplitude', 'Z1', 'Z2', 'Time', 'Gain level'] )

        # Convert Frequency values from kHz to Hz
        data_table['Frequency'] = data_table['Frequency'] * 1000;

        # Delete Unnecessary Columns
        data_table = data_table.drop(['Temperature', 'Gain level', 'Raw Amplitude', 'Time'], axis=1); # axis=1 selects "vertical axis" (i.e. columns instead of rows)

        # Adds calculated values of impedance modulus and angle
        data_table['Z'] = np.sqrt(data_table['Z1']**2 + data_table['Z2']**2);
        data_table['Angle'] = np.degrees( np.arctan( -data_table['Z2'] / data_table['Z1'] ) );

        EIS_df = EIS_df.append(data_table)
        refresh_plots()

    except:
        quit()




def nyquist_plot(Z1, Z2, plot_axes=None):
    if plot_axes == None:
        plot_axes = plt.subplot(111)
    if not EIS_df.empty:
        plot_axes.plot(Z1, Z2)
        plot_axes.set_xlabel('$\Re(Z)$')
        plot_axes.set_ylabel('$\Im(Z)$')  
        plot_axes.set_xlim([0, 800]);
        plot_axes.set_ylim([-800, 0]);

def bode_plot(freq, Z, angle, imped_axis=None):

    if imped_axis == None:
        imped_axis = plt.subplot(111)
    if not EIS_df.empty:    
        handle_imped, = imped_axis.plot(freq, Z, label="Impedance")
        imped_axis.set_xlabel('$Frequency$ $(Hz)$')
        imped_axis.set_ylabel('$|Z|$')
        imped_axis.semilogx()
        imped_axis.semilogy()
        imped_axis.legend(loc=2)

    #    imped_axis.set_xlim([0, 1E7]);
    #    imped_axis.set_ylim([1E-1, 1E5]);

        angle_axis = imped_axis.twinx();
        handle_angle, = angle_axis.plot(freq, angle, plot_colors[1], label="Angle", linestyle='--');

        #Configure plot design    
        angle_axis.set_ylabel(r"$\theta$ $(^{\circ}) $")
    #    angle_axis.semilogx()
        angle_axis.grid('off')
        angle_axis.set_ylim([0, 90]);
        angle_axis.legend(loc=1, handlelength=3.6)


def randles_plot(freq, Z1, Z2, plot_axes=None):
    if plot_axes == None:
        plot_axes = plt.subplot(111)
    if not EIS_df.empty:
        plot_axes.plot(1/(np.pi*np.sqrt(freq)),Z1, label='$\Re(Z)$')
        plot_axes.plot(1/(np.pi*np.sqrt(freq)),-Z2, label='$\Im(Z)$')
        plot_axes.legend(loc=2)
        plot_axes.set_xlabel('$(\sqrt{\omega})^{-1}$')
        plot_axes.set_ylabel('$Impedance$') 


def refresh_plots():
    nyquist_plot(EIS_df.Z1, EIS_df.Z2, plot_axes_nyquist)
    fig_nyquist.tight_layout()

    bode_plot(EIS_df.Frequency, EIS_df.Z, EIS_df.Angle, plot_axes_bode)
    fig_bode.tight_layout()    

    randles_plot(EIS_df.Frequency, EIS_df.Z1, EIS_df.Z2, plot_axes_randles)
    fig_randles.tight_layout()


EIS_df = pd.DataFrame(columns=['Frequency', 'Z1', 'Z2', 'Z', 'Angle'] )
app = EISapp()
app.mainloop()

调用canvas的draw()方法。

试试 your_figure_name.canvas.draw_idle(),对我有用。