Uh oh!
There was an error while loading.Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork7.9k
Description
Bug summary
Memory leak from repeated live plotting: seems to be caused bytransform._parents
growing continuously over time, as in#11956 and#11972.
Code for reproduction
importdatetime#import tkinter for GUIimporttkinterastkfromtkinterimportW,LEFT#font typesTITLE_FONT= ("Verdana",14,'bold')LARGE_FONT= ("Verdana",12)MEDIUM_FONT= ("Verdana",10)SMALL_FONT= ("Verdana",8)#import stuff for graphimportmatplotlibfrommatplotlibimporttickerasmtickerfrommatplotlib.backends.backend_tkaggimportFigureCanvasTkAgg,NavigationToolbar2Tkmatplotlib.use('TkAgg')frommatplotlibimportfigurefrommatplotlibimportdatesasmdates#import animation to make graph liveimportmatplotlib.animationasanimationfrommatplotlibimportstylestyle.use("seaborn-darkgrid")importrandomasrnum_contacts=5importcProfileimportpstatsimportio#create figure for plots and set figure size/layoutf=figure.Figure(figsize=(16.6,15),dpi=100,facecolor='white')f.subplots_adjust(top=0.993,bottom=0.015,left=0.04,right=0.96,hspace=0.65)param_dict= {}param_list= ['pH','TDS (ppm)','Rela. Humidity (%)','Air Temp (\N{DEGREE SIGN}C)','Water Temp (\N{DEGREE SIGN}C)','Water Level (cm)']param_ylim= [(0,15), (0,15), (0,15), (0,15), (0,15), (0,15)]live_dict= {}classLive_Text:def__init__(self,label):self.label=labelclassSensor_Plot:def__init__(self,plot,tList,x_ax,ylim,param,incoming_data,plot_color):self.plot=plotself.tList=tListself.x_ax=x_axself.ylim=ylimself.param=paramself.incoming_data=incoming_data#<- graph is bound by incoming data and Data Summary Table displays most recent value 20 of themself.plot_color=plot_color#initially 'b' for alldefmake_plot(self):self.plot.clear()self.plot.set_xlabel('Time')self.plot.set_ylabel(self.param)self.plot.set_ylim(self.ylim)self.x_ax.xaxis_date()self.x_ax.xaxis.set_major_formatter(mdates.DateFormatter('%a %I:%M:%S %p')) [tk.set_visible(True)fortkinself.x_ax.get_xticklabels()] [label.set_rotation(10)forlabelinself.x_ax.xaxis.get_ticklabels()]#slant the x axis tick labels for extra coolnessiflen(self.tList)>4:self.x_ax.set_xlim(self.tList[-2],self.tList[0])self.x_ax.xaxis.set_major_locator(mticker.MaxNLocator(nbins=4))self.plot.fill_between(self.tList,self.incoming_data,#where=(self.incoming_data > [0]*len(self.incoming_data))facecolor=self.plot_color,edgecolor=self.plot_color,alpha=0.5)#blue @initilizationdefinitialize_plots():#intiailizes plots...globalinitialize_plotstry:most_recent= [(int(round(datetime.datetime.now().timestamp())),)+tuple([r.randint(1,10),r.randint(1,10),r.randint(1,10),r.randint(1,10),r.randint(1,10),r.randint(1,10)])]fori,paraminenumerate(param_list,1):tList= []most_recent_any_size= []forjinrange(len(most_recent)):#time_f = datetime.strptime(most_recent[j][0], "%m/%d/%Y %H:%M:%S")time_f=datetime.datetime.fromtimestamp(most_recent[j][0])tList.append(time_f)most_recent_any_size.append(most_recent[j][i])subplot=f.add_subplot(6,2,i)# sharex?x_ax=f.get_axes()current_plot=Sensor_Plot(subplot,tList,x_ax[i-1],param_ylim[i-1],param,most_recent_any_size,'b')param_dict[param]=current_plotcurrent_plot.make_plot()except:#if there is no data points available to plot, initialize the subplotsfori,paraminenumerate(param_list,1):subplot=f.add_subplot(6,2,i)x_ax=f.get_axes()current_plot=Sensor_Plot(subplot, [],x_ax[i-1],param_ylim[i-1],param, [],'b')param_dict[param]=current_plot#current_plot.make_plot()#reader.commit()initialize_plots=_plots_initializeddef_plots_initialized():#ensures plots only intialized once though!passinitialize_plots()###ANIMATE FUNCTION, REMOVE LAST ITEM FROM MOST_RECENT_ANY LIST AND INSERT FRESHLY CALLED VALUE TO BE FIRST IN LISTdefanimate(ii):profile=cProfile.Profile()profile.enable()whileTrue:most_recent_time_graphed=param_dict[param_list[0]]#first, pulls up first plot#most_recent = reader.query_by_num(table="SensorData", num=1)#generate fake sensor data heremost_recent= [(int(round(datetime.datetime.now().timestamp())),)+tuple([r.randint(1,10),r.randint(1,10),r.randint(1,10),r.randint(1,10),r.randint(1,10),r.randint(1,10)])]#log time in unix as intprint(most_recent)#reader.commit() #if identical, do not animate#then checks that plot's time listif (len(most_recent)==0):break#time_reader = datetime.strptime(most_recent[0][0], "%m/%d/%Y %H:%M:%S")time_reader=datetime.datetime.fromtimestamp(most_recent[0][0])if (len(most_recent_time_graphed.tList)!=0)and (time_reader==most_recent_time_graphed.tList[0]):fori,paraminenumerate(param_list,1):current_text=live_dict[param]current_text.label.config(text=most_recent[0][i],fg="black",bg="white")break#checks if the timestamp is exactly the same as prior, i.e. no new data points have been logged in this frame#do I have to add an else?else:fori,keyinenumerate(param_dict,1):current_plot=param_dict[key]current_param_val=float(most_recent[0][i])current_text=live_dict[key]#update to live text data summarycurrent_text.label.config(text=most_recent[0][i],fg="black",bg="white")current_plot.plot_color='g'data_stream=current_plot.incoming_datatime_stream=current_plot.tListdata_stream.insert(0,most_recent[0][i])#time_f = datetime.strptime(most_recent[0][0], "%m/%d/%Y %H:%M:%S")time_f=datetime.datetime.fromtimestamp(most_recent[0][0])time_stream.insert(0,time_f)iflen(data_stream)<20:#graph updates, growing to show 20 pointscurrent_plot.make_plot()else:#there are 20 points and more available, so animation occursdata_stream.pop()time_stream.pop()current_plot.make_plot()breakiftime_reader.minute==50andmost_recent_time_graphed.tList[1].minute==49:profile.disable()result=io.StringIO()pstats.Stats(profile,stream=result).print_stats()result=result.getvalue()# chop the string into a csv-like bufferresult='ncalls'+result.split('ncalls')[-1]result='\n'.join([','.join(line.rstrip().split(None,5))forlineinresult.split('\n')])# save it to diskwithopen('testing.csv','r')asf:result=f.read()+'hour: '+str(time_reader.hour)+'\n'+resultwithopen('testing.csv','w+')asf:f.write(result)f.close()else:profile.disable()#initializationclassAllWindow(tk.Tk):def__init__(self,*args,**kwargs):tk.Tk.__init__(self,*args,**kwargs)#add titletk.Tk.wm_title(self,"Matplotlib Live Plotting")container=tk.Frame(self)container.pack(side="top",fill="both",expand=True)container.grid_rowconfigure(0,weight=1)container.grid_columnconfigure(0,weight=1)#show the framesself.frames= {}#remember to add page to this list when making new onesframe=HomePage(container,self)#set background color for the pagesframe.config(bg='white')self.frames[HomePage]=frameframe.grid(row=0,column=0,sticky="nsew")self.show_frame(HomePage)defshow_frame(self,cont):frame=self.frames[cont]frame.tkraise()#end program fcn triggered by quit buttondefdie(self):exit()#add home pageclassHomePage(tk.Frame):def__init__(self,parent,controller):tk.Frame.__init__(self,parent)canvas=FigureCanvasTkAgg(f,self)#background = canvas.copy_from_bbox(f.bbox)canvas.draw()#embed graph into canvascanvas.get_tk_widget().pack(side=tk.TOP,fill=tk.BOTH,expand=True)#add navigation bartoolbar=NavigationToolbar2Tk(canvas,self)toolbar.update()#data table labelsfori,paraminenumerate(param_list):#tk.Label self refers to Homepageparam_label=tk.Label(self,text=param,fg="black",bg="white",font=MEDIUM_FONT,borderwidth=2,relief="ridge",width=16,height=1,anchor=W,justify=LEFT)param_label.place(x=5,y=500+22*i)fori,paraminenumerate(param_list):loading_text=tk.Label(self,text="Loading",fg="black",bg="white",font=MEDIUM_FONT,borderwidth=2,relief="ridge",width=7,height=1)loading_text.place(x=140,y=500+22*i)current_text=Live_Text(loading_text)live_dict[param]=current_textapp=AllWindow()#app.geometry('1280x623')app.geometry('1917x970')#update animation firstani=animation.FuncAnimation(f,animate,interval=100)#mainloopapp.mainloop()
Actual outcome
The program eventually crashes after running for several days. Memory usage gradually increases throughout the program runtime. Additionally, profiling the animate function revealed that_invalidate_internal
in Matplotlib seemed to be growing in its execution time. This is in line with#11956 and#11972, since_invalidate_internal
is searching throughtransform._parents
, which grows over time. However, these issues were resolved, so I am unsure why I am encountering the same problem.
Expected outcome
Expect the program to run continuously and indefinitely without memory usage increasing over time.
Operating system
Raspbian GNU/Linux 10 (buster)
Matplotlib Version
3.4.3
Matplotlib Backend
Qt5Agg
Python version
3.9
Jupyter version
No response
Other libraries
No response
Installation
pip
Conda channel
No response