3

I know that this topic is often popping out, but after many tries, searches and give-ups, I am bringing it back to you.

I have a class, which contains a matplotlib figure. In this figure, I want a text, and when the user hits some key, the text is updated to something, without drawing all the heavy stuff in the axis. It looks like I need to blit someone here, but how? Here is a working example, the best I could get to until now.

import matplotlib as mpl
mpl.use('TkAgg')
import matplotlib.pyplot as plt
import numpy as np

class textUpdater:
    def __init__(self):
        self.fig, self.ax = plt.subplots()
        # self.text = plt.figtext(.02, .14, 'Blibli')
        self.text = self.ax.text(0, .5, 'Blabla')#, transform = self.ax.transAxes)#, animated=True)

        self.fig.canvas.mpl_connect('key_press_event', self.action)
        self.fig.canvas.draw()

        plt.show()

    def action(self, event):
        if event.key == 'z':
            self.text.set_text('Blooooo')
            self.ax.draw_artist(self.text)
            self.fig.canvas.blit(self.text.get_window_extent())

textUpdater()

First question: when bliting the thing, the previous text appears behind. I want it gone!

And second: I would in fact prefer to have it as a fig text, out of any axes. Does it sound feasible?

Your are the bests, thanks a lot.

0

1 Answer 1

4

The previous text still stays behind, because you never removed it - you just drew on top of it. To prevent this, you should save the piece of figure where the text will be, then show the text, then, when the text has changed, restore the saved background and reshow the text.

matplotlib.ArtistAnimation already does all this for you, so you can just use it:

import matplotlib as mpl
mpl.use('TkAgg')
import matplotlib.pyplot as plt
from matplotlib.animation import ArtistAnimation
import numpy as np

class textUpdater:
    def __init__(self):
        self.fig, self.ax = plt.subplots()
        self.text = self.ax.text(.5, .5, 'Blabla')

        self.fig.canvas.mpl_connect('key_press_event', self.action)
        self.fig.canvas.draw()

        self.animation = ArtistAnimation(self.fig, [(self.text,)])

        plt.show()

    def action(self, event):
        if event.key == 'z':
            self.text.set_text('Blooooo')

textUpdater()

Now, to your second question, Figure.text will create a text that belongs just to the figure, but ArtistAnimation does not support artists that do not belong to any axes, so in this case you might want to redefine ArtistAnimation to support this.

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

4 Comments

Thanks Tim! For some reason I thought I had to stay away from mpl.animation, but yeah that's perfect. I will try to adapt the thing for a Figure.text . Thanks again.
I have filed a bug report with matplotlib about not supporting this behaviour. I guess I'll submit a patch soon, but till then, you just need to redefine ArtistAnimation._init_draw method to use artist.get_figure() instead of artist.axes.figure.
@TimFuchs Hi! In a more complex situation, do you just shove all the artists you want to animate in a ArtistAnimation constructor then update them as usual without caring about ax.draw_artist(...) or figure.canvas.blit(ax.bbox)? I'm following this solution, it works but there is a lot of overhead to save references to artists and restore background when needed. It also only works for data but not for titles and labels...
@Guimoute Yes, I believe so. The ArtistAnimation should take care of all the redrawing and blitting for you.

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.