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.

Etienne
  • 67
  • 2
  • 6

1 Answers1

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.

Tim Fuchs
  • 1,061
  • 5
  • 14
  • 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. – Etienne Aug 23 '16 at 06:50
  • I have filed a [bug report](https://github.com/matplotlib/matplotlib/issues/6965) 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](https://github.com/matplotlib/matplotlib/blob/master/lib/matplotlib/animation.py#L1200) method to use `artist.get_figure()` instead of `artist.axes.figure`. – Tim Fuchs Aug 23 '16 at 08:45
  • @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](https://stackoverflow.com/questions/40126176/fast-live-plotting-in-matplotlib-pyplot), 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 Feb 26 '20 at 17:13
  • @Guimoute Yes, I believe so. The ``ArtistAnimation`` should take care of all the redrawing and blitting for you. – Tim Fuchs Mar 02 '20 at 06:35