如何同时加载两个带Matplotlib动画的Tkinter窗口并实现独立运行的计算场景?
如何同时加载两个带Matplotlib动画的Tkinter窗口并实现独立运行的计算场景?
嘿,我来帮你搞定这个问题!要实现两个独立的带Matplotlib动画的Tkinter窗口,核心思路就是把每个场景的参数、计算逻辑、绘图和动画都封装成独立的单元,让它们各玩各的,互不干扰。我把你提供的计算代码改成了可复用的类结构,每个窗口对应一个类实例,完美实现独立运行:
import numpy as np import matplotlib.pyplot as plt from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg from matplotlib.animation import FuncAnimation import tkinter as tk from tkinter import ttk class ScenarioWindow: def __init__(self, root, title, scenario_params): # 创建独立的Toplevel窗口,作为主窗口的子窗口 self.window = tk.Toplevel(root) self.window.title(title) # 存储当前场景的参数 self.params = scenario_params # 初始化计算结果容器 self.results = [] self.time_points = [] # 先搭好绘图框架和动画 self.setup_plot() self.animation = FuncAnimation(self.fig, self.update_plot, interval=200, blit=True) # 加个运行按钮,点击就触发当前场景的计算 run_btn = ttk.Button(self.window, text="运行场景", command=self.run_model) run_btn.pack(pady=10) # 单位转换函数,和原来的逻辑一致 def convert_to_seconds(self, duration): return duration * 60 # 分钟转秒 def convert_to_kg(self, amount): return amount / 1000 # 克转千克 def convert_to_per_second(self, rate): return rate / 3600 # 每小时转每秒 def run_model(self): # 清空之前的计算结果,避免和新结果混在一起 self.results.clear() self.time_points.clear() # 把参数从字典里取出来,方便后续计算 freq_per_year = self.params["frequency_per_year"] exposure_dur = self.params["exposure_duration"] product_amount = self.params["product_amount"] weight_frac = self.params["weight_fraction_substance"] room_vol = self.params["room_volume"] vent_rate = self.params["ventilation_rate"] inh_rate = self.params["inhalation_rate"] body_weight = self.params["body_weight_kg"] vapour_press = self.params["vapour_pressure"] temp = self.params["application_temperature"] mol_weight = self.params["molecular_weight"] # 执行单位转换 exposure_dur_sec = self.convert_to_seconds(exposure_dur) product_amount_kg = self.convert_to_kg(product_amount) vent_rate_per_sec = self.convert_to_per_second(vent_rate) inh_rate_per_sec = self.convert_to_per_second(inh_rate) # 这里是核心计算逻辑,我保留了你的框架,用了一个简单的浓度模拟示例 # 你可以替换成自己的真实模型 for t in np.linspace(0, exposure_dur_sec, 100): conc = (product_amount_kg * weight_frac * vapour_press) / (room_vol * (1 + vent_rate_per_sec * t)) self.results.append(conc) self.time_points.append(t / 60) # 把时间转回分钟,方便绘图 # 打印当前场景的结果 print(f"=== {self.window.title()} 计算结果 ===") print(f"最终浓度: {self.results[-1]:.6f} kg/m³") print(f"暴露时长: {exposure_dur} 分钟\n") def setup_plot(self): # 创建Matplotlib的图和轴,每个窗口用自己的实例 self.fig, self.ax = plt.subplots(figsize=(6, 4)) self.line, = self.ax.plot([], [], 'b-', linewidth=2) # 设置图表的标签和样式 self.ax.set_xlabel("时间 (分钟)") self.ax.set_ylabel("浓度 (kg/m³)") self.ax.set_title(self.window.title()) self.ax.set_xlim(0, self.params["exposure_duration"]) self.ax.set_ylim(0, 0.001) # 可以根据实际计算结果调整y轴范围 self.ax.grid(True) # 把Matplotlib画布嵌入到当前的Tkinter窗口里 self.canvas = FigureCanvasTkAgg(self.fig, master=self.window) self.canvas.draw() self.canvas.get_tk_widget().pack(pady=10) def update_plot(self, frame): # 动画更新函数,每次只更新到当前帧的数据 if self.time_points and self.results: self.line.set_data(self.time_points[:frame+1], self.results[:frame+1]) return self.line, if __name__ == "__main__": # 主Tk窗口,我们把它隐藏起来,只作为事件循环的载体 root = tk.Tk() root.withdraw() # 定义两个不同场景的参数,你可以随便修改这些值 scenario1_params = { "frequency_per_year": 365, "exposure_duration": 240, "product_amount": 5, "weight_fraction_substance": 0.6, "room_volume": 58, "ventilation_rate": 0.5, "inhalation_rate": 10, "body_weight_kg": 65, "vapour_pressure": 2, "application_temperature": 18, "molecular_weight": 100 } scenario2_params = { "frequency_per_year": 100, "exposure_duration": 120, "product_amount": 8, "weight_fraction_substance": 0.8, "room_volume": 30, "ventilation_rate": 1.0, "inhalation_rate": 12, "body_weight_kg": 70, "vapour_pressure": 3, "application_temperature": 22, "molecular_weight": 120 } # 创建两个独立的场景窗口,各自用自己的参数 window1 = ScenarioWindow(root, "场景1: 日常暴露模拟", scenario1_params) window2 = ScenarioWindow(root, "场景2: 高浓度作业模拟", scenario2_params) # 启动Tkinter的主事件循环,两个窗口都会正常运行 root.mainloop()
关键细节说明:
- 类封装隔离:每个
ScenarioWindow实例都是独立的,参数、计算结果、绘图对象都是自己的,完全不会和另一个窗口冲突 - 独立窗口载体:用
Toplevel创建子窗口,主窗口隐藏,既保证了事件循环的正常运行,又不会多出来一个没用的窗口 - 动画独立更新:每个窗口的
FuncAnimation只绑定自己的画布,动画播放互不影响 - 可扩展性强:如果要加第三个、第四个场景,直接复制参数字典,创建新的
ScenarioWindow实例就行
备注:内容来源于stack exchange,提问作者Nick
相关产品推荐
相关产品推荐

