|
| 1 | +import customtkinter as ctk |
| 2 | +import tkinter as tk |
| 3 | +from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg |
| 4 | +from matplotlib.figure import Figure |
| 5 | +import time |
| 6 | + |
| 7 | +def create_cpu_tab(parent_frame): |
| 8 | + card = ctk.CTkFrame(parent_frame, fg_color="#FFFFFF", corner_radius=12) |
| 9 | + card.grid(row=0, column=0, sticky="nsew") |
| 10 | + card.grid_rowconfigure(2, weight=1) |
| 11 | + card.grid_columnconfigure(0, weight=1) |
| 12 | + |
| 13 | + top_frame = ctk.CTkFrame(card, fg_color="transparent") |
| 14 | + top_frame.grid(row=0, column=0, sticky="ew", padx=20, pady=10) |
| 15 | + ctk.CTkLabel(top_frame, text="CPU Scheduling & Analytics", font=ctk.CTkFont(size=18, weight="bold"), text_color="#111827").pack(side="left") |
| 16 | + |
| 17 | + # Layout: Top is Queues & Gantt, Bottom is Graph & Stats |
| 18 | + visual_frame = ctk.CTkFrame(card, fg_color="transparent") |
| 19 | + visual_frame.grid(row=1, column=0, sticky="nsew", padx=20) |
| 20 | + |
| 21 | + ctk.CTkLabel(visual_frame, text="Ready Queue (Process Creation)", font=ctk.CTkFont(size=14, weight="bold"), text_color="#374151").pack(anchor="w", pady=(10,0)) |
| 22 | + queue_canvas = tk.Canvas(visual_frame, height=80, bg="#F9FAFB", highlightthickness=1, highlightbackground="#E5E7EB") |
| 23 | + queue_canvas.pack(fill="x", pady=5) |
| 24 | + |
| 25 | + ctk.CTkLabel(visual_frame, text="Execution Gantt Chart", font=ctk.CTkFont(size=14, weight="bold"), text_color="#374151").pack(anchor="w", pady=(10,0)) |
| 26 | + cv = tk.Canvas(visual_frame, height=120, bg="#F9FAFB", highlightthickness=1, highlightbackground="#E5E7EB") |
| 27 | + cv.pack(fill="x", pady=5) |
| 28 | + |
| 29 | + bottom_frame = ctk.CTkFrame(card, fg_color="transparent") |
| 30 | + bottom_frame.grid(row=2, column=0, sticky="nsew", padx=20, pady=10) |
| 31 | + bottom_frame.grid_columnconfigure(0, weight=1) |
| 32 | + bottom_frame.grid_columnconfigure(1, weight=1) |
| 33 | + |
| 34 | + # Graph Area |
| 35 | + graph_parent = ctk.CTkFrame(bottom_frame, fg_color="transparent") |
| 36 | + graph_parent.grid(row=0, column=0, sticky="nsew", padx=(0,10)) |
| 37 | + fig = Figure(figsize=(4,3), dpi=100) |
| 38 | + fig.patch.set_facecolor('#F9FAFB') |
| 39 | + ax = fig.add_subplot(111) |
| 40 | + ax.set_facecolor('#FFFFFF') |
| 41 | + ax.set_title("Real-Time Waiting Time Trend") |
| 42 | + ax.set_ylabel("Waiting Time") |
| 43 | + ax.set_xlabel("Process Index") |
| 44 | + graph_canvas = FigureCanvasTkAgg(fig, master=graph_parent) |
| 45 | + graph_canvas.draw() |
| 46 | + graph_canvas.get_tk_widget().pack(fill="both", expand=True) |
| 47 | + |
| 48 | + # Stats Area |
| 49 | + stats_frame = ctk.CTkFrame(bottom_frame, fg_color="#F9FAFB", corner_radius=8, border_width=1, border_color="#E5E7EB") |
| 50 | + stats_frame.grid(row=0, column=1, sticky="nsew", padx=(10,0)) |
| 51 | + ctk.CTkLabel(stats_frame, text="Simulation Output", font=ctk.CTkFont(weight="bold"), text_color="#111827").pack(pady=10) |
| 52 | + result_box = tk.Text(stats_frame, height=10, width=30, font=("Courier", 11), bg="#F9FAFB", fg="#111827", relief="flat") |
| 53 | + result_box.pack(fill="both", expand=True, padx=10, pady=10) |
| 54 | + |
| 55 | + processes = [ |
| 56 | + {"id": "P1", "burst": 4, "priority": 2}, |
| 57 | + {"id": "P2", "burst": 3, "priority": 1}, |
| 58 | + {"id": "P3", "burst": 5, "priority": 3} |
| 59 | + ] |
| 60 | + |
| 61 | + def draw_queue(procs): |
| 62 | + queue_canvas.delete("all") |
| 63 | + x = 20 |
| 64 | + for p in procs: |
| 65 | + queue_canvas.create_rectangle(x, 20, x+60, 60, fill="#FFD166", outline="#F59E0B", width=2) |
| 66 | + queue_canvas.create_text(x+30, 40, text=p["id"], font=("Arial", 11, "bold"), fill="#78350F") |
| 67 | + x += 80 |
| 68 | + |
| 69 | + def calculate_waiting_times(procs): |
| 70 | + waiting_times = [] |
| 71 | + for i, p in enumerate(procs): |
| 72 | + if i == 0: |
| 73 | + waiting_times.append(0) |
| 74 | + else: |
| 75 | + wait = sum(prev["burst"] for prev in procs[:i]) |
| 76 | + waiting_times.append(wait) |
| 77 | + return waiting_times |
| 78 | + |
| 79 | + def update_graph(waiting_times): |
| 80 | + ax.clear() |
| 81 | + ax.plot(waiting_times, marker='o', color="#1F4ED8") |
| 82 | + ax.set_title("Real-Time Waiting Time Trend") |
| 83 | + ax.set_ylabel("Waiting Time") |
| 84 | + ax.set_xlabel("Process Index") |
| 85 | + graph_canvas.draw() |
| 86 | + |
| 87 | + def simulate(algo="FCFS"): |
| 88 | + cv.delete("all") |
| 89 | + result_box.delete("1.0", tk.END) |
| 90 | + cv.update() |
| 91 | + queue_canvas.update() |
| 92 | + w = cv.winfo_width() |
| 93 | + |
| 94 | + procs = processes.copy() |
| 95 | + |
| 96 | + if algo == "Priority": |
| 97 | + procs.sort(key=lambda x: x["priority"]) |
| 98 | + elif algo == "SJF": |
| 99 | + procs.sort(key=lambda x: x["burst"]) |
| 100 | + |
| 101 | + y_pos = 60 |
| 102 | + cv.create_line(50, y_pos+30, w-50, y_pos+30, fill="#E5E7EB", width=2) |
| 103 | + |
| 104 | + wait_times = calculate_waiting_times(procs) |
| 105 | + |
| 106 | + # Process Creation Animation (Move to Ready Queue) |
| 107 | + queue_canvas.delete("all") |
| 108 | + |
| 109 | + def animate_creation(idx=0): |
| 110 | + if idx < len(procs): |
| 111 | + p = procs[idx] |
| 112 | + x_start = -50 |
| 113 | + x_end = 20 + (idx * 80) |
| 114 | + |
| 115 | + rect = queue_canvas.create_rectangle(x_start, 20, x_start+60, 60, fill="#4CAF50", outline="#22C55E", width=2) |
| 116 | + txt = queue_canvas.create_text(x_start+30, 40, text=p["id"], font=("Arial", 11, "bold"), fill="#14532D") |
| 117 | + |
| 118 | + def slide(cx): |
| 119 | + if cx < x_end: |
| 120 | + queue_canvas.move(rect, 15, 0) |
| 121 | + queue_canvas.move(txt, 15, 0) |
| 122 | + parent_frame.after(30, lambda: slide(cx+15)) |
| 123 | + else: |
| 124 | + animate_creation(idx+1) |
| 125 | + slide(x_start) |
| 126 | + else: |
| 127 | + # Proceed to Gantt rendering |
| 128 | + run_gantt() |
| 129 | + |
| 130 | + def run_gantt(): |
| 131 | + x = 50 |
| 132 | + index = 0 |
| 133 | + live_waits = [] |
| 134 | + |
| 135 | + def step(): |
| 136 | + nonlocal x, index |
| 137 | + if index < len(procs): |
| 138 | + p = procs[index] |
| 139 | + width = p["burst"] * 30 |
| 140 | + |
| 141 | + # Update Ready Queue visually (remove process) |
| 142 | + draw_queue(procs[index+1:]) |
| 143 | + |
| 144 | + # Draw Block |
| 145 | + cv.create_rectangle(x, y_pos-30, x+width, y_pos+30, fill="#10B981", outline="#059669", width=2) |
| 146 | + cv.create_text(x+width/2, y_pos, text=p["id"], font=("Arial", 12, "bold"), fill="#064E3B") |
| 147 | + |
| 148 | + # Draw Time Ticks |
| 149 | + cv.create_text(x, y_pos+45, text=str(sum(pr["burst"] for pr in procs[:index])), font=("Arial", 10), fill="#6B7280") |
| 150 | + if index == len(procs) - 1: |
| 151 | + cv.create_text(x+width, y_pos+45, text=str(sum(pr["burst"] for pr in procs)), font=("Arial", 10), fill="#6B7280") |
| 152 | + |
| 153 | + live_waits.append(wait_times[index]) |
| 154 | + update_graph(live_waits) |
| 155 | + |
| 156 | + result_box.insert(tk.END, f"{algo} -> Executed {p['id']} (Wait: {wait_times[index]})\n") |
| 157 | + |
| 158 | + x += width |
| 159 | + index += 1 |
| 160 | + parent_frame.after(1000, step) |
| 161 | + else: |
| 162 | + avg_wait = sum(wait_times)/len(wait_times) |
| 163 | + avg_ta = sum(wait_times[i] + procs[i]["burst"] for i in range(len(procs))) / len(procs) |
| 164 | + result_box.insert(tk.END, f"\nAvg Waiting Time: {avg_wait:.2f}\n") |
| 165 | + result_box.insert(tk.END, f"Avg Turnaround: {avg_ta:.2f}\n") |
| 166 | + |
| 167 | + step() |
| 168 | + |
| 169 | + animate_creation() |
| 170 | + |
| 171 | + def simulate_starvation(): |
| 172 | + cv.delete("all") |
| 173 | + queue_canvas.delete("all") |
| 174 | + result_box.delete("1.0", tk.END) |
| 175 | + result_box.insert(tk.END, "Low priority process P5 waiting...\n") |
| 176 | + |
| 177 | + draw_queue([{"id": "P5", "burst": 2, "priority": 10}]) |
| 178 | + |
| 179 | + def sim_starve(): |
| 180 | + result_box.insert(tk.END, "-> High priority P1 arrived and executed\n") |
| 181 | + result_box.insert(tk.END, "-> High priority P2 arrived and executed\n") |
| 182 | + result_box.insert(tk.END, "-> High priority P3 arrived and executed\n") |
| 183 | + result_box.insert(tk.END, "\n⚠ Starvation occurred:\n P5 never executed due to continuous high-priority arrivals.\n") |
| 184 | + result_box.see(tk.END) |
| 185 | + |
| 186 | + parent_frame.after(1500, sim_starve) |
| 187 | + |
| 188 | + |
| 189 | + btn_frame = ctk.CTkFrame(card, fg_color="transparent") |
| 190 | + btn_frame.grid(row=3, column=0, sticky="ew", padx=20, pady=10) |
| 191 | + |
| 192 | + ctk.CTkButton(btn_frame, text="FCFS", fg_color="#10B981", hover_color="#059669", command=lambda: simulate("FCFS"), width=100).pack(side="left", padx=5) |
| 193 | + ctk.CTkButton(btn_frame, text="SJF", fg_color="#F59E0B", hover_color="#D97706", command=lambda: simulate("SJF"), width=100).pack(side="left", padx=5) |
| 194 | + ctk.CTkButton(btn_frame, text="Priority", fg_color="#8B5CF6", hover_color="#7C3AED", command=lambda: simulate("Priority"), width=100).pack(side="left", padx=5) |
| 195 | + ctk.CTkButton(btn_frame, text="Starvation Demo", fg_color="#EF4444", hover_color="#DC2626", command=simulate_starvation, width=150).pack(side="left", padx=5) |
0 commit comments