Tkinter集成Matplotlib子图坐标轴更新但折线不显示问题
Tkinter嵌入Matplotlib动态绘图折线不显示问题修复
核心错误点
- 每次刷新绘图时重复新建
FigureCanvasTkAgg画布实例,绘图操作绑定在旧画布的子图对象上,新创建的画布没有同步最新绘制的折线内容,导致折线无法渲染。 - 未保存历史采集的x、y数据序列,每次调用
plot仅传入单个数值点,Matplotlib无法基于孤立单点生成连续折线,同时坐标轴自动缩放逻辑也无法正常适配数据范围。 - 在Tkinter主事件循环中错误调用
plt.show(),该方法仅适用于Matplotlib原生弹出窗口,嵌入Tkinter的画布调用该方法会阻塞事件循环,干扰正常渲染流程。 - 重复对同位置的画布控件调用
grid布局,会造成控件堆叠、内存泄漏,程序运行时间越长卡顿越明显。
修复步骤
- 初始化阶段创建的8个画布实例全程复用,不要在刷新函数里重复新建。
- 为每个子图维护独立的x、y数据列表,每次采集到新数据就追加到对应列表中,再将完整序列传入
plot方法。 - 每次绘制新数据后直接调用对应画布的
draw_idle()方法触发刷新,不需要调用plt.show()。 - 可根据数据长度动态调整x轴显示范围,实现滚动绘图效果,避免大量数据挤在同一画布上。
修正后完整可运行代码
from tkinter import * from tkinter import ttk import time import matplotlib.pyplot as plt from matplotlib.figure import Figure from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg import random root = Tk() root.title("ktu") root.state('zoomed') root.configure(bg="#0059aa") plt.style.use('ggplot') # 数据存储区 packageNum = 0 datas = [] # 为每个图初始化x、y数据存储列表 plot_datas = { "plot1": {"xs": [], "ys": []}, "plot2": {"xs": [], "ys": []}, "plot3": {"xs": [], "ys": []}, "plot4": {"xs": [], "ys": []}, "plot5": {"xs": [], "ys": []}, "plot6": {"xs": [], "ys": []}, "plot7": {"xs": [], "ys": []}, "plot8": {"xs": [], "ys": []}, } def randomData(): global packageNum randData = [] randData.append(356356) packageNum += 1 randData.append(packageNum) randData.append(time.strftime("%H:%M:%S, %d/%m/%Y")) randData.append(random.randint(0,10000)) # yindex 3 randData.append(random.randint(0,10000)) # yindex 4 randData.append(random.randint(0,700)) # yindex 5 randData.append(random.randint(0,700)) # yindex 6 randData.append(randData[6]-randData[5]) randData.append(random.randint(10,20)) # yindex 8 randData.append(random.randint(20,25)) # yindex 9 randData.append(random.randint(39,50)) # yindex 10 randData.append(random.randint(1,6)) # yindex 11 randData.extend([0.0,0.0,random.randint(0,700),0.0,0.0,random.randint(0,700)]) randData.extend(["durum",random.randint(0,100),random.randint(0,100),random.randint(0,100),random.randint(0,50),"hayır"," "]) return randData # 串口面板区 seriPortOuter = LabelFrame(root,text="SERIAL PORT",border=3, width=170,height=210) seriPortOuter.place(x=5,y=305) text1= Label(seriPortOuter,text="get connection", width=14, font=("Helvetica",10), fg="white", bg="red") text1.place(x=3,y=5) text2= Label(seriPortOuter,text="COM Port", width=8, font=("Helvetica",10)) text2.place(x=1,y=30) portList = [f"Option{i}" for i in range(1,7)] portCombo = ttk.Combobox(seriPortOuter, values = portList, width=8) portCombo.set("") portCombo.place(x=68,y=30) text3= Label(seriPortOuter,text="Speed", width=4, font=("Helvetica",10)) text3.place(x=1,y=55) speedList = [f"Option{i}" for i in range(1,7)] speedCombo = ttk.Combobox(seriPortOuter, values = speedList, width=8) speedCombo.set("") speedCombo.place(x=68,y=55) x = 0 def StartListing(): global x, datas root.after(2000,StartListing) datas = randomData() GraphIt(plot1, x, 3, plot1Canvas, plot_datas["plot1"]) GraphIt(plot2, x, 4, plot2Canvas, plot_datas["plot2"]) GraphIt(plot3, x, 5, plot3Canvas, plot_datas["plot3"]) GraphIt(plot4, x, 6, plot4Canvas, plot_datas["plot4"]) GraphIt(plot5, x, 10, plot5Canvas, plot_datas["plot5"]) GraphIt(plot6, x, 9, plot6Canvas, plot_datas["plot6"]) GraphIt(plot7, x, 11, plot7Canvas, plot_datas["plot7"]) GraphIt(plot8, x, 8, plot8Canvas, plot_datas["plot8"]) x += 1 StartListingButton = Button(seriPortOuter, text="Start Listing Datas", command=StartListing, width=19) StartListingButton.place(x=10,y=125) # 图表面板区 graph_canvas = Canvas(root, width=842,height=575, bg="#0059aa" ) graph_canvas.place(x=585,y=30) # 初始化8个子图和对应画布 fig_plot1= Figure(figsize=(2.33,2.5), dpi=100) plot1= fig_plot1.add_subplot(111) plot1.set_title('Pressure1 (Pa)',fontsize=8) plot1Canvas = FigureCanvasTkAgg(fig_plot1, master=graph_canvas) plot1Canvas.get_tk_widget().grid(row=0,column=0,pady=5, padx=3) plot1Canvas.draw() fig_plot2= Figure(figsize=(2.33,2.5), dpi=100) plot2= fig_plot2.add_subplot(111) plot2.set_title('Pressure2 (Pa)',fontsize=8) plot2Canvas = FigureCanvasTkAgg(fig_plot2, master=graph_canvas) plot2Canvas.get_tk_widget().grid(row=1,column=0,pady=5, padx=3) plot2Canvas.draw() fig_plot3= Figure(figsize=(2.33,2.5), dpi=100) plot3= fig_plot3.add_subplot(111) plot3.set_title('altitude1 (m)',fontsize=8) plot3Canvas = FigureCanvasTkAgg(fig_plot3, master=graph_canvas) plot3Canvas.get_tk_widget().grid(row=0,column=1,pady=5, padx=2) plot3Canvas.draw() fig_plot4= Figure(figsize=(2.33,2.5), dpi=100) plot4= fig_plot4.add_subplot(111) plot4.set_title('altitude2 (m)',fontsize=8) plot4Canvas = FigureCanvasTkAgg(fig_plot4, master=graph_canvas) plot4Canvas.get_tk_widget().grid(row=1,column=1,pady=5, padx=2) plot4Canvas.draw() fig_plot5= Figure(figsize=(2.33,2.5), dpi=100) plot5= fig_plot5.add_subplot(111) plot5.set_title('temperature (°C)',fontsize=8) plot5Canvas = FigureCanvasTkAgg(fig_plot5, master=graph_canvas) plot5Canvas.get_tk_widget().grid(row=0,column=2,pady=5, padx=2) plot5Canvas.draw() fig_plot6= Figure(figsize=(2.33,2.5), dpi=100) plot6= fig_plot6.add_subplot(111) plot6.set_title('temperature (°C)',fontsize=8) plot6Canvas = FigureCanvasTkAgg(fig_plot6, master=graph_canvas) plot6Canvas.get_tk_widget().grid(row=1,column=2,pady=5, padx=2) plot6Canvas.draw() fig_plot7= Figure(figsize=(2.33,2.5), dpi=100) plot7= fig_plot7.add_subplot(111) plot7.set_title('voltage (V)',fontsize=8) plot7Canvas = FigureCanvasTkAgg(fig_plot7, master=graph_canvas) plot7Canvas.get_tk_widget().grid(row=0,column=3,pady=5, padx=2) plot7Canvas.draw() fig_plot8= Figure(figsize=(2.33,2.5), dpi=100) plot8= fig_plot8.add_subplot(111) plot8.set_title('descent velocity (m/sn)',fontsize=8) plot8Canvas = FigureCanvasTkAgg(fig_plot8, master=graph_canvas) plot8Canvas.get_tk_widget().grid(row=1,column=3,pady=5, padx=2) plot8Canvas.draw() def GraphIt(myplot, xindex, yindex, canvas, data_store): # 追加新数据到存储列表 data_store["xs"].append(xindex) data_store["ys"].append(datas[yindex]) # 清空上次绘制内容,重绘全量数据 myplot.clear() myplot.plot(data_store["xs"], data_store["ys"]) # 自动适配坐标轴范围,保留最近20个点做滚动显示 if len(data_store["xs"]) > 20: myplot.set_xlim(left=data_store["xs"][-20], right=data_store["xs"][-1]) # 复用已有画布刷新 canvas.draw_idle() root.mainloop()
内容的提问来源于stack exchange,提问作者ayse
相关产品推荐
相关产品推荐

