Skip to content
Advertisement

Having problems labelling axes in a barplot animation shown on a FigureCanvas

I’ve been using the animation method outlined at Managing dynamic plotting in matplotlib Animation module to create animations and place them on a tkinter FigureCanvas.

I’m having difficulties animating a sequence of barplots in such a way that the y-axis tickmark labels appear as I want them to. My animation will have 100 frames, each consisting of a barplot using four x-values. The data is stored in a 100-by-4 matrix, whose entries are random and fall between zero and one. Each frame of the animation appears on a FigureCanvas. Below is a summary of what I’ve done so far, where Player is the class defined at the link above.

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from tkinter import Tk, TOP, BOTH

root=Tk()
root.geometry('1000x1000')

fig=Figure() 

# Place canvas on figure. Each frame of the animation will place a barplot on the 
#canvas.

canvas = FigureCanvasTkAgg(fig, master=root)
canvas.draw()
canvas.get_tk_widget().pack(side=TOP,fill=BOTH,expand=1)

# Random 100-by-4 matrix, each row of which corresponds to an instant in time. 
M=np.random.rand(100,4)
labels=['a','b','c','d']
num_times=M.shape[0]

def update_bar(i):
    ax=fig.add_subplot(111)
    ax.bar(labels,list(M[i,:]))
    # Want y ticks to be labelled 0, .2, .4, .6, .8 for each frame.
    ax.set_yticks(np.arange(0, 1, step=0.2))


ani = Player(fig, update_bar, maxi=num_times)
root.mainloop()

When I play the animation, the y-axis labels turn out to be a mess, because the tick marks are recreated in each frame. (See image, which shows the eighth frame.)

Is there a way for me to set the y-tickmark label at all at once before the animation starts? enter image description here

Advertisement

Answer

You should move the line:

ax = fig.add_subplot(111)

out of update_bar function: you don’t need a new ax in each iteration.
Then, within update_bar function you should add this line:

ax.cla()

in order to erase the previous plot.
Finally, I suggest to add this line:

ax.set_ylim(0, 1)

in the update_bar function, in order to keep fixed y axis limits.

Complete Code

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from tkinter import Tk, TOP, BOTH
import matplotlib
from matplotlib.animation import FuncAnimation
import mpl_toolkits.axes_grid1

root=Tk()
root.geometry('1000x1000')

fig=Figure()

# Place canvas on figure. Each frame of the animation will place a barplot on the
#canvas.

canvas = FigureCanvasTkAgg(fig, master=root)
canvas.draw()
canvas.get_tk_widget().pack(side=TOP,fill=BOTH,expand=1)

# Random 100-by-4 matrix, each row of which corresponds to an instant in time.
M=np.random.rand(100,4)
labels=['a','b','c','d']
num_times=M.shape[0]

class Player(FuncAnimation):
    def __init__(self, fig, func, frames=None, init_func=None, fargs=None,
                 save_count=None, mini=0, maxi=100, pos=(0.125, 0.92), **kwargs):
        self.i = 0
        self.min=mini
        self.max=maxi
        self.runs = True
        self.forwards = True
        self.fig = fig
        self.func = func
        self.setup(pos)
        FuncAnimation.__init__(self,self.fig, self.func, frames=self.play(),
                                           init_func=init_func, fargs=fargs,
                                           save_count=save_count, **kwargs )

    def play(self):
        while self.runs:
            self.i = self.i+self.forwards-(not self.forwards)
            if self.i > self.min and self.i < self.max:
                yield self.i
            else:
                self.stop()
                yield self.i

    def start(self):
        self.runs=True
        self.event_source.start()

    def stop(self, event=None):
        self.runs = False
        self.event_source.stop()

    def forward(self, event=None):
        self.forwards = True
        self.start()
    def backward(self, event=None):
        self.forwards = False
        self.start()
    def oneforward(self, event=None):
        self.forwards = True
        self.onestep()
    def onebackward(self, event=None):
        self.forwards = False
        self.onestep()

    def onestep(self):
        if self.i > self.min and self.i < self.max:
            self.i = self.i+self.forwards-(not self.forwards)
        elif self.i == self.min and self.forwards:
            self.i+=1
        elif self.i == self.max and not self.forwards:
            self.i-=1
        self.func(self.i)
        self.fig.canvas.draw_idle()

    def setup(self, pos):
        playerax = self.fig.add_axes([pos[0],pos[1], 0.22, 0.04])
        divider = mpl_toolkits.axes_grid1.make_axes_locatable(playerax)
        bax = divider.append_axes("right", size="80%", pad=0.05)
        sax = divider.append_axes("right", size="80%", pad=0.05)
        fax = divider.append_axes("right", size="80%", pad=0.05)
        ofax = divider.append_axes("right", size="100%", pad=0.05)
        self.button_oneback = matplotlib.widgets.Button(playerax, label=u'$u29CF$')
        self.button_back = matplotlib.widgets.Button(bax, label=u'$u25C0$')
        self.button_stop = matplotlib.widgets.Button(sax, label=u'$u25A0$')
        self.button_forward = matplotlib.widgets.Button(fax, label=u'$u25B6$')
        self.button_oneforward = matplotlib.widgets.Button(ofax, label=u'$u29D0$')
        self.button_oneback.on_clicked(self.onebackward)
        self.button_back.on_clicked(self.backward)
        self.button_stop.on_clicked(self.stop)
        self.button_forward.on_clicked(self.forward)
        self.button_oneforward.on_clicked(self.oneforward)

def update_bar(i):
    ax.cla()
    ax.bar(labels,list(M[i,:]))
    # Want y ticks to be labelled 0, .2, .4, .6, .8 for each frame.
    ax.set_yticks(np.arange(0, 1, step=0.2))
    ax.set_ylim(0, 1)


ax = fig.add_subplot(111)
ani = Player(fig, update_bar, maxi=num_times)
root.mainloop()

enter image description here

User contributions licensed under: CC BY-SA
9 People found this is helpful
Advertisement