Tkinter嵌入Matplotlib与Shapely绘图时,如何清除旧子图并更新线条样式(避免新图叠加/下方新增)
Tkinter嵌入Matplotlib与Shapely绘图时,如何清除旧子图并更新线条样式(避免新图叠加/下方新增)
我完全懂你的困扰——每次切换颜色或线宽,新的绘图就直接堆在旧图下面,用plt.clf()根本清不掉旧图,销毁整个窗口重建又显得特别笨拙。其实问题出在你当前的代码逻辑里:每次调用CreatePlot都会新建一个Matplotlib的Figure和Tkinter的Canvas控件,相当于不断往窗口里添加新的绘图区域,而不是更新原来的那个。
下面我给你调整后的代码,核心思路是复用同一个绘图对象(Figure、Axes、Canvas),只更新绘图内容,而不是每次都新建控件:
from tkinter import * import tkinter as tk from shapely.geometry import LineString from shapely.plotting import plot_line from matplotlib.figure import Figure from matplotlib.backends.backend_tkagg import (FigureCanvasTkAgg, NavigationToolbar2Tk) root = tk.Tk() root.title("DATA") topFrame = tk.Frame(root) bottomFrame = tk.Frame(root) topFrame.pack(side='top', fill='x', padx=5, pady=5) bottomFrame.pack(side='bottom', fill='both', expand=True, padx=5, pady=5) # 全局变量:复用的绘图对象 peekFigure = Figure(figsize=(6, 4), dpi=100) ax = peekFigure.add_subplot(111) canvas = None toolbar = None # 初始化绘图(只执行一次) def InitPlot(): global canvas, toolbar # 设置轴的基础属性 ax.set_aspect('equal') ax.axis('off') # 创建Canvas并关联到Tkinter的frame canvas = FigureCanvasTkAgg(peekFigure, master=bottomFrame) canvas.draw() # 添加导航工具栏(可选,不需要可以注释掉) toolbar = NavigationToolbar2Tk(canvas, bottomFrame) toolbar.update() # 把Canvas的widget放到bottomFrame里,只需要pack一次 canvas.get_tk_widget().pack(fill='both', expand=True) # 更新绘图内容的函数(替换原来的CreatePlot) def UpdatePlot(lineColour, lineWidth): width = lineWidth colour = "#" + lineColour # 清除当前轴的所有内容 ax.clear() # 重新设置轴属性(clear会重置这些配置) ax.set_aspect('equal') ax.axis('off') # 绘制Shapely的线 sample = LineString([(1.0, 1.0), (2.0, 2.0)]) plot_line(sample, ax=ax, add_points=False, alpha=0.7, color=colour, linewidth=lineWidth) # 刷新Canvas显示 canvas.draw() # 单选按钮的回调函数 def Selection(): # 获取选中的颜色和宽度,更新绘图 UpdatePlot(cVar.get(), wVar.get()) # 初始化界面和绘图 InitPlot() # 绘制初始图形 UpdatePlot('ff0000', 5) # 创建退出按钮 buttonExit = Button(bottomFrame, text="Exit", command=root.destroy, width=20) buttonExit.pack(pady=5) # 颜色单选按钮区域 labelColour = tk.Label(topFrame, text=" Colour") labelColour.grid(row=0, column=1, sticky='w', padx=3, pady=2) colours = [{"colour": 'red', "value": 'ff0000'}, {"colour": 'blue', "value": "0000ff"}, {"colour": 'amber', "value": 'ffa500'}, {"colour": 'magenta', "value": 'ff00ff'}] cVar = tk.StringVar() radC = {} for rowNo, c in enumerate(colours, start=1): radC[f'{rowNo}'] = tk.Radiobutton(topFrame, text=c["colour"], value=c["value"], variable=cVar, command=Selection) radC[f'{rowNo}'].grid(row=rowNo, column=1, sticky='w', padx=3, pady=2) if rowNo == 1: radC[f'{rowNo}'].select() # 线宽单选按钮区域 label3 = tk.Label(topFrame, text="Line Width") label3.grid(row=0, column=2, padx=3, pady=2) widths = ["5", "10", "20", "30"] wVar = tk.IntVar() radW = {} for rowNo, width in enumerate(widths, start=1): radW[f'{rowNo}'] = tk.Radiobutton(topFrame, text=width, value=width, variable=wVar, command=Selection) radW[f'{rowNo}'].grid(row=rowNo, column=2, sticky='w', padx=3, pady=2) if rowNo == 1: radW[f'{rowNo}'].select() root.mainloop()
关键修改点说明:
- 复用绘图对象:把
Figure、Axes、Canvas都变成全局变量,初始化时只创建一次,后续只更新内容,不再新建。 - 分离初始化和更新:
InitPlot只执行一次,负责创建绘图控件并放到窗口里;UpdatePlot负责清除旧内容、重新绘制线条、刷新显示。 - 避免PyPlot状态混乱:全程使用Matplotlib的面向对象API(直接操作
Figure和Axes),不再混用plt.figure这种脚本式API,这在Tkinter嵌入场景下更稳定。 - 清除旧内容用
ax.clear():替代plt.clf(),精准清除当前轴的所有元素,不会影响整个Figure。 - 只Pack一次Canvas控件:初始化时把Canvas的widget放到
bottomFrame里,后续更新只需要调用canvas.draw()刷新,不会再添加新的控件到窗口中,彻底解决新图叠加的问题。
这样修改后,你切换颜色或线宽时,原来的绘图区域会直接更新内容,不会再出现新图叠在下面的情况,体验会流畅很多~
备注:内容来源于stack exchange,提问作者Helidad0
相关产品推荐
相关产品推荐

