0

I am imitating getting stream of data where not each "message" line has the information I want to plot in live mode. Since each line will still be read, how can I skip one iteration (frame) and therefore skip plot update for 1 frame until next iteration that returns data comes along. My code has the following structure:

fig, ax = plt.subplots()
line, = ax.plot([], [])

lap_stat = []
file = open(path + 'file.log', 'r')

def update(lap_stat):

    #Plot lap number on x-axis, lap time on y-axis from lap_stat list that 
    #contains a tuple

    line.set_data(lap_stat[0], lap_stat[1])
    return line,
    
def data_gen():

    while True:
        line = file.readline()

        #Get data from line
        #Apply conditions, if line doesn't contain relevant information nothing happens
        #If data is present in line, save data as tuple [lap, lap_time] to lap_stat

        yield lap_stat

ani = FuncAnimation(fig, update, data_gen, interval=100, blit=True)
plt.show() 

Not each line has the relevant information, so if at current frame no information was appended the input list at update(lap_stat) function is empty, nothing gets plotted and the process dies. How can I modify my code to only plot information when it is returned? Plotting 0's also not an option, if I plot lap times from lap 0-10 and the then next data point is lap 15, I want the point from lap 10 to be connected to point 15. Hope it makes, thank you!

tishafok
  • 13
  • 4

1 Answers1

0

you made me sweat, I dont think this is the right way to approach the problem, but given your layout, here what I got working:

input file file.log :

1 10
2 20
3 10
4 50
5 80
6 10
7 70
8
9
10 55
11 40
12 66

my code:

import matplotlib.animation as animation #to save animation

from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt

fig, ax = plt.subplots()

ax.set_xlim([0,12])
ax.set_ylim([0,100])

line, = ax.plot([],[], marker="o", markersize=10,markeredgecolor="red", markerfacecolor="green")

path = ''

file = open(path + 'file.log', 'r')


def zero():
    line = ax.plot(0,0)
    return line 


def update(lap_stat):
    #Plot lap number on x-axis, lap time on y-axis from lap_stat list that 
    #contains a tuple
    line.set_data(lap_stat[0], lap_stat[1])
    return line,

    
def data_gen():
    line = 'pippo'
    
    while line:
        line = file.readline()
        print('line : ', line)
        
        try:
                lap_stat = (int(line.split()[0]),int( line.split()[1]))
                print('lap_stat : ', lap_stat)    
            #Get data from line
            #Apply conditions, if line doesn't contain relevant information nothing happens
            #If data is present in line, save data as tuple [lap, lap_time] to lap_stat
                yield lap_stat
                
        except:
                continue
        
ani = FuncAnimation(fig, update, data_gen(), init_func=zero ,  interval=2000, blit=True)
# ani = FuncAnimation(fig, update, data_gen(), interval=1000) # need first frame, doesnt clear previous points
# ani.save('animation.gif' , writer=animation.PillowWriter(fps=1)) #save animation

plt.show() 


output :

enter image description here

as per OP, modified code on same input to get animation below:

import matplotlib.animation as animation #to save animation

from matplotlib.animation import FuncAnimation
import matplotlib.pyplot as plt



fig, ax = plt.subplots()

ax.set_xlim([0,1])
ax.set_ylim([0,1])

line, = ax.plot([],[], marker="o", markersize=10,markeredgecolor="red", markerfacecolor="green")

path = ''

file = open(path + 'file.log', 'r')


def zero():
    line = ax.plot(0,0)
    return line 

x_points = [0]
y_points = [0]
def update(lap_stat):
    #Plot lap number on x-axis, lap time on y-axis from lap_stat list that 
    #contains a tuple
    
    x_points.append(lap_stat[0])
    
    y_points.append(lap_stat[1])
    
    ax.set_xlim([0,max(x_points)])
    ax.set_ylim([0,max(y_points)])
    
    # line.set_data(lap_stat[0], lap_stat[1])
    
    line.set_data(x_points, y_points)
    
    print(x_points, y_points)
    
    return line,

    
def data_gen():
    line = 'pippo'
    
    while line:
        line = file.readline()
        print('line : ', line)
        
        try:
                lap_stat = (int(line.split()[0]),int( line.split()[1]))
                print('lap_stat : ', lap_stat)    
            #Get data from line
            #Apply conditions, if line doesn't contain relevant information nothing happens
            #If data is present in line, save data as tuple [lap, lap_time] to lap_stat
                yield lap_stat
                
        except:
                continue
        
# ani = FuncAnimation(fig, update, data_gen(), init_func=zero ,  interval=2000, blit=True)

ani = FuncAnimation(fig, update, data_gen(), init_func=zero ,  interval=2000, blit=False, repeat = False)

# ani = FuncAnimation(fig, update, data_gen(), interval=1000) # need first frame, doesnt clear previous points
# ani.save('animation_line.gif' , writer=animation.PillowWriter(fps=1)) #save animation

plt.show() 

print(x_points, y_points)

output:

enter image description here

pippo1980
  • 2,181
  • 3
  • 14
  • 30
  • That makes sense, thank you so much for addressing it. Just need to connect the markers with line and figure out how to plot the live data properly. The issue is that length of X-axis can be unknown before init and needs to be adjusted with each iteration. Same for Y-axis, it is preferred to have the Y-region auto-scaled between min and max values at all times. – tishafok Mar 01 '22 at 14:29
  • Think you can set_xlim and set_ylim in the update() function too – pippo1980 Mar 01 '22 at 14:32
  • In any case read somewhere that file.readlines() is IO buffered too, so it would be easier to just read the entire file and loop over its contents – pippo1980 Mar 01 '22 at 14:36
  • @tishafok see updates, let me know – pippo1980 Mar 01 '22 at 20:09