20

Below my code. Why doesn't title updates every tick? I read this: Matplotlib animation with blit -- how to update plot title? but it wasn't useful.

#! /usr/bin/python3
import matplotlib
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time
import random as rr

plt.rc('grid', color='#397939', linewidth=1, linestyle='-')
plt.rc('xtick', labelsize=10)
plt.rc('ytick', labelsize=5)

width, height = matplotlib.rcParams['figure.figsize']
size = min(width, height)
fig = plt.figure(figsize=(size, size))
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True, facecolor='#cfd98c')

ax.set_rmax(20.0)
plt.grid(True)
plt.title("")
def data_gen(t=0):
    tw = 0
    phase = 0
    ctn = 0
    while True:
        ctn += 1
        if ctn == 1000:
            phase=round(rr.uniform(0,180),0)
            tw = round(rr.uniform(0,20),0)
            ctn = 0
            yield tw, phase
def update(data):
    tw, phase = data
    print(data)
    ax.set_title("|TW| = {}, Angle: {}°".format(tw, phase)) 
    arr1 = plt.arrow(phase, 0, 0, tw, alpha = 0.5, width = 0.080,
             edgecolor = 'red', facecolor = 'red', lw = 2, zorder = 5)
    return arr1,

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

EDIT n. 1 After @eyllanesc answer, I edit this piece of code:

def data_gen(t=0):
    tw = 10
    phase = 0
    ctn = 0
    while True:
        if phase < 2*180:
            phase += 1
        else:
            phase=0
        yield tw, phase

def update(data):
    tw, phase = data
    angolo = phase /180 * np.pi
    print("|TW| = {}, Angle = {}°".format(tw,phase))
    ax.set_title("|TW| = {}, Angle: {}°".format(tw, phase)) 
    arr1 = plt.arrow(angolo, 0, 0, tw, alpha = 0.5, width = 0.080, edgecolor = 'red', facecolor = 'red', lw = 2, zorder = 5)
    plt.draw()
    return arr1,

Now text works correctly, but the arrow updating is not "fluid" (it appeares and disappeares every update).

4
  • Explain yourself better, what is your question? Commented Jun 16, 2017 at 21:03
  • I need that in every frame updates, the plot's title will be updated too. Commented Jun 16, 2017 at 21:09
  • The arrow if it is updated but the title is not updated, am I correct? Commented Jun 16, 2017 at 21:11
  • see my answer please Commented Jun 16, 2017 at 21:19

1 Answer 1

41

The problem arises when using blit=True in the FuncAnimation. This will store the background and only update the artists returned by the update function. However, the restored background will overwrite the title, since it is outside the axes.

Possible solutions are:

  1. Put the title inside the axes.
  2. Don't use blitting
  3. Possibly using an ArtistAnimation instead of a FuncAnimation would work as well. But I haven't tested it.

Note that using plt.draw or similar inside the updating function (as proposed in other answers) does not make sense, since it destroys all the advatages of using blitting and makes the animation even slower than in the case of not using blitting.

1. Put the title inside the axes (blit=True)

Instead of a title outside the axes, you may use a title = ax.text(..) positionned inside the axes. This text can be updated for each iteration title.set_text("..") and must then be returned by the updating function (return arr1, title,).

import matplotlib
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

plt.rc('grid', color='#397939', linewidth=1, linestyle='-')
plt.rc('xtick', labelsize=10)
plt.rc('ytick', labelsize=5)

width, height = matplotlib.rcParams['figure.figsize']
size = min(width, height)
fig = plt.figure(figsize=(size, size))
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True, facecolor='#cfd98c')

ax.set_rmax(20.0)
plt.grid(True)

title = ax.text(0.5,0.85, "", bbox={'facecolor':'w', 'alpha':0.5, 'pad':5},
                transform=ax.transAxes, ha="center")

def data_gen(t=0):
    tw = 10
    phase = 0
    while True:
        if phase < 2*180:
            phase += 2
        else:
            phase=0
        yield tw, phase

def update(data):
    tw, phase = data
    title.set_text(u"|TW| = {}, Angle: {}°".format(tw, phase))
    arr1 = ax.arrow(np.deg2rad(phase), 0, 0, tw, alpha = 0.5, width = 0.080,
             edgecolor = 'red', facecolor = 'red', lw = 2, zorder = 5)
    return arr1,title,

ani = animation.FuncAnimation(fig, update, data_gen, interval=100, blit=True, repeat=False)

plt.show()

enter image description here

Note that I slightly changed the angle setting, since it the angle for arrow must be in radiants instead of degrees.

2. Not use blitting (blit=False)

You may decide not to use blitting, which makes sense when the requirements on the speed of the animation are not so high. Not using blitting allows to use a normal title outside the axes. However, in this case you would need to remove the artist for each iteration (otherwise one would end up with a lot of arrows in the plot).

import matplotlib
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation

plt.rc('grid', color='#397939', linewidth=1, linestyle='-')
plt.rc('xtick', labelsize=10)
plt.rc('ytick', labelsize=5)

width, height = matplotlib.rcParams['figure.figsize']
size = min(width, height)
fig = plt.figure(figsize=(size, size))
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8], polar=True, facecolor='#cfd98c')

ax.set_rmax(20.0)
plt.grid(True)

def data_gen(t=0):
    tw = 10
    phase = 0
    while True:
        if phase < 2*180:
            phase += 2
        else:
            phase=0
        yield tw, phase

arr1 = [None]
def update(data):
    tw, phase = data
    ax.set_title(u"|TW| = {}, Angle: {}°".format(tw, phase))
    if arr1[0]: arr1[0].remove()
    arr1[0] = ax.arrow(np.deg2rad(phase), 0, 0, tw, alpha = 0.5, width = 0.080,
             edgecolor = 'red', facecolor = 'red', lw = 2, zorder = 5)

ani = animation.FuncAnimation(fig, update, data_gen, interval=100, blit=False, repeat=False)

plt.show()

enter image description here

Sign up to request clarification or add additional context in comments.

2 Comments

Why not position it same as title, so it's effectively the same? Seems to work for ax.plot
ArtistAnimation also works with the separate title=ax.text(...) object, you just have to return the title as an artist as well for each frame in the list of artists.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.