各位大神好,我建立一个简单的由电池、开关和负载的电路,目的是通过开关实时操控来量测电路中电压、电流的变化。现在的代码如下:
import tkinter as tk
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.animation import FuncAnimation
t_max = 10.0 # 最大时间(秒)
fps = 20 # 帧率
t = np.linspace(0, t_max, int(fps * t_max)) # 时间序列
# 电路参数
V_battery = 15 # 电池电压(V)
R_load = 10 # 负载电阻(欧姆)
class CircuitSimulator:
def __init__(self, t_values, initial_switch_state=True):
self.t_values = t_values
self.VoltageOverTime = np.full_like(t_values, V_battery) # 初始时所有时间点电压均为电池电压
self.CurrentOverTime = np.full_like(t_values, V_battery / R_load) # 初始化电流数组
self.switch_states = np.ones_like(t_values, dtype=bool) * initial_switch_state # 记录每个时间点开关的状态
self.previous_switch_state = initial_switch_state # 记录上一个开关状态
self.previous_switch_time_index = -1 # 初始化为-1,表示尚未进行过切换
def calculate_circuit_response(self, current_time_index):
# 检查当前时间点是否有开关切换发生
if current_time_index > self.previous_switch_time_index:
# 检查当前时间点的开关状态是否与前一时刻不同
if self.switch_states[current_time_index] != self.switch_states[current_time_index - 1]:
self.previous_switch_state = self.switch_states[current_time_index]
next_switch_index = current_time_index + np.argmax(self.switch_states[current_time_index:] != self.switch_states[current_time_index])
if not self.previous_switch_state:
# 开关断开
self.VoltageOverTime[current_time_index:next_switch_index] = 0
self.CurrentOverTime[current_time_index:next_switch_index] = 0
else:
# 开关闭合
self.VoltageOverTime[current_time_index:next_switch_index] = V_battery * np.ones_like(self.VoltageOverTime[current_time_index:next_switch_index])
self.CurrentOverTime[current_time_index:next_switch_index] = V_battery / R_load * np.ones_like(self.CurrentOverTime[current_time_index:next_switch_index])
# 更新上一次开关切换的时间索引
self.previous_switch_time_index = next_switch_index
def toggle_switch_at_time(self, switch_time_index):
if 0 <= switch_time_index < len(self.switch_states):
# 只改变指定时间点及其之后已定义的时间段内的开关状态
end_of_simulation = min(switch_time_index + 1, len(self.switch_states)) # 防止越界
self.switch_states[switch_time_index:end_of_simulation] = not self.switch_states[switch_time_index]
else:
raise IndexError(f"Invalid time index: {switch_time_index}. Maximum allowed index is {len(self.switch_states) - 1}")
# 创建一个新的窗口类,用于集成matplotlib图表与Tkinter GUI
class CircuitSimulationGUI(tk.Tk):
def __init__(self):
super().__init__()
# 设置窗口标题
self.title("电路运行状态监控")
# 创建主容器
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
# 初始化图表
self.fig, self.axs = plt.subplots(2, 1, figsize=(10, 6))
self.V_line, = self.axs[0].plot([], [], label='Circuit Voltage (V)')
self.I_line, = self.axs[1].plot([], [], label='Circuit Current (A)', color='r')
for ax in self.axs:
ax.set_xlabel('Time (s)')
ax.set_ylabel('Value')
ax.legend()
# 将matplotlib图表嵌入到Tkinter窗口中
self.canvas = FigureCanvasTkAgg(self.fig, master=container)
self.canvas.draw()
self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)
# 创建开关按钮
self.manual_switch_button = tk.Button(master=self, text="Close Circuit", command=self.toggle_manual_switch)
self.manual_switch_button.pack(pady=10)
# 初始化动画
self.simulator = CircuitSimulator(t)
self.ani = None # 将动画实例化变量初始化为None
# 初始化状态变量
self.current_time_index = 0
# 启动模拟
self.start_simulation()
def toggle_manual_switch(self):
"""
处理手动开关按钮点击事件,切换当前时刻的开关状态并持续影响后续状态
"""
# 获取当前时刻的索引
current_index = int(self.current_time_index)
# 切换开关状态
self.simulator.toggle_switch_at_time(current_index)
self.simulator.calculate_circuit_response(current_index)
# 更新按钮文本和命令
if self.manual_switch_button["text"] == "Close Circuit":
self.manual_switch_button["text"] = "Open Circuit"
else:
self.manual_switch_button["text"] = "Close Circuit"
# 更新整个图表,传递当前时间点的索引
self.update_plot(current_index)
self.canvas.draw_idle()
def start_simulation(self):
# 使用更现代且兼容的方式检测和控制动画状态
if self.ani is None:
self.ani = FuncAnimation(
fig=self.fig,
func=self.update_plot, #传递函数而非方法
frames=len(t),
interval=200,
blit=True
)
self.canvas.draw_idle()
elif getattr(self.ani, '_is_running', False): # 更改此处
self.ani.event_source.stop()
self.ani.event_source.start()
else:
self.ani.event_source.start()
def update_plot(self, frame):
self.simulator.calculate_circuit_response(frame)
time = t[frame]
V_circuit = self.simulator.VoltageOverTime[:frame+1]
I_circuit = self.simulator.CurrentOverTime[:frame+1]
self.V_line.set_data(t[:len(V_circuit)], V_circuit)
self.I_line.set_data(t[:len(I_circuit)], I_circuit)
self.axs[0].set_xlim(0, t_max)
self.axs[1].set_xlim(0, t_max)
self.axs[0].set_ylim(0, 20)
self.axs[1].set_ylim(0, 2)
print("Plot updated") # 添加这行代码来确认图表是否被更新
print("Plot Voltage:", V_circuit[-1], "V")
return self.V_line, self.I_line
# 创建并启动GUI应用
if __name__ == "__main__":
app = CircuitSimulationGUI()
app.mainloop()
现在基本运行时没有问题,但是在逻辑上有些不对。
1、在点击模拟开关的按钮时,整体的电压电流从0时刻,而不是点击按钮时刻开始。
2、按钮无法实现电路的断开和闭合。
我曾经改动了在CircuitSimulator类中的calculate_circuit_response,CircuitSimulationGUI(tk.Tk)中的toggle_manual_switch以及update_plot,但都达不到理想的效果。也曾经到某四字字母网站求助,但是回答的都是GPT未经验证的答案。
希望获得效果如下图所示:
这种问题很赞
修改值不持久问题。能满足逻辑,但思想好像和你的不太一致,又不太想花太多时间了解你的意图。