1+ import copy
2+ import threading
3+ import time
14import tkinter as tk
2- from tkinter import ttk , messagebox
5+ from tkinter import messagebox , ttk
6+
37import matplotlib .pyplot as plt
48from matplotlib .backends .backend_tkagg import FigureCanvasTkAgg
5- import threading , time , copy
9+
610
711# ===================== Scheduler Engine ===================== #
812class SchedulerEngine :
9- def __init__ (self , processes , algorithm , quantum = 2 ):
13+ def __init__ (self , processes , algorithm , quantum : int = 2 ):
1014 self .original = copy .deepcopy (processes )
1115 self .processes = [p .copy () for p in processes ]
1216 self .algorithm = algorithm
1317 self .quantum = quantum
1418 for p in self .processes :
1519 p ["remaining" ] = p ["burst" ]
16- self .timeline = [] # [(time, pid)]
17- self .stats = []
20+ self .timeline : list [ tuple [ int , str ]] = [] # [(time, pid)]
21+ self .stats : list [ tuple ] = []
1822
1923 def simulate (self ):
2024 algo = self .algorithm .lower ()
@@ -32,26 +36,30 @@ def simulate(self):
3236 yield from self ._simulate_rr ()
3337 self ._calculate_stats ()
3438
35- #first come first serve
39+ # first come first serve
3640 def _simulate_fcfs (self ):
3741 t = 0
3842 processes = sorted (self .processes , key = lambda p : p ["arrival" ])
3943 for p in processes :
40- if t < p ["arrival" ]:
41- t = p ["arrival" ]
44+ t = max (t , p ["arrival" ])
4245 for _ in range (p ["burst" ]):
4346 self .timeline .append ((t , p ["pid" ]))
4447 yield (t , p ["pid" ], [])
4548 t += 1
4649 p ["completion" ] = t
47-
48- #shortest job first non preemptive
50+
51+ # shortest job first non preemptive
4952 def _simulate_sjf_np (self ):
5053 t = 0
5154 processes = sorted (self .processes , key = lambda p : p ["arrival" ])
5255 done = 0
5356 while done < len (processes ):
54- ready = [p for p in processes if p ["arrival" ] <= t and "completion" not in p ]
57+ ready = [
58+ p
59+ for p in processes
60+ if p ["arrival" ] <= t and "completion" not in p
61+ ]
62+
5563 if not ready :
5664 t += 1
5765 yield (t , None , [])
@@ -63,14 +71,18 @@ def _simulate_sjf_np(self):
6371 t += 1
6472 p ["completion" ] = t
6573 done += 1
66-
74+
6775 # shortest job first preemptive
6876 def _simulate_sjf_p (self ):
6977 t = 0
7078 processes = sorted (self .processes , key = lambda p : p ["arrival" ])
7179 done = 0
7280 while done < len (processes ):
73- ready = [p for p in processes if p ["arrival" ] <= t and p ["remaining" ] > 0 ]
81+ ready = [
82+ p
83+ for p in processes
84+ if p ["arrival" ] <= t and p ["remaining" ] > 0
85+ ]
7486 if not ready :
7587 t += 1
7688 yield (t , None , [])
@@ -83,13 +95,18 @@ def _simulate_sjf_p(self):
8395 p ["completion" ] = t + 1
8496 done += 1
8597 t += 1
86-
87- # priority non preemptive
98+
99+ # priority non preemptive
88100 def _simulate_priority_np (self ):
89101 t = 0
90102 done = 0
91103 while done < len (self .processes ):
92- ready = [p for p in self .processes if p ["arrival" ] <= t and "completion" not in p ]
104+ ready = [
105+ p
106+ for p in self .processes
107+ if p ["arrival" ] <= t and "completion" not in p
108+ ]
109+
93110 if not ready :
94111 t += 1
95112 yield (t , None , [])
@@ -102,12 +119,17 @@ def _simulate_priority_np(self):
102119 p ["completion" ] = t
103120 done += 1
104121
105- # priority preemptive
122+ # priority preemptive
106123 def _simulate_priority_p (self ):
107124 t = 0
108125 done = 0
109126 while done < len (self .processes ):
110- ready = [p for p in self .processes if p ["arrival" ] <= t and p ["remaining" ] > 0 ]
127+ ready = [
128+ p
129+ for p in self .processes
130+ if p ["arrival" ] <= t and p ["remaining" ] > 0
131+ ]
132+
111133 if not ready :
112134 t += 1
113135 yield (t , None , [])
@@ -124,7 +146,7 @@ def _simulate_priority_p(self):
124146 # round robin
125147 def _simulate_rr (self ):
126148 t = 0
127- q = []
149+ q : list [ dict ] = []
128150 processes = sorted (self .processes , key = lambda p : p ["arrival" ])
129151 i = 0
130152 done = 0
@@ -152,34 +174,41 @@ def _simulate_rr(self):
152174 p ["completion" ] = t
153175 done += 1
154176
155-
156177 def _calculate_stats (self ):
157178 for p in self .processes :
158179 pid = p ["pid" ]
159180 arrival = p ["arrival" ]
160181 burst = p ["burst" ]
161182 completion = p ["completion" ]
162- first_exec = next ((t for t , pid2 in self .timeline if pid2 == pid ), arrival )
183+ first_exec = next (
184+ (t for t , pid2 in self .timeline if pid2 == pid ), arrival
185+ )
163186 tat = completion - arrival
164187 wt = tat - burst
165188 rt = first_exec - arrival
166189 self .stats .append ((pid , arrival , burst , completion , tat , wt , rt ))
167190
168- # Interface
191+
192+ # Interface
169193class CPUSchedulerGUI :
170194 def __init__ (self , root ):
171195 self .root = root
172196 self .root .title ("CPU Scheduling Visualizer" )
173197 self .root .geometry ("1000x700" )
174198
175- self .processes = []
199+ self .processes : list [ dict ] = []
176200 self .setup_ui ()
177201
178202 def setup_ui (self ):
179203 top_frame = ttk .Frame (self .root )
180204 top_frame .pack (pady = 10 )
181205
182- self .tree = ttk .Treeview (top_frame , columns = ("pid" , "arrival" , "burst" , "priority" ), show = "headings" )
206+ self .tree = ttk .Treeview (
207+ top_frame ,
208+ columns = ("pid" , "arrival" , "burst" , "priority" ),
209+ show = "headings" ,
210+ )
211+
183212 for col in self .tree ["columns" ]:
184213 self .tree .heading (col , text = col .capitalize ())
185214 self .tree .pack (side = "left" )
@@ -198,23 +227,37 @@ def setup_ui(self):
198227 self .arrival_e .grid (row = 1 , column = 1 )
199228 self .burst_e .grid (row = 2 , column = 1 )
200229 self .priority_e .grid (row = 3 , column = 1 )
201- ttk .Button (form , text = "Add" , command = self .add_process ).grid (row = 4 , column = 0 , pady = 5 )
202- ttk .Button (form , text = "Delete" , command = self .delete_process ).grid (row = 4 , column = 1 )
230+ ttk .Button (form , text = "Add" , command = self .add_process ).grid (
231+ row = 4 , column = 0 , pady = 5
232+ )
233+
234+ ttk .Button (form , text = "Delete" , command = self .delete_process ).grid (
235+ row = 4 , column = 1
236+ )
203237
204238 algo_frame = ttk .Frame (self .root )
205239 algo_frame .pack (pady = 10 )
206240 ttk .Label (algo_frame , text = "Algorithm:" ).pack (side = "left" )
207- self .algo_cb = ttk .Combobox (algo_frame , values = [
208- "FCFS" , "SJF (Non-Preemptive)" , "SJF (Preemptive)" ,
209- "Priority (Non-Preemptive)" , "Priority (Preemptive)" , "Round Robin"
210- ])
241+ self .algo_cb = ttk .Combobox (
242+ algo_frame ,
243+ values = [
244+ "FCFS" ,
245+ "SJF (Non-Preemptive)" ,
246+ "SJF (Preemptive)" ,
247+ "Priority (Non-Preemptive)" ,
248+ "Priority (Preemptive)" ,
249+ "Round Robin" ,
250+ ],
251+ )
211252 self .algo_cb .current (0 )
212253 self .algo_cb .pack (side = "left" , padx = 5 )
213254 ttk .Label (algo_frame , text = "Quantum:" ).pack (side = "left" )
214255 self .quantum_e = ttk .Entry (algo_frame , width = 5 )
215256 self .quantum_e .insert (0 , "2" )
216257 self .quantum_e .pack (side = "left" )
217- ttk .Button (algo_frame , text = "Run" , command = self .run_scheduling ).pack (side = "left" , padx = 10 )
258+ ttk .Button (algo_frame , text = "Run" , command = self .run_scheduling ).pack (
259+ side = "left" , padx = 10
260+ )
218261
219262 self .ready_label = ttk .Label (self .root , text = "Ready Queue:" )
220263 self .ready_list = tk .Listbox (self .root , height = 3 )
@@ -225,9 +268,13 @@ def setup_ui(self):
225268 self .canvas = FigureCanvasTkAgg (self .figure , master = self .root )
226269 self .canvas .get_tk_widget ().pack ()
227270
228- self .result_box = ttk .Treeview (self .root ,
229- columns = ("pid" , "arrival" , "burst" , "completion" , "tat" , "wt" , "rt" ),
230- show = "headings" , height = 6 )
271+ self .result_box = ttk .Treeview (
272+ self .root ,
273+ columns = ("pid" , "arrival" , "burst" , "completion" , "tat" , "wt" , "rt" ),
274+ show = "headings" ,
275+ height = 6 ,
276+ )
277+
231278 for col in self .result_box ["columns" ]:
232279 self .result_box .heading (col , text = col .upper ())
233280 self .result_box .pack (pady = 10 )
@@ -241,7 +288,14 @@ def add_process(self):
241288 arrival = int (self .arrival_e .get ())
242289 burst = int (self .burst_e .get ())
243290 priority = int (self .priority_e .get () or 0 )
244- self .processes .append ({"pid" : pid , "arrival" : arrival , "burst" : burst , "priority" : priority })
291+ self .processes .append (
292+ {
293+ "pid" : pid ,
294+ "arrival" : arrival ,
295+ "burst" : burst ,
296+ "priority" : priority ,
297+ }
298+ )
245299 self .tree .insert ("" , "end" , values = (pid , arrival , burst , priority ))
246300 except ValueError :
247301 messagebox .showerror ("Error" , "Invalid input" )
@@ -264,22 +318,31 @@ def run_scheduling(self):
264318 self .ready_list .pack_forget ()
265319
266320 self .engine = SchedulerEngine (self .processes , algo , quantum )
267- threading .Thread (target = self .animate ).start ()
321+ threading .Thread (target = self .animate , daemon = True ).start ()
268322
269323 def animate (self ):
270324 self .ax .clear ()
271325 x , colors , current_pid = 0 , {}, None
272326 for step in self .engine .simulate ():
273- t , pid , rq = step
327+ _ , pid , rq = step
274328 if pid :
275329 if pid != current_pid :
276330 self .ax .axvline (x , color = "black" , linewidth = 0.8 )
277331 current_pid = pid
278332 colors .setdefault (pid , plt .cm .tab20 (len (colors ) % 20 ))
279333 self .ax .barh (0 , 1 , left = x , color = colors [pid ])
280- self .ax .text (x + 0.5 , 0 , pid , ha = "center" , va = "center" , color = "white" , fontsize = 9 )
334+ self .ax .text (
335+ x + 0.5 ,
336+ 0 ,
337+ pid ,
338+ ha = "center" ,
339+ va = "center" ,
340+ color = "white" ,
341+ fontsize = 9 ,
342+ )
343+
281344 x += 1
282- self .ax .set_xticks (range (0 , x + 1 ))
345+ self .ax .set_xticks (range (x + 1 ))
283346 self .ax .set_yticks ([])
284347 self .ax .set_xlabel ("Time" )
285348 self .canvas .draw ()
@@ -299,12 +362,18 @@ def show_results(self):
299362 total_wt += row [5 ]
300363 total_tat += row [4 ]
301364 total_rt += row [6 ]
302- n = len (self .engine .stats )
303- self .avg_label .config (text = f"AVG WT = { total_wt / n :.2f} | AVG TAT = { total_tat / n :.2f} | AVG RT = { total_rt / n :.2f} " )
365+ n = len (self .engine .stats ) or 1
366+ self .avg_label .config (
367+ text = (
368+ f"AVG WT = { total_wt / n :.2f} | "
369+ f"AVG TAT = { total_tat / n :.2f} | "
370+ f"AVG RT = { total_rt / n :.2f} "
371+ )
372+ )
373+
304374
305375# main call
306376if __name__ == "__main__" :
307377 root = tk .Tk ()
308378 CPUSchedulerGUI (root )
309379 root .mainloop ()
310-
0 commit comments