Есть 3 вещи, которые мешают вашему коду работать:
При использовании 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)
Изменить bfig1 = Figure(container, controller=self)
на bfig1 = Figure1(container, controller=self)
.В противном случае холст никогда не прикрепляется к фигуре f1
.
*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()