Две анимации в ткинтере за один раз - PullRequest
0 голосов
/ 13 мая 2018

Я хочу, чтобы две анимации работали одновременно.

Вот мой код:

import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.animation as animation
import tkinter as tk

f = plt.figure(figsize=(6, 2), dpi=100)
f1 = plt.figure(figsize=(4, 2), dpi=100)

def animate(i):
    a = f.add_subplot(111)
    x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    y = [3, 5, 2, 7, 3, 5, 4, 6, 2, 2]
    a.plot(x, y)

def animate1(j):
    a1 = f1.add_subplot(111)
    x1 = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    y1 = [6, 1, 4, 9, 4, 2, 5, 1, 2, 4]
    a1.plot(x1, y1)

class Figure(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.config(background='blue')

        canvas = FigureCanvasTkAgg(f, self)
        canvas.draw()
        canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)

class Figure1(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.config(background='blue')

        canvas = FigureCanvasTkAgg(f1, self)
        canvas.draw()
        canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)


class Calculator(tk.Tk):
        def __init__(self, *args, **kwargs):
            tk.Tk.__init__(self, *args, *kwargs)
            tk.Tk.wm_title(self, 'Beam calculator')
            container = tk.Frame(self)
            container.pack(side='top', fill='both', expand=True)
            bfig = Figure(container, controller=self)
            bfig.grid(row=0, column=0, sticky="nsew")
            bfig1 = Figure(container, controller=self)
            bfig1.grid(row=1, column=0, sticky="nsew")


if __name__ == '__main__':

    app = Calculator()
    app.geometry('1280x720')
    ani = animation.FuncAnimation(f, animate, interval=1000)
    ani1 = animation.FuncAnimation(f1, animate1, interval=1000)
    app.mainloop()

Но появляется только вторая цифра.

Я нашел следующую тему:

Одновременная анимация двух и более фигур с помощью matplotlib

но я не знаю, как это сделать правильно.

1 Ответ

0 голосов
/ 13 мая 2018

Есть 3 вещи, которые мешают вашему коду работать:

  1. При использовании grid вам необходимо присвоить положительный вес хотя бы одной строке иодин столбец :

    class Calculator(tk.Tk):
        def __init__(self, *args, **kwargs):
            ...
            container.grid_columnconfigure(0, weight=1)
            # give the rows equal weight so they are allotted equal size
            container.grid_rowconfigure(0, weight=1)
            container.grid_rowconfigure(1, weight=1)
    
  2. Изменить bfig1 = Figure(container, controller=self) на bfig1 = Figure1(container, controller=self).В противном случае холст никогда не прикрепляется к фигуре f1.

  3. *kwargs должен быть **kwargs в строке tk.Tk.__init__(self, *args, *kwargs).См. этот пост, чтобы узнать больше о том, что означают *args и **kwargs.

Это минимальные изменения, необходимые для запуска кода.


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

Одно большое из них заключается в том, что a = f.add_subplot(111) не следует размещать внутри функции animate.Это создает объект Axes, a, внутри фигуры, f.Обычно есть один Оси и одна фигура на участок.(См. иерархия объектов matplotlib .) Поскольку animate вызывается один раз для каждого кадра анимации, вы не хотите создавать новые Оси для каждого кадра.Код ниже предлагает, как определить a один раз, глобально.


Еще один важный принцип программирования - аббревиатура DRY: «Не повторяйся» .

Хотя сначала копирование кода может быть простым, вВ долгосрочной перспективе это усложняет поддержку и изменение кода.Предположим, например, что вы решили добавить код в класс Figure.Поскольку у вас есть два почти идентичных класса Figure, вам, возможно, придется добавить один и тот же код к Figure и Figure1.И если позже вы обнаружите, что в этом коде есть ошибка, вам придется исправить ошибку в обоих местах.Ваша производительность уменьшается вдвое, потому что каждое редактирование должно быть сделано дважды.И если вы забудете сделать это в одном месте, то появится еще одна ошибка.

Вы можете сделать свой код СУШИЛЬНЫМ, обобщив animate и Figure, чтобы вы могли удалить animate1 и Figure1.Все, что вам нужно сделать, это добавить дополнительный аргумент к функциям (например, line и f), чтобы обобщить код и разрешить его повторное использование.(Смотрите код ниже, чтобы понять, что я имею в виду.)


Почти всегда плохая идея использовать нумерованные имена переменных.A list или dict обычно должны использоваться вместо пронумерованных переменных.Поскольку нумерованные имена переменных часто используются одинаково (то есть, сделать это с x1, сделать то же самое с x2, сделать то же самое с x3 и т. Д.), Используя пронумерованные переменные, приводит к кодуповторение, которое идет вразрез с принципом СУХОЙ.Если вместо этого вы используете список для сбора всех числовых переменных в одном объекте, то вы можете обрабатывать их все одновременно с помощью цикла:

for x in xs:
    do_something(x)

Совет по производительности: вызов plt.plot тысячвремя, которое может возникнуть в анимации, может заметно замедлить работу вашей программы.Каждый вызов генерирует новые Line2D объекты, которые необходимо перерисовывать с каждым кадром анимации.Для повышения производительности повторное использование одного и того же объекта Line2D снова и снова и просто измените ydata внутри объекта Line2D, чтобы matplotlib отображал другую строку:

line.set_ydata(y)

import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.animation as animation
import matplotlib.figure as mplfig
import tkinter as tk

def animate(i, line):
    y = np.random.randint(10, size=len(x))
    line.set_ydata(y)
    return [line]

class Figure(tk.Frame):

    def __init__(self, parent, controller, f):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        canvas = FigureCanvasTkAgg(f, self)
        canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True)
        canvas.draw()

class Calculator(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        self.wm_title('Beam calculator')
        container = tk.Frame(self)
        container.pack(side='top', fill='both', expand=True)
        for i, f in enumerate(figs):
            bfig = Figure(container, controller=self, f=f)
            bfig.grid(row=i, column=0, sticky="nsew")
            # give the rows equal weight so they are allotted equal size
            container.grid_rowconfigure(i, weight=1)
        # you need to give at least one row and one column a positive weight 
        # https://stackoverflow.com/a/36507919/190597 (Bryan Oakley)
        container.grid_columnconfigure(0, weight=1)

if __name__ == '__main__':
    figs = [mplfig.Figure(figsize=(6, 2), dpi=100),
          mplfig.Figure(figsize=(4, 2), dpi=100)]
    axs = [f.add_subplot(111) for f in figs]
    x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    ys = [[3, 5, 2, 7, 3, 5, 4, 6, 2, 2],
          [6, 1, 4, 9, 4, 2, 5, 1, 2, 4]]
    lines = [ax.plot(x, ys[i])[0] for i, ax in enumerate(axs)]
    for ax in axs:
        ax.set_ylim([0, 10])

    app = Calculator()
    app.geometry('1280x720')
    anis = [animation.FuncAnimation(f, animate, interval=1000, fargs=[line])
            for f, line in zip(figs,lines)]
    app.mainloop()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...