Python matplotlib blit и обновляет текст

Я знаю, что эта тема часто всплывает, но после многих попыток, поисков и отказов я возвращаю ее вам.

У меня есть класс, который содержит фигуру matplotlib. На этом рисунке мне нужен текст, и когда пользователь нажимает какую-либо клавишу, текст обновляется до чего-то, без рисования всего тяжелого на оси. Похоже, мне нужно кого-то здесь забить, но как? Вот рабочий пример, лучшее, что я мог получить до сих пор.

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()

Первый вопрос: при блитинге позади появляется предыдущий текст. Я хочу, чтобы это исчезло!

И второе: я бы вообще-то предпочел, чтобы это был фиговый текст, вне всяких осей. Звучит ли это осуществимо?

Вы лучшие, спасибо большое.


person Etienne    schedule 22.08.2016    source источник


Ответы (1)


Предыдущий текст все еще остается позади, потому что вы никогда не удаляли его — вы просто рисовали поверх него. Чтобы этого не произошло, следует сохранить кусок рисунка, где будет текст, затем показать текст, затем, когда текст изменился, восстановить сохраненный фон и заново показать текст.

matplotlib.ArtistAnimation уже делает все это за вас, поэтому вы можете просто использовать Это:

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()

Теперь, что касается вашего второго вопроса, Figure.text создаст текст, принадлежащий только фигуре, но ArtistAnimation не поддерживает художников, не принадлежащих ни к одной оси, поэтому в этом случае вы можете переопределить ArtistAnimation для поддержки этого.

person Tim Fuchs    schedule 22.08.2016
comment
Спасибо Тим! По какой-то причине я думал, что должен держаться подальше от mpl.animation, но да, это прекрасно. Я постараюсь адаптировать эту штуку для Figure.text . Спасибо еще раз. - person Etienne; 23.08.2016
comment
Я отправил отчет об ошибке в matplotlib о том, что не поддерживает такое поведение. Думаю, я скоро отправлю патч, но до тех пор вам просто нужно переопределить ArtistAnimation._init_draw для использования artist.get_figure() вместо artist.axes.figure. - person Tim Fuchs; 23.08.2016
comment
@TimFuchs Привет! В более сложной ситуации вы просто помещаете всех художников, которых хотите анимировать, в конструктор ArtistAnimation, а затем обновляете их, как обычно, не заботясь о ax.draw_artist(...) или figure.canvas.blit(ax.bbox)? Я следую этому решению, оно работает, но много накладных расходов сохранять ссылки на исполнителей и восстанавливать фон при необходимости. Он также работает только для данных, но не для заголовков и меток... - person Guimoute; 26.02.2020
comment
@Guimoute Да, я так думаю. ArtistAnimation должен позаботиться обо всей перерисовке и блитинге за вас. - person Tim Fuchs; 02.03.2020