Tkinter循环生成新图时工具栏占用空间持续增大的问题求助
我最近做GUI的时候碰到个棘手的问题:点击按钮会循环生成图像,还给图像加了只保留缩放和主页的自定义工具栏。但每次循环,我明明已经把frame1里的所有子组件都销毁了,可工具栏却会占越来越多的空间,导致图像框越来越宽。试了销毁控件、清空画布各种办法都没用,感觉问题大概率出在pick_single函数里,可能旧工具栏有什么残留没清干净。
相关代码片段如下:
#function to remove several of the toolbar buttons and only keep zoom and home class Toolbar(NavigationToolbar2Tk): NavigationToolbar2Tk.toolitems = [t for t in NavigationToolbar2Tk.toolitems if t[0] not in ('Back','Forward','Pan','Subplots','Save',None)] def set_message(self, s): pass def create_plot(image_cur): global counter, txt_files f = Figure(figsize=(plotwidth, plotheight),dpi=dpi,frameon=False,tight_layout = True) f.clear() ax = f.add_subplot(111) p1=ax.imshow(image_cur) ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) ax.set_title(str(counter+1)+' of '+str(len(txt_files)),fontsize=5) return f # function that picks the center of a scale through a mouse click def pickc(event): global st_cent, counter, store_centers,pxmm,trigg,cnt_r x=event.xdata y=event.ydata store_centers[counter,:] = [x,y] if trigg == -1: cnt_r += 1 ratio_coords[cnt_r,:] = [x,y] if cnt_r == 1: trigg = 0 pxmm = np.sqrt((ratio_coords[0,0]-ratio_coords[1,0])**2+(ratio_coords[0,1]-ratio_coords[1,1])**2) print(pxmm) def pick_single(): global st_cent, counter, store_centers, txt_files, plotwidth, plotheight,dpi #destroy all widgets associated with the frame for widget in frame1.winfo_children(): widget.destroy() counter += 1 if counter > len(txt_files): counter = len(txt_files) scale = imageio.imread(txt_files[counter]) # problems are likely somewhere in the remains of the function frame1.update() dpi = root.winfo_fpixels('1i') plotwidth = frame1.winfo_width() / dpi plotheight = frame1.winfo_height() / dpi # create plot fig1 = create_plot(image_cur=scale) canvas = FigureCanvasTkAgg(fig1, master=frame1) mplcursors.cursor(fig1) canvas.draw() canvas.get_tk_widget().pack(side=tk.RIGHT, fill=tk.X, expand=1) toolbar = Toolbar(canvas, frame1, pack_toolbar=False) toolbar.pack(side=tk.BOTTOM,fill=X) toolbar.update() fig1.canvas.callbacks.connect('button_press_event', pickc)
我试过销毁frame里的所有控件、清空画布,但都没解决问题,有没有大佬能帮忙看看?
问题分析&解决方案
我试着琢磨了下,你每次循环都重新创建Toolbar和FigureCanvasTkAgg,虽然调用了widget.destroy(),但可能Matplotlib和Tkinter绑定的一些底层资源没被彻底释放,而且你是基于当前frame1的尺寸计算新图的宽高,旧工具栏残留的布局影响了frame1的尺寸,导致新图尺寸计算偏差,形成恶性循环。
给你几个可行的调整方向:
彻底清理旧的Figure资源
除了销毁Tkinter控件,还要手动关闭旧的Figure,避免内存泄漏。可以在pick_single开头加一段:global fig1 if 'fig1' in globals() and fig1 is not None: plt.close(fig1) # 或者fig1.clear() fig1 = None提前初始化画布和工具栏,避免重复创建
把canvas和toolbar的创建移到循环外面,每次只更新Figure内容,从根源避免重复创建工具栏。修改后的pick_single大概是这样:# 提前把这些变量设为全局,只初始化一次 global fig1, canvas, toolbar fig1 = None canvas = None toolbar = None def pick_single(): global st_cent, counter, store_centers, txt_files, plotwidth, plotheight,dpi, fig1, canvas, toolbar counter += 1 if counter > len(txt_files): counter = len(txt_files) scale = imageio.imread(txt_files[counter]) dpi = root.winfo_fpixels('1i') plotwidth = frame1.winfo_width() / dpi plotheight = frame1.winfo_height() / dpi # 第一次运行时初始化Figure、canvas和toolbar if fig1 is None: fig1 = Figure(figsize=(plotwidth, plotheight),dpi=dpi,frameon=False,tight_layout = True) canvas = FigureCanvasTkAgg(fig1, master=frame1) mplcursors.cursor(fig1) canvas.get_tk_widget().pack(side=tk.RIGHT, fill=tk.X, expand=1) toolbar = Toolbar(canvas, frame1, pack_toolbar=False) toolbar.pack(side=tk.BOTTOM,fill=X) fig1.canvas.callbacks.connect('button_press_event', pickc) else: # 不是第一次就清空旧内容 fig1.clear() # 绘制新图 ax = fig1.add_subplot(111) p1=ax.imshow(scale) ax.get_xaxis().set_visible(False) ax.get_yaxis().set_visible(False) ax.set_title(str(counter+1)+' of '+str(len(txt_files)),fontsize=5) canvas.draw() toolbar.update() frame1.update_idletasks()销毁组件后强制刷新布局
在销毁frame1子组件后,调用frame1.update_idletasks()和frame1.update(),确保Tkinter彻底更新布局状态,再计算新图尺寸:for widget in frame1.winfo_children(): widget.destroy() frame1.update_idletasks() frame1.update()
另外,你也可以试试在销毁时针对性处理工具栏:
for widget in frame1.winfo_children(): if isinstance(widget, Toolbar): widget.destroy() toolbar = None # 释放引用 else: widget.destroy()
这样应该能彻底清理旧工具栏的残留资源,解决空间越来越大的问题。
备注:内容来源于stack exchange,提问作者Julien Chaput

