当前位置: 首页 > 知识库问答 >
问题:

python - 设计tkinter控制按钮实时生成函数图像出现问题?

荆钱明
2024-04-18

各位大神好,我建立一个简单的由电池、开关和负载的电路,目的是通过开关实时操控来量测电路中电压、电流的变化。现在的代码如下:

import tkinter as tkimport numpy as npimport matplotlib.pyplot as pltfrom matplotlib.backends.backend_tkagg import FigureCanvasTkAggfrom matplotlib.animation import FuncAnimationt_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 GUIclass 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未经验证的答案。
希望获得效果如下图所示:

共有1个答案

陆昊
2024-04-18

这种问题很赞

def toggle_manual_switch(self):    """    处理手动开关按钮点击事件,切换当前时刻的开关状态并持续影响后续状态    """    # 获取当前时刻的索引    current_index = int(self.current_time_index)  # FIXME: Q1.这个值没有更新过def update_plot(self, frame):    self.simulator.calculate_circuit_response(frame)    time = t[frame]    # FIXME: A1.这里更新一下    self.current_time_index = frame

修改值不持久问题。能满足逻辑,但思想好像和你的不太一致,又不太想花太多时间了解你的意图。

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]            self.previous_switch_state = not self.previous_switch_state  # TODO: 这块的值反正需要修改一下的            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:] = 0 # FIXME: VoltageOverTime 和 CurrentOverTime 的赋值有些问题,之前是只赋值一个点?                self.CurrentOverTime[current_time_index:] = 0            else:                # 开关闭合                self.VoltageOverTime[current_time_index:] = V_battery * np.ones_like(                    self.VoltageOverTime[current_time_index:])                self.CurrentOverTime[current_time_index:] = V_battery / R_load * np.ones_like(                    self.CurrentOverTime[current_time_index:])            # 更新上一次开关切换的时间索引            self.previous_switch_time_index = next_switch_index

image.png

 类似资料:
  • 问题内容: 我想生成大量的Tkinter ,它们可以做不同的事情。我有以下代码: 如果为5,尽管我得到标记为的按钮,但单击它们时都可以。 我需要第一个按钮执行,第二个按钮执行,依此类推。 问题答案: 我认为问题在于,循环结束后,会获取的最终值。这应该解决(未测试): 更新资料 顺便说一句,这是通过在函数中添加一个自变量的默认值来实现的,该默认值是根据在循环中创建每个变量时的值计算得出的,而不是在稍

  • Button 控件是 Tkinter 中常用的窗口部件之一,同时也是实现程序与用户交互的主要控件。通过用户点击按钮的行为来执行回调函数,是 Button 控件的主要功用。首先自定义一个函数或者方法,然后将函数与按钮关联起来,最后,当用户按下这个按钮时,Tkinter 就会自动调用相关函数。 按钮控件使用起来非常简单,它同样可以包含文本、图像、位图,并通过 参数回调函数。当然按钮也并非一定要执行回调

  • 我有一个简单的PDF与一个按钮字段,我想用图像填充。 按钮字段在文档中出现多次。我想在字段级别设置一次图像,以便在字段的每次出现时都显示图像。 我用于测试的PDF。 失败的代码: 引发的异常: 我使用的是iText7.1.4(上一个7.x版本)。

  • 本文向大家介绍Python 窗体(tkinter)按钮 位置实例,包括了Python 窗体(tkinter)按钮 位置实例的使用技巧和注意事项,需要的朋友参考一下 如下所示: 以上这篇Python 窗体(tkinter)按钮 位置实例就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持呐喊教程。

  • 本文向大家介绍js实现的倒计时按钮实例,包括了js实现的倒计时按钮实例的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了js实现的倒计时按钮。分享给大家供大家参考。具体分析如下: 效果图如下: 具体代码如下: 这里只是一个小例子,在实际运用中,可能需要记录各种时间,考虑到页面刷新的变化,需要用cookie来进行数据存储等等! 希望本文所述对大家的javascript程序设计有所帮助。

  • 本文向大家介绍php输出控制函数和输出函数生成静态页面,包括了php输出控制函数和输出函数生成静态页面的使用技巧和注意事项,需要的朋友参考一下 本文实例为大家分享了php输出控制函数和输出函数生成静态页面的具体代码,供大家参考,具体内容如下 Output Control 函数详解: flush - 刷新输出缓冲 ob_clean - 清空输出缓冲区 ob_end_clean - 清空缓冲区并关闭输