【发布时间】:2019-09-16 20:29:23
【问题描述】:
我有一个使用 tkinter 创建 GUI 的小程序。它包含一个加载 .csv 文件的按钮,创建一个带有与 csv 文件中的列一样多的选项卡的笔记本。然后,在每个活动选项卡上(至少这是我的意图)我都有一个从图形创建的图。
程序按预期运行,唯一的问题是切换Tabs时,每次点击Tabs时使用的内存都会增加。
使用 Windows 任务管理器监控内存使用情况。
加载 csv 文件后,当我选择不加载新文件时,我没有看到已用内存下降。
如果我不调用绘图功能,则仅创建选项卡时,不会出现内存问题。
我已经尝试使用 gc.collect() 手动调用垃圾收集器,但这并没有帮助。这是我的代码:
import matplotlib
matplotlib.use('TkAgg')
import pandas as pd
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.figure import Figure
import matplotlib.pyplot as plt
import os
import sys
import tkinter as tk
from tkinter import messagebox as msg
from tkinter import ttk, filedialog
##import gc
class Graphs(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
container = tk.Frame(self)
container.pack(side='top', fill='both', expand=1)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
self.protocol('WM_DELETE_WINDOW', self._destroyWindow)
self.frames = {}
frame = StartPage(parent=container, controller=self)
self.frames[StartPage] = frame
frame.grid(row=0, column=0, sticky='nsew')
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
def _destroyWindow(self):
self.quit() # stops mainloop
self.destroy()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
# initialize lists
self.tabs_list = []
self.hdrs = []
self.figs_list = []
self.ax_list = []
self.canvas_list = []
self.toolbars_list = []
# initialize Data Frame
self.df = pd.DataFrame()
self.nb = None
self.canvas = None
self.toolbar = None
# create LOAD button
self.btn = tk.Button(self, text = 'Load file', command=self.load_csv)
self.btn.pack()
def load_csv(self):
'''
Reset Data Frame;
Destroy notebook if exists;
Load CSV file.
'''
# reset Data Frame
self.df = pd.DataFrame()
# destroy notebook if exists
if self.nb:
self.nb.pack_forget()
self.nb.destroy()
self.nb = None
## gc.collect()
# Select CSV file
self.file_path = filedialog.askopenfilename()
if not self.file_path:
msg.showinfo('Select CSV file', "No file chosen.")
return
try:
# read csv file (exemple.csv)
self.df = pd.read_csv(self.file_path, header=0)
except:
msg.showinfo('Select CSV file', 'Not a csv file / corrupt file.')
return
print(self.df.head())
print(self.df.shape)
# get dimensions
self.m, self.n = self.df.shape
# build the abscissa x from first column
self.x = self.df.iloc[:,0]
# create the notebook
self.nb = ttk.Notebook(self)
# allow Tab navigation
self.nb.enable_traversal()
# add Tabs
for k in range(1, self.n):
hdr = self.df.columns[k]
self.hdrs.append(hdr)
tab = tk.Frame(self.nb, name=hdr.lower())
self.nb.add(tab, text=hdr)
self.tabs_list.append(tab)
self.nb.pack(fill='both', expand=1)
# virtual event after a new tab is selected
self.nb.bind("<<NotebookTabChanged>>", self.plotTH)
def plotTH(self, event):
'''
Plot each Column from Data Frame on its own Tab/Figure
'''
# get path of the selected Tab
tab_path = event.widget.nametowidget(event.widget.select())
# add selected Tab to the list of Tabs
self.tabs_list.append(tab_path)
# get the Tab index;
# When there are no tabs, .select() returns an empty string,
# but .index('current') throws an exception;
# nb.select() returns the Tab NAME (string) of the current selection
if self.nb.select():
i = self.nb.index('current')
# get the Tab text
tab_text = self.nb.tab(i)['text']
else:
return
# remove previous figures ... not sure...
# the used memory as seen in Task Manager still increases
if self.canvas_list:
for cnv in self.canvas_list:
cnv.figure.get_axes().clear()
cnv.get_tk_widget().pack_forget()
cnv.get_tk_widget().destroy()
cnv._tkcanvas.pack_forget()
cnv._tkcanvas.destroy()
cnv = None
if self.figs_list:
for fig in self.figs_list:
fig.delaxes(fig.gca())
plt.cla()
fig.clf()
fig.clear()
plt.close(fig)
self.figs_list = []
# remove toolbar
for widget in tab_path.winfo_children():
widget.pack_forget()
widget.destroy()
self.nb.update() #!!!!!!!!!!!!
######## gc.collect()
# prepare plotting
fig = Figure(figsize=(7, 5), dpi=100)
ax = fig.add_subplot(111)
ax.plot(self.x, self.df.iloc[:,i+1], 'b-', linewidth=1, label=tab_text)
ax.set_xlabel('index')
ax.set_title(self.hdrs[i], fontsize = 8)
ax.legend(loc='best')
ax.grid()
# add to list of figures
self.figs_list.append(fig)
# add to list of axes
self.ax_list.append(ax)
canvas = FigureCanvasTkAgg(fig, master=tab_path)
# add to list of canvases
self.canvas_list.append(canvas)
## self.canvas.draw()
canvas.draw_idle()
canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
toolbar = NavigationToolbar2Tk(canvas, tab_path)
# add to list of toolbars
self.toolbars_list.append(toolbar)
toolbar.update()
canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=1)
def clearPlot(self):
""" not used"""
pass
app = Graphs()
app.title('CSV Plots')
app.geometry('800x600+400+150')
app.resizable(True, True)
app.mainloop();
我使用以下代码创建了一个有效的 csv 文件:
import numpy as np
import pandas as pd
np.random.seed(0)
df = pd.DataFrame(np.random.randn(50,4), columns=['ALPHA', 'BETA', 'GAMMA', 'DELTA'])
df.index.names = ['Rec']
df.index = df.index + 1
df.to_csv('example.csv', index=True)
print(df)
我很抱歉发了这么长的帖子。我真的不知道从这里出发,所以任何帮助将不胜感激。
【问题讨论】:
-
您好您的脚本工作,除了笔记本不是创建的问题。你能控制吗?
-
tab = tk.Frame(self.nb, name=hdr.lower()),这个小部件在哪里打包?
-
@1966bc:您好,感谢您的反馈。如果我没有遗漏什么,按照一些关于 tkinter notebook 的文档,我创建了一个 tabControl(又名 notebook):self.nb = ttk.Notebook(self);打包由:self.nb.pack(fill='both', expand=1)。
标签: python-3.x matplotlib memory tkinter