Matplotlib live сюжет relim после использования панели навигации в графическом интерфейсе tkinter - PullRequest
0 голосов
/ 12 октября 2018

Я делаю графический интерфейс в tkinter с живыми, встроенными графиками matplotlib.Я использую FigureCanvasTkAgg для холста, NavigationToolbar2Tk для панели навигации и FuncAnimation для обработки периодических обновлений данного источника данных.

Обратный вызов, связанный с FuncAnimation, сбрасывает данные в данной строке (т. Е. Возвращаемое значение из Axes.plot(...)) при каждом вызове (т. Е. Line2D.set_data(...)).Обратный вызов также переопределяет и применяет соответствующие пределы по осям X и Y, чтобы соответствовать новым данным через

axis.relim()
axis.autoscale_view()

, где axis является экземпляром AxesSubplot.

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

Проблема, с которой я сталкиваюсь, заключается в том, что если используются какие-либо функции на панели навигации(панорамирование, масштабирование и т. д.) повторное масштабирование больше не работает, это означает, что график может вырваться из поля зрения, и единственный способ увидеть новые данные для пользователя - это выполнить панорамирование вручную или уменьшить масштаб вручную, что являетсянежелательно.

Реально, эта функциональность имеет смысл, так как это может раздражать, например, пытаться увеличивать часть графика только для того, чтобы сразу же уменьшить масштаб для повторной установки осей на новые данные, чтопочему я намеревался добавить tkinter.Checkbutton, чтобы временно отключить повторное масштабирование.

Я попытался найти источник для панели навигации, и он, кажется, меняет состояние на осях и холсте, чтоЯ могу только предположить, что это проблема, но мне пока не удалось найти способ «отменить» эти изменения.Если такой способ существует, я бы связал его с tkinter.Button или чем-то таким, чтобы можно было повторно включить автоматическое масштабирование.

Как я могу решить эту проблему?

Нижеминимальный пример, демонстрирующий эту проблему.

import math
import itertools
import tkinter as tk
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2Tk
from matplotlib.animation import FuncAnimation


def xydata_generator(func, div):
    for num in itertools.count():
        num = num / div
        yield num, func(num)


class Plot(tk.Frame):

    def __init__(self, master, data_source, interval=100, *args, **kwargs):
        super().__init__(master, *args, **kwargs)

        self.data_source = data_source
        self.figure = Figure((5, 5), 100)
        self.canvas = FigureCanvasTkAgg(self.figure, self)
        self.nav_bar = NavigationToolbar2Tk(self.canvas, self)
        self.canvas.get_tk_widget().pack(fill=tk.BOTH, expand=True)
        self.axis = self.figure.add_subplot(111)
        self.x_data = []
        self.y_data = []
        self.line = self.axis.plot([], [])[0]  # Axes.plot returns a list
        # Set the data to a mutable type so we only need to append to it then force the line to invalidate its cache
        self.line.set_data(self.x_data, self.y_data)
        self.ani = FuncAnimation(self.figure, self.update_plot, interval=interval)

    def update_plot(self, _):
        x, y = next(self.data_source)  # (realistically the data source wouldn't be restricted to be a generator)
        # Because the Line2D object stores a reference to the two lists, we need only update the lists and signal
        # that the line needs to be updated.
        self.x_data.append(x)
        self.y_data.append(y)
        self.line.recache_always()
        self._refit_artists()

    def _refit_artists(self):
        self.axis.relim()
        self.axis.autoscale_view()


root = tk.Tk()
data = xydata_generator(math.sin, 5)
plot = Plot(root, data)
plot.pack(fill=tk.BOTH, expand=True)
root.mainloop()

1 Ответ

0 голосов
/ 12 октября 2018

Оказывается довольно просто.Чтобы сбросить оси так, чтобы вызовы Axes.relim() и Axes.autoscale_view() вступили в силу, нужно просто позвонить Axes.set_autoscale_on(True).Это необходимо повторять каждый раз, когда используются функции панели навигации (панорамирование, масштабирование и т. Д.).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...