(Python) tkinter 图形(带颜色条)在使用滑块时重叠

(Python) tkinter figures (with colorbar) overlap when using slider

以下代码用于绘制 tkinter 上五个(滑块:0 ~ 4).xlsx 文件的等高线。每个文件只包含矩阵 12X6 中的数值数据,例如

from tkinter import *
import tkinter.ttk as ttk
import matplotlib
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
import ipywidgets as wg
import os
import pandas as pd
from matplotlib.ticker import MaxNLocator
from matplotlib.colors import BoundaryNorm
import math
from matplotlib.ticker import LinearLocator
%matplotlib widget
from matplotlib.widgets import Slider
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
matplotlib.use('TkAgg')

root = Tk()
root.title('TEST')
root.geometry("800x800")

def plot_noise():
    rec = np.shape(dfs[0])
    rmm = np.concatenate([dfs[0], dfs[1]])
    for jj in range(2,num):
        rmm = np.concatenate([rmm, dfs[jj]])
    # =================================================PLOT===========================================
    fig = plt.Figure()                       
    canvas = FigureCanvasTkAgg(fig, root)     
    canvas.get_tk_widget().grid(row=3, column=0, columnspan=3, rowspan=3, sticky=W+E+N+S, padx=0, pady=0) 
    
    # ===============================================contourf=========================================
    ax = fig.add_subplot(111)                 
    fig.subplots_adjust(bottom=0.25)          
    X = np.arange(1,rec[1]+1,1)
    Y = np.arange(1,rec[0]+1,1)
    x , y = np.meshgrid(X,Y)  
    # ==============================================color bar=========================================
    cbar_max = math.floor(np.min(rmm))
    cbar_min = math.ceil(np.max(rmm))
    cbar_num_colors = 200
    cbar_num_format = "%d"
    levels = MaxNLocator(nbins=cbar_num_colors).tick_values(cbar_min, cbar_max)
    # ============================================Initial plot======================================== 
    con = ax.contourf(x,y,dfs[1], levels = levels, cmap=cm.jet, alpha = 0.5, antialiased = True)   
    cbar = fig.colorbar(con,ax = ax)
    ax.axis([1, 12, 1, 6])
    # ================================================Slider==========================================
    global slider_de
    slider_bar = fig.add_axes([0.12, 0.1, 0.78, 0.03])     
    slider_de = Slider(slider_bar, 's_bar', 0, num-1, valinit=1,valfmt='%0.0f',  valstep=1)
    num_on_slider = []
    def update(val):
        num_on_slider.append(slider_de.val)
        for ii in range(0,num):
            if num_on_slider[-1] == ii:
                con = ax.contourf(x,y,dfs[ii], levels = levels, cmap=cm.jet, alpha = 0.5, antialiased = True)
                cbar = fig.colorbar(con,ax = ax)
                ax.axis([1, 12, 1, 6])
                
    slider_de.on_changed(update)            
                
    
# =================================================GUI - Tkinter======================================= 
resultButton = ttk.Button(root, text = 'show', command = plot_noise)
resultButton.grid(column=0, row=1, pady=15, sticky=W)

root.mainloop()

当运行它的时候,我得到了

现在,如果我使用 fig.clf,例如

fig.clf()
con = ax.contourf(x,y,dfs[ii], levels = levels, cmap=cm.jet, alpha = 0.5, antialiased = True)
cbar = fig.colorbar(con,ax = ax)

我得到了

轮廓消失了。我也试过 from matplotlib.figure import Figure 而不是 pyplot;但是,它不起作用。

如何解决这个奇怪的问题?

有什么建议,谢谢!

这是我根据@Kat

的提示得出的一个答案
from tkinter import *
import tkinter.ttk as ttk
import matplotlib
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
import ipywidgets as wg
import os
import pandas as pd
from matplotlib.ticker import MaxNLocator
from matplotlib.colors import BoundaryNorm
import math
from matplotlib.ticker import LinearLocator
%matplotlib widget
from matplotlib.widgets import Slider
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
matplotlib.use('TkAgg')

root = Tk()
root.title('TEST')
root.geometry("800x800")

cbar = None
def plot_noise():
    # ============================================Read .xlsx file=====================================
    folder = r'C:\Users\Dian-Jing Chen\Desktop\Work\test_read'
    files = os.listdir(folder)
    dfs = {}
    for i, file in enumerate(files):
        if file.endswith('.xlsx'):
            dfs[i] = pd.read_excel(os.path.join(folder,file), sheet_name='Z=143', header = None, skiprows=[0], usecols = "B:M")

    num = i + 1
    rec = np.shape(dfs[0])
    rmm = np.concatenate([dfs[0], dfs[1]])
    for jj in range(2,num):
        rmm = np.concatenate([rmm, dfs[jj]])
    # =================================================PLOT===========================================
    fig, ax = plt.subplots()                       
    canvas = FigureCanvasTkAgg(fig, root)     
    canvas.get_tk_widget().grid(row=3, column=0, columnspan=3, rowspan=3, sticky=W+E+N+S, padx=0, pady=0) 
    # ===============================================contourf=========================================          
    fig.subplots_adjust(bottom=0.25)          
    X = np.arange(1,rec[1]+1,1)
    Y = np.arange(1,rec[0]+1,1)
    x , y = np.meshgrid(X,Y)  
    # ==============================================color bar=========================================
    cbar_max = math.floor(np.min(rmm))
    cbar_min = math.ceil(np.max(rmm))
    cbar_num_colors = 200
    cbar_num_format = "%d"
    levels = MaxNLocator(nbins=cbar_num_colors).tick_values(cbar_min, cbar_max)
    # ============================================Initial plot======================================== 
    con = ax.contourf(x,y,dfs[1], levels = levels, cmap=cm.jet, alpha = 0.5, antialiased = True)  
    cbar = fig.colorbar(con,ax = ax)
    ax.axis([1, 12, 1, 6])
    # ================================================Slider==========================================
    slider_bar = fig.add_axes([0.12, 0.1, 0.78, 0.03])     
    slider_de = Slider(slider_bar, 's_bar', 0, num-1, valinit=1,valfmt='%0.0f',  valstep=1)
    num_on_slider = []
    def update(val):
        num_on_slider.append(slider_de.val)
        for ii in range(0,num):
            if num_on_slider[-1] == ii:
                con = ax.contourf(x,y,dfs[ii], levels = levels, cmap=cm.jet, alpha = 0.5, antialiased = True)
                ax.axis([1, 12, 1, 6])
                
    slider_de.on_changed(update)            
                
    
# =================================================GUI - Tkinter======================================= 
resultButton = ttk.Button(root, text = 'show', command = plot_noise)
resultButton.grid(column=0, row=1, pady=15, sticky=W)

root.mainloop()

我删除了 update() 函数中的 cbar,因为颜色栏始终是固定的。

所以我现在正在寻找另一种高级方法(因为由于赏金我不能删除这个问题),这也是我学习更多的机会。例如,使用 class 是一种非常高级的方法。

最佳答案将获得50分。

将代码重构为 class 并将功能添加到 select 文件夹。其他想法在评论中。

#Do not use wild imports
import tkinter as tk
import tkinter.ttk as ttk
import matplotlib
import matplotlib.pyplot as plt
from matplotlib import cm
import numpy as np
import ipywidgets as wg
import os
import pandas as pd
from matplotlib.ticker import MaxNLocator
from matplotlib.colors import BoundaryNorm
import math
from matplotlib.ticker import LinearLocator
from matplotlib.widgets import Slider
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
matplotlib.use('TkAgg')


class MyPlot(ttk.Frame):
    def __init__(self, parent):
        super().__init__()

        #put widgets in frame(self)
        resultButton = ttk.Button(self, text = 'Select Folder', command = self.select_dir)
        resultButton.grid(column=0, row=1, pady=15, sticky=tk.W)

        # show frame in root
        self.grid(column=0, row=0)

    def select_dir(self):
        selected_dir = tk.filedialog.askdirectory(parent=self, title='Select a directory')
        # print(selected_dir)
        if selected_dir:
            self.plot_noise(selected_dir)
        else:
            tk.messagebox.showerror('Select', 'Select a directory', parent=self)

    def plot_noise(self, folder):
        # ============================================Read .xlsx file=====================================
        files = os.listdir(folder)
        dfs = {}
        # Prepare for scenarios where files(other than the expected .xlsx) are in the folder passed
        # when that is the case the enumerated i previously being used to create dict keys will get all messed up
        index = 0
        for file in files:
            if file.endswith('.xlsx'):
                dfs[index] = pd.read_excel(os.path.join(folder,file), sheet_name='Z=143', header = None, skiprows=[0], usecols = "B:M")
                index += 1

        if dfs:
            num = len(dfs)
            rec = np.shape(dfs[0])
            rmm = np.concatenate([dfs[0], dfs[1]])
            for jj in range(2,num):
                rmm = np.concatenate([rmm, dfs[jj]])
            # =================================================PLOT===========================================
            fig, ax = plt.subplots()                
            canvas = FigureCanvasTkAgg(fig, self)     
            canvas.get_tk_widget().grid(row=3, column=0, columnspan=3, rowspan=3, sticky=tk.W+tk.E+tk.N+tk.S, padx=0, pady=0) 
            # ===============================================contourf=========================================          
            fig.subplots_adjust(bottom=0.25)          
            X = np.arange(1,rec[1]+1,1)
            Y = np.arange(1,rec[0]+1,1)
            x , y = np.meshgrid(X,Y)  
            # ==============================================color bar=========================================
            cbar_max = math.floor(np.min(rmm))
            cbar_min = math.ceil(np.max(rmm))
            cbar_num_colors = 200
            cbar_num_format = "%d"
            levels = MaxNLocator(nbins=cbar_num_colors).tick_values(cbar_min, cbar_max)
            # ============================================Initial plot======================================== 
            con = ax.contourf(x,y,dfs[1], levels = levels, cmap=cm.jet, alpha = 0.5, antialiased = True)  
            cbar = fig.colorbar(con,ax = ax)
            ax.axis([1, 12, 1, 6])
            # ================================================Slider==========================================
            slider_bar = fig.add_axes([0.12, 0.1, 0.78, 0.03])     
            slider_de = Slider(slider_bar, 's_bar', 0, num-1, valinit=1,valfmt='%0.0f',  valstep=1)
            num_on_slider = []

            def update(val):
                num_on_slider.append(slider_de.val)
                for ii in range(0,num):
                    if num_on_slider[-1] == ii:
                        con = ax.contourf(x,y,dfs[ii], levels = levels, cmap=cm.jet, alpha = 0.5, antialiased = True)
                        ax.axis([1, 12, 1, 6])
                        
            slider_de.on_changed(update)

        else:
            tk.messagebox.showerror('No File', 'No .xlsx file found')            
                
    
# =================================================GUI - Tkinter======================================= 
root = tk.Tk()
root.title('TEST')
root.geometry("800x800")
MyPlot(root)
root.mainloop()