Скрипт медленной FuncAnimation - PullRequest
0 голосов
/ 02 июля 2018

Я использую Python 3.6 (самый новый) с интерпретатором паука на ПК с Windows 10 с аппаратным обеспечением выше среднего и написал скрипт, который позволяет мне непрерывно измерять и отображать частоты двух каналов на частотомере Agilent и сохраняет данные в текстовый файл. Кроме того, мне нужно преобразовать скрипт в файл .exe (используя pyinstaller), чтобы распространить его на несколько измерительных ПК.

Все отлично работает, даже из файла .exe, пока время измерения не достигнет 2000 секунд. Затем программное обеспечение начинает работать очень медленно, пока не отобразится даже то, что "window not answering" делает Windows во время печати.

Я пытался активировать функцию бликования 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 секунд.

1 Ответ

0 голосов
/ 02 июля 2018

Кажется, что большая часть ваших проблем приходится каждый раз пересчитывать полные списки мин и макс,

например. значения x и y вашего подплота x / y, а не обработку всего добавленного списка, вы должны использовать переменную для хранения самых низких и самых высоких значений, пока что сравниваете текущий фрагмент входных данных с ними, и если он удовлетворяет условию, обновите их,

   subplot1.set_ylim(min(y_data1)-1,max(y_data1)+1)         
   subplot1.set_xlim(min(x_data)-1,max(x_data)+1)

Вы также, кажется, сбрасываете ярлыки подпрограмм при каждом обновлении, теперь я не так знаком с GUI в TKinter, но я чувствую, что это может быть включено в определение инициализации,

   subplot1.set_title('Channel 1')
   subplot1.set_xlabel('Time')
   subplot1.set_ylabel('Frequency')

Первое предложение должно поддерживать выполнение вашей программы в более согласованное время, а не постепенное замедление, а другое должно быть в состоянии уменьшить объем вашей работы за обновление.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...