我正在尝试为回流炉控制器制作简单的GUI。GUI每1s采样一次温度(现在它是从随机函数获得的,最终是从arduino获得的)。但是现在ram的使用量每1s增加大约3.5MB——我想这与matplot动画有关,它只是在旧图像(第103-115行)的基础上绘制新图像,但是如何解决这个问题呢?它运行约385秒,然后:
C:\用户\veeti\PycharmProjects\Reflow\venv\脚本\python.exeC:/用户/veeti/PycharmProjects/Reflow/Main.py异常在Tkinter回调Traceback(最近一次调用): File"C:\Program Files(x86)\python37\lib\tkinter__init__. py", line 1705, in call返回self.func(*args)文件"C:\Program Files(x86)\python37\lib\tkinter__init__. py",第749行,在Callit func(*args)文件"C:\用户\veeti\PycharmProjects\Reflow\venv\lib\site-包\matplotlib\backends_backend_tk.py",第118行,在_on_timerTimerBase._on_timer(自己)文件"C:\用户\veeti\PycharmProjects\Reflow\venv\lib\site-包\matplotlib\backend_bases.py",第1194行,_on_timerret=func(*args,**kwargs)文件"C:\用户\veeti\PycharmProjects\Reflow\venv\lib\site-包\matplotlib\animation.py",第1447行,_stepstill_going=动画。_step(自我,*args)文件"C:\用户\veeti\PycharmProjects\Reflow\venv\lib\site-pack\matplotlib\animation.py",第1173行,_step自我。_draw_next_frame(framedata,自我。_blit)文件"C:\用户\veeti\PycharmProjects\Reflow\venv_draw_frame(framedata)文件"C:\用户\veeti\PycharmProjects\Reflow\venv\lib\site-包\matplotlib\animation.py",第1192行,在_draw_frame。_drawn_artists=自我。_func(framedata,自我。_args)文件"C:/用户/veeti/PycharmProjects/Reflow/Main.py",第115行,在动画canvas.draw()文件"C:\用户\veeti\PycharmProjects\Reflow\venv\lib\site-包\matplotlib\后端\backend_tkagg.py",第9行,在绘图超级(图CanvasTkAgg,文件"C:\用户\veeti\PycharmProjects\Reflow\venv\lib\site-包\matplotlib\后端\backend_agg.py",第386行,在绘制self.renderer=self.get_renderer(清除=True)文件"C:\用户\veeti\PycharmProjects\Reflow\venv\lib\站点包\matplotlib\后端\backend_agg.py",第399行,get_rendererself.renderer=RendererAgg(w,h,self.figure.dpi)File"C:\用户\veeti\PycharmProjects\Reflow\venv\lib\site包\matplotlib\后端\backend_agg.py",line_renderer=_RendererAgg(int(宽), int(高), dpi)MemoryError: In RendererAgg:内存不足
此外,如果没有第124行命令“动画(1)”,动画也不会打开-为什么?
import tkinter
import matplotlib.pyplot as plt
import random
import matplotlib.animation as animation
from matplotlib.backends.backend_tkagg import (
FigureCanvasTkAgg)
def exitHandler():
print("at exit handler")
ani.event_source.stop()
root.destroy()
def startButtonClick():
print("start")
def stopButtonClick():
print("stop")
def readTemperature():
return random.randint(0,250)
def updateTimeLabel(time = -99):
timeLabel.config(text= "Time: {}".format (time))
def updateTargetTempLabel(temp = -99) :
targetTempLabel.config(text="Target temp: \n {} ".format (temp))
def updateCurrentTempLabel(temp = -99) :
currentTempLabel.config(text="Current temp: \n {} ".format (temp))
def updateHeaterStatus(temp,target):
if (temp < target):
heaterStatusLabel.config(text="Heater: \n On")
else:
heaterStatusLabel.config(text="Heater: \n Off")
def calculateTarget(time):
global timePoints, tempQuidance, targetTemp
#find current slope and calculate the target temp
for i in range (len(timePoints)):
if (timePoints[i] < time < timePoints[i+1]):
slope = (tempQuidance[i+1] - tempQuidance[i]) / (timePoints[i+1] -timePoints[i])
currentTarget = (time -timePoints[i]) * slope + tempQuidance[i]
return (currentTarget)
def animate(i):
global timePoints,tempQuidance,numberOfPoints,measuredTemp, time
time = time+1
measuredTemp.append(readTemperature())
numberOfPoints.append(len(measuredTemp))
ax.clear()
ax.plot(timePoints,tempQuidance)
ax.plot(numberOfPoints,measuredTemp)
updateTimeLabel(time)
updateTargetTempLabel(calculateTarget(time))
updateCurrentTempLabel(measuredTemp[-1])
updateHeaterStatus(measuredTemp[-1], 100)
canvas = FigureCanvasTkAgg(fig,plotFrame)
canvas.draw()
canvas.get_tk_widget().grid(row=3,column=0,columnspan = 4)
global measuredTemp, numberOfPoints, time, targetTemp
time = 0
measuredTemp=[]
numberOfPoints=[]
targetTemp = 0
#temperature profile
timePoints = [0,300,400,460,500]
tempQuidance =[0,150,150,250,0]
root=tkinter.Tk()
root.title('Reflow oven controller')
root.geometry("1600x800")
controlFrame = tkinter.LabelFrame(root,width = 500, height = 800, borderwidth = 3, padx=5,pady=5)
plotFrame = tkinter.LabelFrame(root,padx=5,pady=5)
controlFrame.grid(row = 0, column = 0 )
controlFrame.grid_propagate(0)
plotFrame.grid(row = 0, column= 1)
timeLabel=tkinter.Label(plotFrame,text = "Time: ")
targetTempLabel = tkinter.Label(plotFrame,text = "Target temp")
currentTempLabel = tkinter.Label(plotFrame,text = "Current temp")
heaterStatusLabel = tkinter.Label(plotFrame,text = "Heater status: \n unknown")
timeLabel.grid(row= 0, column=0)
targetTempLabel.grid(row=0, column= 1)
currentTempLabel.grid(row=0, column= 2)
heaterStatusLabel.grid(row = 0, column = 3)
fig=plt.figure()
ax = fig.add_subplot(1, 1, 1)
startButton = tkinter.Button(controlFrame, text = "Start", command = startButtonClick)
stopButton = tkinter.Button(controlFrame, text="Stop", command = stopButtonClick)
startButton.grid(row = 0,column = 0)
stopButton.grid(row = 0 ,column= 1 )
animate(1)
ani = animation.FuncAnimation(fig, animate, interval=1000) #run animation every 1s, animate func takes care of getting new values and GUI update
root.protocol("WM_DELETE_WINDOW", exitHandler)
root.mainloop()
thx提前
问题是您正在一次又一次地堆叠画布。我们希望在animate
函数之外创建画布,然后将draw留在函数中,而不是像这样重新创建画布和堆叠它们。
解决方法是更改以下内容:
fig=plt.figure()
ax = fig.add_subplot(1, 1, 1)
def animate(i):
global timePoints,tempQuidance,numberOfPoints,measuredTemp, time
time = time+1
measuredTemp.append(readTemperature())
numberOfPoints.append(len(measuredTemp))
ax.clear()
ax.plot(timePoints,tempQuidance)
ax.plot(numberOfPoints,measuredTemp)
updateTimeLabel(time)
updateTargetTempLabel(calculateTarget(time))
updateCurrentTempLabel(measuredTemp[-1])
updateHeaterStatus(measuredTemp[-1], 100)
canvas = FigureCanvasTkAgg(fig,plotFrame)
canvas.draw()
canvas.get_tk_widget().grid(row=3,column=0,columnspan = 4)
为此:
fig=plt.figure()
ax = fig.add_subplot(1, 1, 1)
canvas = FigureCanvasTkAgg(fig,plotFrame)
canvas.get_tk_widget().grid(row=3,column=0,columnspan = 4)
def animate(i):
global timePoints,tempQuidance,numberOfPoints,measuredTemp, time
time = time+1
measuredTemp.append(readTemperature())
numberOfPoints.append(len(measuredTemp))
ax.clear()
ax.plot(timePoints,tempQuidance)
ax.plot(numberOfPoints,measuredTemp)
updateTimeLabel(time)
updateTargetTempLabel(calculateTarget(time))
updateCurrentTempLabel(measuredTemp[-1])
updateHeaterStatus(measuredTemp[-1], 100)
canvas.draw()
为了遵循PEP8标准,我对您的代码进行了一些清理。见下文:
import tkinter as tk
import matplotlib.pyplot as plt
import random
import matplotlib.animation as animation
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
def exit_handler():
print("at exit handler")
ani.event_source.stop()
root.destroy()
def start_button_click():
print("start")
def stop_button_click():
print("stop")
def read_temperature():
return random.randint(0, 250)
time = 0
measured_temp = []
number_of_points = []
targetTemp = 0
time_points = [0, 300, 400, 460, 500]
temp_quidance = [0, 150, 150, 250, 0]
root = tk.Tk()
root.title('Reflow oven controller')
root.geometry("1600x800")
control_frame = tk.LabelFrame(root, width=500, height=800, borderwidth=3, padx=5, pady=5)
plot_frame = tk.LabelFrame(root, padx=5, pady=5)
control_frame.grid(row=0, column=0)
control_frame.grid_propagate(0)
plot_frame.grid(row=0, column=1)
time_label = tk.Label(plot_frame, text="Time: ")
target_temp_label = tk.Label(plot_frame, text="Target temp")
current_temp_label = tk.Label(plot_frame, text="Current temp")
heater_status_label = tk.Label(plot_frame, text="Heater status: \n unknown")
time_label.grid(row=0, column=0)
target_temp_label.grid(row=0, column=1)
current_temp_label.grid(row=0, column=2)
heater_status_label.grid(row=0, column=3)
def update_time_label(time=-99):
time_label.config(text="Time:\n{}".format(time))
def update_target_temp_label(temp=-99):
target_temp_label.config(text="Target temp:\n{}".format(temp))
def update_current_temp_label(temp=-99) :
current_temp_label.config(text="Current temp:\n{}".format(temp))
def update_heater_status(temp, target):
if temp < target:
heater_status_label.config(text="Heater:\nOn")
else:
heater_status_label.config(text="Heater:\nOff")
def calculate_target(time):
for i in range(len(time_points)):
if time_points[i] < time < time_points[i+1]:
slope = (temp_quidance[i+1] - temp_quidance[i]) / (time_points[i+1] - time_points[i])
current_target = (time - time_points[i]) * slope + temp_quidance[i]
return current_target
fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
canvas = FigureCanvasTkAgg(fig, plot_frame)
canvas.get_tk_widget().grid(row=3, column=0, columnspan=4)
def animate(_=None):
global time
time = time+1
measured_temp.append(read_temperature())
number_of_points.append(len(measured_temp))
ax.clear()
ax.plot(time_points, temp_quidance)
ax.plot(number_of_points, measured_temp)
update_time_label(time)
update_target_temp_label(calculate_target(time))
update_current_temp_label(measured_temp[-1])
update_heater_status(measured_temp[-1], 100)
canvas.draw()
start_button = tk.Button(control_frame, text="Start", command=start_button_click)
stop_button = tk.Button(control_frame, text="Stop", command=stop_button_click)
start_button.grid(row=0, column=0)
stop_button.grid(row=0, column=1)
animate()
ani = animation.FuncAnimation(fig, animate, interval=1000)
root.protocol("WM_DELETE_WINDOW", exit_handler)
root.mainloop()
接近动画开始的内存:
1分钟后记忆:
正如您所看到的,内存增长已经停止,不再是3 MB/秒。
至于需要使用动画()
来启动动画,我不是100%确定。我还没有使用matplotlib
足够了解一切是如何工作的。我甚至不能在这里猜测,所以其他人将不得不回答这部分。