Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

[Bug]: Memory leak from repeated live plotting #21595

Closed
Labels
@bencaterine

Description

@bencaterine

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions


      [8]ページ先頭

      ©2009-2025 Movatter.jp