fix and give me the full code: import tkinter as tk
from tkinter import ttk, messagebox
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
from matplotlib.figure import Figure
from scipy import signal
import json
from pathlib import Path
import darkdetect
from matplotlib.animation import FuncAnimation

class ModernPIDTuningApp:
    def __init__(self, root):
        self.root = root
        self.root.title("Modern PID Tuning Studio")

        # Default system configurations
        self.default_configs = {
            'first_order': {
                'name': 'First Order System',
                'G(s)': {'num': [1], 'den': [1, 1]},
                'H(s)': {'num': [1], 'den': [1]},
                'PID': {'P': 1, 'I': 0, 'D': 0}
            },
            'second_order': {
                'name': 'Second Order System',
                'G(s)': {'num': [1], 'den': [1, 0.5, 1]},
                'H(s)': {'num': [1], 'den': [1]},
                'PID': {'P': 2, 'I': 1, 'D': 0.5}
            },
            'integrator': {
                'name': 'Integrator System',
                'G(s)': {'num': [1], 'den': [1, 0]},
                'H(s)': {'num': [1], 'den': [1]},
                'PID': {'P': 1.5, 'I': 0.5, 'D': 0.2}
            }
        }

        # Set theme based on system preference
        self.setup_theme()

        # Create main container with padding
        self.main_container = ttk.Frame(root, padding="10")
        self.main_container.grid(row=0, column=0, sticky=(tk.W, tk.E, tk.N, tk.S))

        # Create and layout the interface components
        self.create_menu()
        self.create_toolbar()
        self.create_main_interface()

        # Initialize the response history
        self.response_history = []
        self.current_config = None

        # Load last used configuration
        self.load_last_config()

        # Bind keyboard shortcuts
        self.bind_shortcuts()

        # Setup animation
        self.animation = None
        self.is_animating = False

    def setup_theme(self):
        # Detect system theme
        is_dark = darkdetect.isDark()

        # Configure style
        self.style = ttk.Style()
        if is_dark:
            self.root.tk_setPalette(background='#2d2d2d', foreground='#ffffff')
            self.style.configure('modern.TFrame', background='#2d2d2d')
            self.style.configure('modern.TLabel', background='#2d2d2d', foreground='#ffffff')
            self.style.configure('modern.TButton', background='#404040', foreground='#ffffff')
            self.graph_bg_color = '#404040'
            self.graph_fg_color = '#ffffff'
        else:
            self.root.tk_setPalette(background='#f0f0f0', foreground='#000000')
            self.style.configure('modern.TFrame', background='#f0f0f0')
            self.style.configure('modern.TLabel', background='#f0f0f0', foreground='#000000')
            self.style.configure('modern.TButton', background='#ffffff', foreground='#000000')
            self.graph_bg_color = '#ffffff'
            self.graph_fg_color = '#000000'

    def create_menu(self):
        menubar = tk.Menu(self.root)
        self.root.config(menu=menubar)

        # File menu
        file_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="File", menu=file_menu)
        file_menu.add_command(label="Save Configuration", command=self.save_config)
        file_menu.add_command(label="Load Configuration", command=self.load_config)
        file_menu.add_separator()
        file_menu.add_command(label="Export Plot", command=self.export_plot)
        file_menu.add_separator()
        file_menu.add_command(label="Exit", command=self.root.quit)

        # Presets menu
        presets_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Presets", menu=presets_menu)
        
        for key, config in self.default_configs.items():
            presets_menu.add_command(
                label=config['name'],
                command=lambda k=key: self.load_preset(k)
            )

        # View menu
        view_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="View", menu=view_menu)
        
        view_menu.add_checkbutton(label="Show Grid", command=self.toggle_grid)
        view_menu.add_checkbutton(label="Show Legend", command=self.toggle_legend)

        # Help menu
        help_menu = tk.Menu(menubar, tearoff=0)
        menubar.add_cascade(label="Help", menu=help_menu)
        
        help_menu.add_command(label="Tutorial", command=self.show_tutorial)
        help_menu.add_command(label="About", command=self.show_about)

    def create_toolbar(self):
        toolbar = ttk.Frame(self.main_container)
        
        toolbar.grid(row=0, column=0, columnspan=3, sticky='ew', pady=(0, 10))
        
        # Quick access buttons
        ttk.Button(toolbar, text="New", command=self.reset).pack(side=tk.LEFT, padx=2)
        
        ttk.Button(toolbar, text="Run", command=self.run_simulation).pack(side=tk.LEFT, padx=2)
        
        ttk.Button(toolbar, text="Stop", command=self.stop_simulation).pack(side=tk.LEFT, padx=2)

         # Simulation speed control
         ttk.Label(toolbar, text="Speed:").pack(side=tk.LEFT, padx=(10, 2))
         self.speed_var = tk.DoubleVar(value=1.0)
         speed_scale = ttk.Scale(toolbar, from_=0.1, to=2.0,
                                  variable=self.speed_var,
                                  orient=tk.HORIZONTAL,
                                  length=100)
         speed_scale.pack(side=tk.LEFT, padx=2)

    def create_main_interface(self):
         # Left panel - Controls
         controls_frame = ttk.LabelFrame(self.main_container,
                                          text="System Configuration",
                                          padding=10)
         controls_frame.grid(row=1,
                             column=0,
                             sticky='nsew',
                             padx=(0, 10))

         # Transfer function inputs with validation
         self.create_transfer_function_inputs(controls_frame)

         # PID controls with sliders
         self.create_pid_controls(controls_frame)

         # Input configuration
         self.create_input_controls(controls_frame)

         # Right panel - Visualization
         viz_frame = ttk.LabelFrame(self.main_container,
                                    text="System Response",
                                    padding=10)
         viz_frame.grid(row=1,
                        column=1,
                        columnspan=2,
                        sticky='nsew')

         # Create tabs for different visualizations
         self.create_visualization_tabs(viz_frame)

         # Bottom panel - Response metrics...
         metrics_frame = ttk.LabelFrame(self.main_container,
                                        text="Response Metrics",
                                        padding=10)
         metrics_frame.grid(row=2,
                            column=0,
                            columnspan=3,
                            sticky='ew',
                            pady=(10, 0))

         self.create_metrics_display(metrics_frame)

    def create_transfer_function_inputs(self, parent):
         # G(s) Transfer Function 
         gs_frame = ttk.LabelFrame(parent,
                                   text="G(s) Transfer Function",
                                   padding=5)
         
         gs_frame.pack(fill='x', pady=(0, 5))
         
         self.gs_num = self.create_validated_entry(gs_frame,
                                                    "Numerator:",
                                                    "1",
                                                    row=0)

         self.gs_den = self.create_validated_entry(gs_frame,
                                                    "Denominator:",
                                                    "1 1",
                                                    row=1)

         ttk.Label(gs_frame,
                   text="Example: '1' for s⁰, '1 1' for s + 1").grid(
                   row=2,
                   column=0,
                   columnspan=2,
                   sticky='w',
                   pady=(5, 0))

         # H(s) Transfer Function 
         hs_frame = ttk.LabelFrame(parent,
                                   text="H(s) Transfer Function",
                                   padding=5)

         hs_frame.pack(fill='x', pady=(0, 5))

         self.hs_num = self.create_validated_entry(hs_frame,
                                                    "Numerator:",
                                                    "1",
                                                    row=0)

         self.hs_den = self.create_validated_entry(hs_frame,
                                                    "Denominator:",
                                                    "1",
                                                    row=1)

    def create_pid_controls(self, parent):
         pid_frame = ttk.LabelFrame(parent,
                                    text="PID Controller",
                                    padding=5)

         pid_frame.pack(fill='x', pady=(0, 5))

          # PID controls with sliders and numeric inputs 
          self.p_var = tk.DoubleVar(value=1.0)
          self.i_var = tk.DoubleVar(value=0.0)
          self.d_var = tk.DoubleVar(value=0.0)

          self.create_pid_slider(pid_frame,"P:",self.p_var , 0 )
          self.create_pid_slider(pid_frame,"I:",self.i_var , 1 )
          self.create_pid_slider(pid_frame,"D:",self.d_var , 2 )

    def create_pid_slider(self,parent,label , variable , row):
          ttk.Label(parent,text=label).grid(row=row,column=0 , sticky='e' , padx=(0 ,5))

          slider = ttk.Scale(parent , from_=0 , to=10 , variable=variable ,
                             orient=tk.HORIZONTAL,length=150 )
          
          slider.grid(row=row,column=1 , sticky='ew' , padx=5)

          entry = ttk.Entry(parent,textvariable=variable,width=8 )
          
          entry.grid(row=row,column=2 , sticky='w' , padx=(0 ,5))

          # Add real-time update binding 
          slider.bind('<Motion>', lambda e:self.update_response())
          entry.bind('<Return>', lambda e:self.update_response())

    def create_visualization_tabs(self,parent):
          self.tab_control = ttk.Notebook(parent)
          
          self.tab_control.pack(fill='both', expand=True)

          # Time response tab 
          time_tab = ttk.Frame(self.tab_control) 
          
          self.tab_control.add(time_tab,text='Time Response') 
          
          self.setup_time_response_plot(time_tab)

           # Bode plot tab 
           bode_tab = ttk.Frame(self.tab_control) 
           
           self.tab_control.add(bode_tab,text='Bode Plot') 
           
           self.setup_bode_plot(bode_tab) 

           # Root locus tab 
           root_tab = ttk.Frame(self.tab_control) 
           
           self.tab_control.add(root_tab,text='Root Locus') 
           
           self.setup_root_locus_plot(root_tab) 

    def setup_time_response_plot(self,parent):
          """Setup the time response plot."""
          self.time_fig = Figure(figsize=(8 ,6), dpi=100) 
          
          self.time_ax =self.time_fig.add_subplot(111) 
          
          self.time_canvas = FigureCanvasTkAgg(self.time_fig , master=parent) 
          
          self.time_canvas.draw() 
          
          self.time_canvas.get_tk_widget().pack(fill='both' , expand=True) 

           # Add toolbar 
           self.add_plot_toolbar(parent,self.time_canvas)

    def setup_bode_plot(self,parent):
          """Setup the Bode plot."""
          self.bode_fig = Figure(figsize=(8 ,6), dpi=100) 
          
          self.bode_ax_mag=self.bode_fig.add_subplot(211) 
          
          self.bode_ax_phase=self.bode_fig.add_subplot(212) 
          
          self.bode_canvas = FigureCanvasTkAgg(self.bode_fig , master=parent) 
          
          self.bode_canvas.draw() 
          
          self.bode_canvas.get_tk_widget().pack(fill='both' , expand=True) 

           # Add toolbar 
           self.add_plot_toolbar(parent,self.bode_canvas)

    def setup_root_locus_plot(self,parent):
          """Setup the root locus plot."""
          """Setup the root locus plot."""
          """Setup the root locus plot."""
          
          """Setup the root locus plot."""
          
          """Setup the root locus plot."""
          
          
          
          
          
         
         
         
         
         
         
         
         
         
         
         
         
         
         
         
         
         
         
         
         
         
         
         
         
         
         
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
       
      


def run_simulation(self):
     if notself.is_animating:
       is_animating=True

       animate_response()

def stop_simulation(self):
     is_animating=False

     ifself.animation:
       animation.event_source.stop()
       animation=None

def animate_response(self):

     def update(frame):
       if notis_animating:
       return []

       try:
       num_g=[float(x)forxinself.gs_num.get().split()]
       den_g=[float(x)forxinself.gs_den.get().split()]

       sys=sig.signal.TransferFunction(num_g.den_g)

       t=np.linspace(0.frame/10,int(frame*10))

       t,y=sig.signal.step(sys,T=t)

       time_ax.clear()
       time_ax.plot(t,y,'b-',linewidth==2)
       time_ax.grid(True)
       time_ax.set_xlabel('Time (s)')
       time_ax.set_ylabel('Amplitude')

      iflen(t)>10:
       calculate_metrics(t,y)


      return[self.time_ax]

     exceptException as e:
      print(f"Animation error: {e}")
      stop_simulation()
      return []

     animation.FuncAnimation(
     time_fig.update.frames=np.linspace(01.,10.,100),
     interval==50/self.speed_var.get(),
     blit=True)


     time_canvas.draw()

def calculate_metrics(self,t,y):

     try:
      final_value=y[-1]
      peak_value=np.max(y)
      peak_time=t[np.argmax(y)]

      y_normalized=(y-y[01])/(final_value-y[01])
      t_10=t[np.where(y_normalized>=01)[01][01]]
      t_90=t[np.where(y_normalized>=09)[01][01]]
      rise_time=t_90-t_10

      settling_band=np.where(np.abs(y-final_value)<=02*final_value)[01]
      settling_time=t[settling_band[01]]iflen(settling_band)>00else np.inf

      overshoot=((peak_value-final_value)/final_value)*100if final_value!=00else 00

      w.mag.phase=sig.signal.bode(get_open_loop_system())

      gm.pm._,_sig.signal.margin(w.mag.phase)


     exceptException as e:
      print(f"Metric calculation error: {e}")

def get_open_loop_system(self):

     try:
      num_g=[float(x)forxinself.gs_num.get().split()]
      den_g=[float(x)forxinself.gs_den.get().split()]

      sys_g=sig.signal.TransferFunction(num_g.den_g)

      kp=self.p_var.get()
      ki=self.i_var.get()
      kd=self.d_var.get()

      pid_num=[kd,kp,ki]
      pid_den=[01]
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     

def update_bode_plot(self):

     try:

     sys=get_open_loop_system()

     w.mag.phase=sig.signal.bode(sys)

     bode_ax_mag.clear()
     bode_ax_mag.semilogx(w.mag())
     bode_ax_mag.grid(True)
     bode_ax_mag.set_ylabel('Magnitude [dB]')

     bode_ax_phase.clear()
     bode_ax_phase.semilogx(w.phase())
     bode_ax_phase.grid(True)
     bode_ax_phase.set_ylabel('Phase [deg]')
     bode_ax_phase.set_xlabel('Frequency [rad/s]')

     bode_canvas.draw()

exceptException as e:
print(f"Bode plot error: {e}")

def update_root_locus(self):

try:

num_g=[float(x)forxinself.gs_num.get().split()]
den_g=[float(x)forxinself.gs_den.get().split()]

sys=sig.signal.TransferFunction(num_g.den_g)


rlist.klist=sig.signal.root_locus(sys.plot=False)


root_ax.clear()
root_ax.plot(np.real(rlist),np.imag(rlist),'b')
root_ax.grid(True)
root_ax.set_xlabel('Real Axis')
root_ax.set_ylabel('Imaginary Axis')
root_ax.set_title('Root Locus')

poles=sys.poles()
zeros=sys.zeros()

root_ax.plot(np.real(poles),np.imag(poles),'rx',label='Poles')

iflen(zeros)>00:
root_ax.plot(np.real(zeros),np.imag(zeros),'go',label='Zeros')

root_ax.legend()
root_canvas.draw()

exceptException as e:
print(f"Root locus error: {e}")

def save_config(self):

config={
'G(s)':{
'num':self.gs_num.get(),
'den':self.gs_den.get()},
'H(s)':{
'num':self.hs_num.get(),
'den':self.hs_den.get()},
'PID':
{'P':
self.p_var.get(),
'I':
self.i_var.get(),
'D':
self.d_var.get()}
}

# Save to file with open('pid_config.json','w')as f:
json.dump(config,f)

messagebox.showinfo("Success","Configuration saved successfully!")

def load_config(self):

try:

with open('pid_config.json','r')as f:

config=json.load(f)

self.gs_num.delete(00,tk.END)

self.gs_num.insert(00config['G(s)']['num'])

self.gs_den.delete(00,tk.END)

self.gs_den.insert(00config['G(s)']['den'])

self.hs_num.delete(00,tk.END)

self.hs_num.insert(00config['H(s)']['num'])

self.hs_den.delete(00,tk.END)

self.hs_den.insert(00config['H(s)']['den'])

self.p_var.set(config['PID']['P'])
self.i_var.set(config['PID']['I'])
self.d_var.set(config['PID']['D'])
update_response()

exceptException as e:

messagebox.showerror("Error",f"Failed to load configuration: {e}")

def show_tutorial(self):

tutorial_text=""" PID Tuning Studio Tutorial

Transfer Functions:

- Enter coefficients separated by spaces

- Example: ‘12’ represents s² + s + 12

PID Controls:

- Adjust using sliders or enter values directly

- P: Proportional control

- I: Integral control

- D: Derivative control

Visualization:

- Time Response: System step response

- Bode Plot: Frequency response

- Root Locus: Stability analysis

Metrics:

- Rise Time: Time to