how to use FuncAnimation to update and animate multiple figures with matplotlib?

924
March 06, 2017, at 7:59 PM

Trying to create a program that reads serial data and updates multiple figures (1 line and 2 bar charts for now but could potentially be more).

Using 3 separate calls to FuncAnimation() right now, but proving to be really slow which is not good as I still need the option of adding more animated figures in the future.

So how can I make it a single FuncAnimation (or maybe something similar) that updates all three (potentially more) figures? Alternatively, what can I do to speed it up a bit?

#figure for current
amps = plt.figure(1)
ax1 = plt.subplot(xlim = (0,100), ylim = (0,500))
line, = ax1.plot([],[])
ax1.set_ylabel('Current (A)')
#figure for voltage
volts = plt.figure(2)
ax2 = plt.subplot()
rects1 = ax2.bar(ind1, voltV, width1)
ax2.grid(True)
ax2.set_ylim([0,6])
ax2.set_xlabel('Cell Number')
ax2.set_ylabel('Voltage (V)')
ax2.set_title('Real Time Voltage Data')
ax2.set_xticks(ind1)
#figure for temperature
temp = plt.figure(3)
ax3 = plt.subplot()
rects2 = ax3.bar(ind2, tempC, width2)
ax3.grid(True)
ax3.set_ylim([0,101])
ax3.set_xlabel('Sensor Number')
ax3.set_ylabel('temperature (C)')
ax3.set_title('Real Time Temperature Data')
ax3.set_xticks(ind2)
def updateAmps(frameNum):
    try:
    #error check for bad serial data
        serialString = serialData.readline()
        serialLine = [float(val) for val in serialString.split()]
        print (serialLine)
        if (len(serialLine) == 5):
            voltV[int(serialLine[1])] = serialLine[2]
            tempC[int(serialLine[3])] = serialLine[4]
            currentA.append(serialLine[0])
            if (len(currentA)>100):
                currentA.popleft()
        line.set_data(range(100), currentA)
    except ValueError as e:
    #graphs not updated for bad serial data
        print (e)
    return line,
#function to update real-time voltage data
def updateVolts(frameNum):
    for rects, h in zip(rects1,voltV):
        rects.set_height(h)
    return rects1
#function to update real-time temperature data
def updateTemp(frameNum):
    for rects, h in zip(rects2,tempC):
        rects.set_height(h)
    return rects2

Call to funcAnimation:

anim1 = animation.FuncAnimation(amps, updateAmps,
                                interval = 20, blit = True)
anim2 = animation.FuncAnimation(volts, updateVolts, interval = 25, blit = True)
anim3 = animation.FuncAnimation(temp, updateTemp, interval = 30, blit = True)
Answer 1

Echoing @ImportanceOfBeingErnest's comment, the obvious solution would be to use 3 subplots and only one FuncAnimation() call. You simply have to make sure your callback function returns a list of ALL artists to be updated at each iteration.

One drawback is that the update will happen a the same interval in all 3 subplots (contrary to the different timings you had in your example). You could potentially work around that by using global variables that count how many time the function has been called and only do some of the plots every so often for example.

#figure 
fig = plt.figure(1)
# subplot for current
ax1 = fig.add_subplot(131, xlim = (0,100), ylim = (0,500))
line, = ax1.plot([],[])
ax1.set_ylabel('Current (A)')
#subplot for voltage
ax2 = fig.add_subplot(132)
rects1 = ax2.bar(ind1, voltV, width1)
ax2.grid(True)
ax2.set_ylim([0,6])
ax2.set_xlabel('Cell Number')
ax2.set_ylabel('Voltage (V)')
ax2.set_title('Real Time Voltage Data')
ax2.set_xticks(ind1)
#subplot for temperature
ax3 = fig.add_subplot(133)
rects2 = ax3.bar(ind2, tempC, width2)
ax3.grid(True)
ax3.set_ylim([0,101])
ax3.set_xlabel('Sensor Number')
ax3.set_ylabel('temperature (C)')
ax3.set_title('Real Time Temperature Data')
ax3.set_xticks(ind2)
def updateAmps(frameNum):
    try:
    #error check for bad serial data
        serialString = serialData.readline()
        serialLine = [float(val) for val in serialString.split()]
        print (serialLine)
        if (len(serialLine) == 5):
            voltV[int(serialLine[1])] = serialLine[2]
            tempC[int(serialLine[3])] = serialLine[4]
            currentA.append(serialLine[0])
            if (len(currentA)>100):
                currentA.popleft()
        line.set_data(range(100), currentA)
    except ValueError as e:
    #graphs not updated for bad serial data
        print (e)
    return line,
#function to update real-time voltage data
def updateVolts(frameNum):
    for rects, h in zip(rects1,voltV):
        rects.set_height(h)
    return rects1
#function to update real-time temperature data
def updateTemp(frameNum):
    for rects, h in zip(rects2,tempC):
        rects.set_height(h)
    return rects2
def updateALL(frameNum):
    a = updateAmps(frameNum)
    b = updateVolts(frameNum)
    c = updateTemp(frameNum)
    return a+b+c
animALL = animation.FuncAnimation(fig, updateALL,
                                interval = 20, blit = True)
READ ALSO
in Login Activity if user doesn't exist in the 1st loginURL how to add another alternate URL to check in?

in Login Activity if user doesn't exist in the 1st loginURL how to add another alternate URL to check in?

I have 2 tables in my database i want to link it to my login activitybut after login success i want it to open different activity according to diffrent table user

358
“Google Play services are updating” in Google Maps API

“Google Play services are updating” in Google Maps API

I have an Android app which makes use of Google MapsAll of a sudden, it stopped working in release mode

376
Store several accounts in the AccountManager and include a SyncAdapter

Store several accounts in the AccountManager and include a SyncAdapter

I'm facing the following scenarioMy app uses the AccountManager to store the users credentials

238
How can I design an Android interface that support different screen size?

How can I design an Android interface that support different screen size?

I want that my android app support different screen size, so what is best practice to do these, how should I choose the sizes of textView and button and Should I design different layout for every screen size?

345