I use python 3.6 (the newest one) with the spider interpreter on a windows 10 PC with more-than-average hardware and wrote a script that enables me to continuously measure and plot the frequencies of two channels on an Agilent frequency counter and saves the data to a txt file. I furthermore have to convert the script to a .exe file (using pyinstaller) in order to distribute it to several measuring PCs.
Everything works fine, even from the .exe file, until the measuring time reaches about 2000 seconds. The software then starts to become very slow until it even show the "window not answering"
thing that windows does while plotting.
I tried to activate the bliting-function of FuncAnimate
, but when I do so, it only shows a white window.
Therefore I am now looking for options to enhance the speed of my software, especially at high data amounts, without cutting of the lots and lots of data (they are needed to be seen from t=0
to t=whatever
).
Why does the blit=True
kill my animation? Or is there a better way to plot these amounts of data fast and over and over again?
My code:
#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()
Okay, as requested, a simpler script showing my problem:
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()
I left the "beautyful-makings" like titles in the code. To actually see the problem I am having you would have to run that script for at least 600 seconds, the problem becomes "stronger" after 2000 seconds.