Quantcast
Channel: Active questions tagged python - Stack Overflow
Viewing all articles
Browse latest Browse all 23131

Real-time Reflection Error of Circuit Switching on Voltage and Current Graph in Tkinter Matplotlib Integration

$
0
0

I'm working on a project where I've integrated a Tkinter GUI with Matplotlib to monitor voltage and current in a circuit simulation. I have a manual switch button to simulate circuit faults by toggling the switch. However, I'm facing an issue where the voltage and current graphs don't reflect the real-time changes when I toggle the switch.

The code is as follows:

import tkinter as tkimport numpy as npimport matplotlib.pyplot as pltfrom matplotlib.backends.backend_tkagg import FigureCanvasTkAggfrom matplotlib.animation import FuncAnimationt_max = 10.0  # Maximum time (seconds)fps = 20  # Frames per secondt = np.linspace(0, t_max, int(fps * t_max))  # Time sequence# Circuit parametersV_battery = 15  # Battery voltage (V)R_load = 10  # Load resistance (ohms)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)  # All time point voltages are initially battery voltage        self.CurrentOverTime = np.full_like(t_values, V_battery / R_load)  # Initialize current array        self.switch_states = np.ones_like(t_values, dtype=bool) * initial_switch_state  # Record the state of the switch at each time point        self.previous_switch_state = initial_switch_state  # Record the previous switch state        self.previous_switch_time_index = -1 # Initialize to -1, indicating no switch has been made    def calculate_circuit_response(self, current_time_index):        # Check if there is a switch change at the current time point        if current_time_index > self.previous_switch_time_index:            # Check if the switch state at the current time point is different from the previous time point            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:                    # Switch is open                    self.VoltageOverTime[current_time_index:next_switch_index] = 0                    self.CurrentOverTime[current_time_index:next_switch_index] = 0                else:                    # Switch is closed                    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])                # Update the time index of the last switch change                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):            # Only change the switch state at the specified time point and the defined time period after it            end_of_simulation = min(switch_time_index + 1, len(self.switch_states))  # Prevent overflow            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}")# Create a new window class for integrating matplotlib charts with Tkinter GUIclass CircuitSimulationGUI(tk.Tk):    def __init__(self):        super().__init__()        # Set window title        self.title("Circuit Operation Monitoring")        # Create the main container        container = tk.Frame(self)        container.pack(side="top", fill="both", expand=True)        container.grid_rowconfigure(0, weight=1)        container.grid_columnconfigure(0, weight=1)        # Initialize the chart        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()        # Embed the matplotlib chart into the Tkinter window        self.canvas = FigureCanvasTkAgg(self.fig, master=container)        self.canvas.draw()        self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1)        # Create the switch button        self.manual_switch_button = tk.Button(master=self, text="Close Circuit", command=self.toggle_manual_switch)        self.manual_switch_button.pack(pady=10)        # Initialize the animation        self.simulator = CircuitSimulator(t)        self.ani = None  # Initialize the animation instance variable to None        # Initialize state variables        self.current_time_index = 0        # Start the simulation        self.start_simulation()    def toggle_manual_switch(self):"""        Handle the click event of the manual switch button, toggle the switch state at the current time point and continue to affect subsequent states"""        # Get the index of the current time point        current_index = int(self.current_time_index)        # Toggle the switch state        self.simulator.toggle_switch_at_time(current_index)        self.simulator.calculate_circuit_response(current_index)        # Update the button text and command        if self.manual_switch_button["text"] == "Close Circuit":            self.manual_switch_button["text"] = "Open Circuit"        else:            self.manual_switch_button["text"] = "Close Circuit"        # Set the transition duration (assume 0.5 seconds)        transition_duration = 0.5  # in seconds        transition_frames = int(transition_duration * fps)        # Get the start and end time indices of the transition        start_index = current_index        end_index = min(start_index + transition_frames, len(t))        # Transition voltage and current arrays step by step        for i in range(start_index, end_index):            transition_ratio = (i - start_index) / transition_frames            if self.manual_switch_button["text"] == "Open Circuit":  # If the circuit is opened                # Transition from battery voltage and current to 0                self.simulator.VoltageOverTime[i] = (1 - transition_ratio) * V_battery                self.simulator.CurrentOverTime[i] = (1 - transition_ratio) * (V_battery / R_load)            else:                # Transition from 0 to battery voltage and current                self.simulator.VoltageOverTime[i] = transition_ratio * V_battery                self.simulator.CurrentOverTime[i] = transition_ratio * (V_battery / R_load)        # Update the entire chart, pass the index of the current time point        self.update_plot(current_index)          self.canvas.draw_idle()    def start_simulation(self):        # Use a more modern and compatible way to detect and control animation status        if self.ani is None:            self.ani = FuncAnimation(                fig=self.fig,                func=self.update_plot, # Pass the function instead of the method                 frames=len(t),                interval=200,                blit=True            )            self.canvas.draw_idle()        elif getattr(self.ani, '_is_running', False):  # Change here            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")  # Add this line of code to confirm if the chart has been updated        print("Plot Voltage:", V_circuit[-1], "V")        return self.V_line, self.I_line# Create and start the GUI applicationif __name__ == "__main__":    app = CircuitSimulationGUI()    app.mainloop()

I've tried updating the voltage and current arrays based on the switch state inside the toggle_manual_switch method, followed by updating the plot. I expected the voltage and current graphs to dynamically update with each switch toggle, showing the changes in real-time. However, the graphs only update after the simulation ends, not during the simulation.


Viewing all articles
Browse latest Browse all 23131

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>