Python 慢速动画脚本
我在windows 10 PC上使用python 3.6(最新版本)和spider解释器,使用的硬件超过平均水平,并编写了一个脚本,使我能够在安捷伦频率计数器上连续测量和绘制两个通道的频率,并将数据保存到txt文件中。此外,我还必须将脚本转换为.exe文件(使用pyinstaller),以便将其分发到多台PC上 在测量时间达到大约2000秒之前,一切正常,即使是从.exe文件开始。然后,软件开始变得非常慢,直到它甚至显示出windows在打印时所做的Python 慢速动画脚本,python,python-3.x,animation,matplotlib,Python,Python 3.x,Animation,Matplotlib,我在windows 10 PC上使用python 3.6(最新版本)和spider解释器,使用的硬件超过平均水平,并编写了一个脚本,使我能够在安捷伦频率计数器上连续测量和绘制两个通道的频率,并将数据保存到txt文件中。此外,我还必须将脚本转换为.exe文件(使用pyinstaller),以便将其分发到多台PC上 在测量时间达到大约2000秒之前,一切正常,即使是从.exe文件开始。然后,软件开始变得非常慢,直到它甚至显示出windows在打印时所做的“窗口不应答” 我试图激活FuncAnimat
“窗口不应答”
我试图激活FuncAnimate
的闪烁功能,但当我这样做时,它只显示一个白色窗口
因此,我现在正在寻找提高软件速度的选项,特别是在高数据量的情况下,而不需要切割大量数据(需要从t=0
到t=whatever
)
为什么blit=True
会杀死我的动画?还是有更好的方法可以快速、反复地绘制这些数据量
我的代码:
#Importing all required additional Python packages and sub-packages/functions
import visa, time, tkinter,sys
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg as FigureCanvas
from tkinter import messagebox, filedialog
from matplotlib.figure import Figure
import matplotlib.animation as animation
#Initialize the tkinter main window (graphical user interface window)
root=tkinter.Tk()
root.geometry('1200x900')
#Initialize the VISA resource manager and define a list object
rm=visa.ResourceManager('C:\\Windows\\System32\\visa32.dll')
liste=[]
#lists all available VISA resources and adds them to the list object
#one entry per VISA instrument
string=str(rm.list_resources())[1:-1]
liste.append(string.split('\'')[1:-1])
#opens a message box for each object in the list (each instrument)
#if user chosses "yes" for a VISA resource, the software tries to access the device
#if the device is present, the for-loop is left, otherwise the whole software quits
for i in liste[0]:
box=messagebox.askyesno('VISA Resources','Is this the correct VISA-Resource?'+'\n'+str(i))
if box==True:
try: inst=rm.open_resource(i)
except:
messagebox.showerror('Wrong Resource','The VISA resource was wrong!')
root.destroy()
sys.exit()
break
elif box==False: continue
#checks if the VISA resource is actually existent and present
try: inst
except:
messagebox.showerror('No Resource found','No VISA Resource was chosen.')
root.destroy()
sys.exit()
#opens a file dialog window and safes the chosen location as "filename"
#furthermore checks if the user specified a valid path or quited by using "cancel"
#if the user clicked "cancel", the software quits
filename=filedialog.asksaveasfilename()
if len(filename)==0:
messagebox.showerror('No File','No file location was specified.')
root.destroy()
sys.exit()
#definition of variables as well as the update function
x_data,y_data1, y_data2, y_data3=[],[],[],[]
def update(frame):
#create X-data, seconds since .clock was first called
x_data.append(time.clock())
#read out current freq on channel1 and float it (originaly returned as string)
value1=float(inst.query('MEAS:FREQ? 10 MHz, 0.1 Hz, (@1)'))
#add data to list
y_data1.append(value1)
#define and automatically adjust subplot limits
subplot1.set_ylim(min(y_data1)-1,max(y_data1)+1)
subplot1.set_xlim(min(x_data)-1,max(x_data)+1)
#define subplot title and labels
subplot1.set_title('Channel 1')
subplot1.set_xlabel('Time')
subplot1.set_ylabel('Frequency')
#same as above for second channel
value2=float(inst.query('MEAS:FREQ? 10 MHz, 0.1 Hz, (@2)'))
y_data2.append(value2)
subplot2.set_ylim(min(y_data2)-1,max(y_data2)+1)
subplot2.set_xlim(min(x_data)-1,max(x_data)+1)
subplot2.set_title('Channel 2')
subplot2.set_xlabel('Time')
subplot2.set_ylabel('Frequency')
#calculates and plots the difference of the upper two channels
y_data3.append(value1-value2)
subplot3.set_ylim(min(y_data3)-1,max(y_data3)+1)
subplot3.set_xlim(min(x_data)-1,max(x_data)+1)
subplot3.set_title('Difference')
subplot3.set_xlabel('Time')
subplot3.set_ylabel('Frequency')
#plots the subplots in the main plot frame
subplot1.plot(x_data,y_data1,'b')
subplot2.plot(x_data, y_data2,'r')
subplot3.plot(x_data, y_data3,'g')
#writes all data do a new file defined before
newfile.write(str(time.clock())+', '+str(value1)+', ' +str(value2)+'\n')
#enables the code to make use of the defined variables/data
return x_data, y_data1, y_data2, y_data3
#create a global boolean variable and set it to "True"
global boolean
boolean=True
#define a Pause function using the global boolean variable
def Pause():
global boolean
#if the boolean is True, the animation stops and the variable is set to False
if boolean==True:
anim.event_source.stop()
boolean=False
#if the boolean is False, the animation continues and the variable is set to True
elif boolean==False:
anim.event_source.start()
boolean=True
#define a Quit function that quits the application and closes the created file
def Quit():
newfile.close()
root.destroy()
#define a function that applies the user input data aquisition time to the animation
def SpeedApply():
anim.event_source.interval=int(float(Interv.get())*1000)
#create and place different buttons that call the defined functions upon buttonclick
QuitBut=tkinter.Button(text='Quit', command=Quit)
QuitBut.place(x=15,y=15)
StartBut=tkinter.Button(text='Pause/Resume',command=Pause)
StartBut.place(x=55, y=15)
Interv=tkinter.Spinbox(root,values=(0.1,0.2,0.5,1,1.5,2), width=8)
Interv.place(x=160, y=17)
Interv.delete(0,'end')
Interv.insert(0,1)
Speedbut=tkinter.Button(text='Apply Speed', command=SpeedApply)
Speedbut.place(x=250, y=15)
#create the figure needed to plot the animated data
figure=Figure(figsize=(8,8), dpi=100)
subplot1=figure.add_subplot(311)
subplot2=figure.add_subplot(312)
subplot3=figure.add_subplot(313)
figure.subplots_adjust(hspace=0.6)
#create a tkinter canvas, needed to embedd the figure into a tkinter root window
canvas=FigureCanvas(figure,root)
canvas.draw()
#canvas.start_event_loop(0.001)
canvas.get_tk_widget().place(x=25,y=50, height=850, width=1150)
#create the newfile where the data will be stored lateron
newfile=open(filename+time.strftime('%d')+time.strftime('%m')+time.strftime('%y')+'.txt','w')
newfile.write('Time, Channel 1, Channel 2\n')
#animation calling the update function upon the figure in the canvas with an interval of 1 second
anim=animation.FuncAnimation(figure,update, blit=False, interval=1000)
#tkinter mainloop, needed to react to user input in tkinter GUI
root.mainloop()
好的,按照要求,一个更简单的脚本显示了我的问题:
import time, tkinter
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg as FigureCanvas
from matplotlib.figure import Figure
import matplotlib.animation as animation
#Initialize the tkinter main window (graphical user interface window)
root=tkinter.Tk()
root.geometry('1200x900')
value1=1
value2=2
#definition of variables as well as the update function
x_data,y_data1, y_data2, y_data3=[],[],[],[]
def update(frame):
#create X-data, seconds since .clock was first called
x_data.append(time.clock())
global value1, value2, value3
value1=value1+2
#add data to list
y_data1.append(value1)
#define and automatically adjust subplot limits
subplot1.set_ylim(min(y_data1)-1,max(y_data1)+1)
subplot1.set_xlim(min(x_data)-1,max(x_data)+1)
#define subplot title and labels
subplot1.set_title('Channel 1')
subplot1.set_xlabel('Time')
subplot1.set_ylabel('Frequency')
#same as above for second channel
value2=value2+1
y_data2.append(value2)
subplot2.set_ylim(min(y_data2)-1,max(y_data2)+1)
subplot2.set_xlim(min(x_data)-1,max(x_data)+1)
subplot2.set_title('Channel 2')
subplot2.set_xlabel('Time')
subplot2.set_ylabel('Frequency')
#calculates and plots the difference of the upper two channels
y_data3.append(value1-value2)
subplot3.set_ylim(min(y_data3)-1,max(y_data3)+1)
subplot3.set_xlim(min(x_data)-1,max(x_data)+1)
subplot3.set_title('Difference')
subplot3.set_xlabel('Time')
subplot3.set_ylabel('Frequency')
#plots the subplots in the main plot frame
subplot1.plot(x_data,y_data1,'b')
subplot2.plot(x_data, y_data2,'r')
subplot3.plot(x_data, y_data3,'g')
#enables the code to make use of the defined variables/data
return x_data, y_data1, y_data2, y_data3
#create a global boolean variable and set it to "True"
global boolean
boolean=True
#define a Quit function that quits the application and closes the created file
def Quit():
root.destroy()
#create and place different buttons that call the defined functions upon buttonclick
QuitBut=tkinter.Button(text='Quit', command=Quit)
QuitBut.place(x=15,y=15)
#create the figure needed to plot the animated data
figure=Figure(figsize=(8,8), dpi=100)
subplot1=figure.add_subplot(311)
subplot2=figure.add_subplot(312)
subplot3=figure.add_subplot(313)
figure.subplots_adjust(hspace=0.6)
#create a tkinter canvas, needed to embedd the figure into a tkinter root window
canvas=FigureCanvas(figure,root)
canvas.draw()
#canvas.start_event_loop(0.001)
canvas.get_tk_widget().place(x=25,y=50, height=850, width=1150)
#animation calling the update function upon the figure in the canvas with an interval of 1 second
anim=animation.FuncAnimation(figure,update, blit=False, interval=100)
#tkinter mainloop, needed to react to user input in tkinter GUI
root.mainloop()
我在代码中留下了“美丽气质”之类的标题。
要真正看到我遇到的问题,您必须运行该脚本至少600秒,2000秒后问题会变得“更严重”。您的大部分问题似乎是每次都必须重新计算整个列表的最小值和最大值 e、 g.您的子批次x和y最小/最大值,而不是处理整个附加列表,您应该使用一个变量来保存迄今为止的最低和最高值,将当前输入数据与它们进行比较,如果它满足条件,则更新它们
subplot1.set_ylim(min(y_data1)-1,max(y_data1)+1)
subplot1.set_xlim(min(x_data)-1,max(x_data)+1)
您似乎每次更新都会重置子批次标签,现在我对TKinter中的GUI不太熟悉,但我觉得这可以在初始化定义中涵盖
subplot1.set_title('Channel 1')
subplot1.set_xlabel('Time')
subplot1.set_ylabel('Frequency')
第一个建议应该让你的程序在一个更一致的时间内运行,而不是逐渐变慢,另一个建议应该能够减少每次更新的工作量 您应该创建一个最小的工作示例来重现问题,并提供一些示例数据。这里有大量可能与问题无关的代码。感谢您的建议,它似乎正在工作(仍需要进一步测试)。但是,我试图将blit=True设置为True,但出现了以下错误:AttributeError:“list”对象没有属性“set_animated”,那么为什么在本例中blit不起作用呢?脚本是否足够快,即使它必须在每个动画中重新绘制每个数据点?我会说,这已达到我所知的极限,因为从未使用matplotlib,这里似乎已经讨论了blit问题:每个更新对性能的影响有多大,这在很大程度上取决于您处理的数据量,因为min/max函数会影响列表的大小,我会说这是一个非常大的集合,如果这样的话,您可以通过简单的缩减来实现,例如,记录所有内容,但有一个X分钟的可滚动显示,这样您只会设置一个固定大小的子集。好的,我应用了Reroute建议的更改,它们至少有助于减少一点延迟。但是,脚本仍然会降低速度,因为它在大约3000个数据点时从1.2秒/数据点下降到~3秒/点。闪电仍然不起作用,但我认为这个新版本暂时可以。谢谢你的帮助!