0

我对使用 tkinter 还很陌生,我想制作一个小 ui 来练习。我选择绘制洛伦兹系统,同时允许用户配置输入参数。

到目前为止,我已经连接了一个图表和输入字段,我可以显示静态图。我的下一步是尝试展示动画情节。然而,这是我遇到问题的地方。我似乎无法让动画显示在我的嵌入式图形中。在下面的代码中,您可以看到我已经更改了plot_animated()函数以创建一个新的图形和轴,并且工作正常,但是当我将它们更改为使用时,绘图上什么self.figureself.ax没有显示。

下面是我的 UI 实现:

from tkinter import *
from matplotlib import animation
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import mpl_toolkits.mplot3d.axes3d as p3
import matplotlib.pyplot as plt

from lorenz_model import run_lorenz


class LorenzUI:
    input_width = 10

    def __init__(self):
        self.root = Tk()
        self.root.title("Lorenz Attractor")
        self.root.geometry("1290x960")

        self.chart_fr = Frame(self.root)

        self.figure = plt.Figure()
        self.figure.tight_layout()
        self.ax = p3.Axes3D(self.figure)
        self.canvas = FigureCanvasTkAgg(self.figure, master=self.chart_fr)
        self.canvas.get_tk_widget().grid(row=0, column=0, sticky="nsew")

        self.input_fr = Frame(self.root, relief=RAISED, bd=2)

        self.init_x_lbl = Label(self.input_fr, text="Initial X")
        self.init_x_input = Entry(self.input_fr, width=self.input_width)
        self.init_y_lbl = Label(self.input_fr, text="Initial Y")
        self.init_y_input = Entry(self.input_fr, width=self.input_width)
        self.init_z_lbl = Label(self.input_fr, text="Initial Z")
        self.init_z_input = Entry(self.input_fr, width=self.input_width)

        self.rho_lbl = Label(self.input_fr, text="Rho")
        self.rho_input = Entry(self.input_fr, width=self.input_width)
        self.sigma_lbl = Label(self.input_fr, text="Sigma")
        self.sigma_input = Entry(self.input_fr, width=self.input_width)
        self.beta_lbl = Label(self.input_fr, text="Beta")
        self.beta_input = Entry(self.input_fr, width=self.input_width)
        self.time_lbl = Label(self.input_fr, text="Sim Time")
        self.time_input = Entry(self.input_fr, width=self.input_width)

        self.is_animated = IntVar(value=1)
        self.animate_chk = Checkbutton(self.input_fr, text="Animate?", variable=self.is_animated)
        self.run_btn = Button(self.input_fr, text="Run", command=self.generate_model)
        self.stop_btn = Button(self.input_fr, text="Stop")
        self.reset_btn = Button(self.input_fr, text="Reset Defaults", command=self.set_defaults)

        self.root.rowconfigure(0, minsize=50, weight=1)
        self.root.columnconfigure(0, minsize=800, weight=1)

        self.init_x_lbl.grid(row=0, column=0, padx=5, pady=5)
        self.init_x_input.grid(row=0, column=1, padx=5, pady=5)
        self.init_y_lbl.grid(row=1, column=0, padx=5, pady=5)
        self.init_y_input.grid(row=1, column=1, padx=5, pady=5)
        self.init_z_lbl.grid(row=2, column=0, padx=5, pady=5)
        self.init_z_input.grid(row=2, column=1, padx=5, pady=5)

        self.rho_lbl.grid(row=4, column=0, padx=5, pady=5)
        self.rho_input.grid(row=4, column=1, padx=5, pady=5)
        self.sigma_lbl.grid(row=5, column=0, padx=5, pady=5)
        self.sigma_input.grid(row=5, column=1, padx=5, pady=5)
        self.beta_lbl.grid(row=6, column=0, padx=5, pady=5)
        self.beta_input.grid(row=6, column=1, padx=5, pady=5)
        self.time_lbl.grid(row=7, column=0, padx=5, pady=5)
        self.time_input.grid(row=7, column=1, padx=5, pady=5)

        self.animate_chk.grid(row=8, column=0, padx=5, pady=5)
        self.run_btn.grid(row=9, column=0, padx=5, pady=5)
        self.stop_btn.grid(row=9, column=1, padx=5, pady=5)
        self.reset_btn.grid(row=10, column=0, padx=5, pady=5)

        self.chart_fr.grid(row=0, column=0, sticky="nse")
        self.input_fr.grid(row=0, column=1, sticky="nsew")

        self.set_defaults()

    @staticmethod
    def __set_default(field, value):
        field.delete(0, END)
        field.insert(0, value)

    def set_defaults(self):
        self.__set_default(self.init_x_input, 1.0)
        self.__set_default(self.init_y_input, 1.0)
        self.__set_default(self.init_z_input, 1.0)
        self.__set_default(self.rho_input, 28.0)
        self.__set_default(self.sigma_input, 10.0)
        self.__set_default(self.beta_input, 2.666667)
        self.__set_default(self.time_input, 40.0)
        self.animate_chk.select()

    def generate_model(self):
        x = float(self.init_x_input.get())
        y = float(self.init_y_input.get())
        z = float(self.init_z_input.get())
        rho = float(self.rho_input.get())
        sigma = float(self.sigma_input.get())
        beta = float(self.beta_input.get())
        t = float(self.time_input.get())
        dt = 0.01  # TODO Needed as Input?

        self.model = run_lorenz([x, y, z], t, dt, rho, sigma, beta)

        if self.is_animated.get():
            self.__plot_animated()
        else:
            self.__plot_static()

    def __plot_animated(self):
        figure = plt.figure()
        ax = p3.Axes3D(figure)
        ax.clear()

        ax.set_xlim3d([min(self.model[0]), max(self.model[0])])
        ax.set_ylim3d([min(self.model[1]), max(self.model[1])])
        ax.set_zlim3d([min(self.model[2]), max(self.model[2])])

        n_frames = len(self.model[0])
        line, = ax.plot(self.model[0, 0:1], self.model[1, 0:1], self.model[2, 0:1])

        self.ani = animation.FuncAnimation(figure, self.__animate, n_frames, fargs=(self.model, line),
                                       interval=1, blit=False)
        plt.show()

    def __animate(self, num, data, line):
        line.set_data(data[:2, :num])
        line.set_3d_properties(data[2, :num])

    def __plot_static(self):
        self.ax.clear()
        self.ax.plot(self.model[0, :], self.model[1, :], self.model[2, :])
        self.canvas.draw()

    def run(self):
        self.root.mainloop()

我有一个单独的文件来计算由以下两个函数组成的模型:

import numpy as np
from scipy.integrate import odeint


def f(state, t, rho, sigma, beta):
    x, y, z = state  # Unpack the state vector
    return sigma * (y - x), x * (rho - z) - y, x * y - beta * z  # Derivatives


def run_lorenz(initial_state, t, dt, rho, sigma, beta):
    times = np.arange(0.0, t, dt)
    result = odeint(f, initial_state, times, args=(rho, sigma, beta))
    return np.array(np.array(result).T)

最后是一个运行应用程序的主文件

from lorenz_ui import LorenzUI


if __name__ == '__main__':
    app = LorenzUI()
    app.run()

所以我真的有2个问题。

  1. 如何让动画显示在嵌入图形中而不是创建一个新图形?
  2. 如果你运行它,你会看到绘图只显示在可用空间的右上角,它是如何让它增长并填满所有可用空间的?

提前感谢您的帮助。

4

0 回答 0