pythonmatplotlibanimationpython-3.9matplotlib-animation

how to show progression of changing subplot labels (plt.text) along with color changing in an ArtistAnimation


running python 3.9:

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

fig, plt = plot.subplots()

myArray = np.array([[0,  1,  2,   3,  4,  5,  6],
                    [7,  8,  9,  10, 11, 12, 13],
                    [14, 15, 16, 17, 18, 19, 20],
                    [21, 22, 23, 24, 25, 26, 27],
                    [28, 29, 30, 31, 32, 33, 34],
                    [35, 36, 37, 38, 39, 40, 41],
                    [42, 43, 44, 45, 46, 47, 48]])
def randint():
    return np.random.randint(0, 7)

def randNewValue():
    return np.random.randint(0, 48) #/10

def labelSquares():
    for i in range(7):
        for j in range(7):
            plt.text(j, i, myArray[i, j], ha="center", va="center", color="white")

def modifyArrayElement(r, c, x):
    row = r
    col = c
    new_value = x
    myArray[row, col] = new_value
    # labelSquares()
    return myArray

ims = []

for i in range(100):
    row = randint()
    col = randint()
    newValue = randNewValue()
    # plt.text(col, row, myArray[row, col], ha="center", va="center", color="white")
    # labelSquares()
    im = plt.imshow(modifyArrayElement(row, col, newValue), animated = True)
    if i == 0:
        plt.imshow(modifyArrayElement(row, col, newValue))
    ims.append([im])

ani = animation.ArtistAnimation(fig, ims, interval = 400, blit = True, repeat_delay = 5000)


labelSquares() # how to make it run "during" the animation, i.e: show progression of changing labels (plt.text) along with color changing, matching myArray values at each iteration?
f = r"c://Temp/animation.gif"
writergif = animation.PillowWriter(fps=2)
# ani.save(f, writer=writergif)
plot.show()

The result shows an animation of changing square colors as the contents of myArray changes, however, I would like the numbers inside them (text labels) to also change along, matching myArray values at each iteration of the for loop instead of staying static at final values all the time. Placing labelSquares() - or any similar labeling attempt inside the for loop - does not work. Is there a way to do it? (Sorry if this is a trivial problem showing my inexperience with pyplot, and I hope all the experts forgive me for asking such a stupid question)

animation result


Solution

  • From the ArtistAnimation documentation on the artistlist parameter:

    Each list entry is a collection of Artist objects that are made visible on the corresponding frame.

    Note that every object you add to a Matplotlib figure is an artist, including the Text objects that are returned by plt.text. So, for each frame, we can get hold of these and put them in a list together with the AxesImage artist from imshow.

    Since your ModifyArrayElement function changes the global array in-place, the labelSquares function has those updates automatically, and so does imshow, so I simplified the imshow calls. Also added animated=True to the plt.text to prevent unnecessary drawing.

    import matplotlib.pyplot as plot
    import numpy as np
    import matplotlib.animation as animation
    
    fig, plt = plot.subplots()
    
    myArray = np.array([[0,  1,  2,   3,  4,  5,  6],
                        [7,  8,  9,  10, 11, 12, 13],
                        [14, 15, 16, 17, 18, 19, 20],
                        [21, 22, 23, 24, 25, 26, 27],
                        [28, 29, 30, 31, 32, 33, 34],
                        [35, 36, 37, 38, 39, 40, 41],
                        [42, 43, 44, 45, 46, 47, 48]])
    def randint():
        return np.random.randint(0, 7)
    
    def randNewValue():
        return np.random.randint(0, 48) #/10
    
    def labelSquares():
        all_texts = []
        for i in range(7):
            for j in range(7):
                all_texts.append(plt.text(j, i, myArray[i, j], ha="center",
                                          va="center", color="white",
                                          animated=True))
        return all_texts
                
    
    def modifyArrayElement(r, c, x):
        row = r
        col = c
        new_value = x
        myArray[row, col] = new_value
        return myArray
    
    artists = []
    
    for i in range(100):
        row = randint()
        col = randint()
        newValue = randNewValue()
        modifyArrayElement(row, col, newValue)
        im = plt.imshow(myArray, animated=True)
        frame_artists = labelSquares()
        frame_artists.append(im)
        artists.append(frame_artists)
    
    ani = animation.ArtistAnimation(fig, artists, interval = 400, blit = True, repeat_delay = 5000)
    
    
    writergif = animation.PillowWriter(fps=2)
    ani.save("myanim.gif", writer=writergif)
    

    enter image description here