abhi-bit-2/Genetic_Algorithm
0
1import tkinter as tk2from tkinter import ttk, messagebox, scrolledtext, filedialog3import sys4import io5import threading6import sqlite37import os8import datetime9import pandas as pd10from openpyxl import Workbook11from openpyxl.styles import Font, Alignment, PatternFill, Border, Side12from openpyxl.utils import get_column_letter13from db_operations import initialize_database, fetch_data_from_db, get_course_classes, set_course_classes14from db_operations import add_professor, add_course, add_branch, add_branch_teacher_course_mapping15from db_operations import delete_course, delete_course_teacher_mapping16from db_operations import get_all_professors, get_professor_courses, get_available_courses_for_professor17from db_operations import add_course_to_professor, remove_course_from_professor18from simplified_genetic_algorithm import genetic_algorithm, print_timetable, print_professor_schedules19from main import debug_database, verify_database_integrity20 21# Color scheme22COLORS = {23 "primary": "#3498db", # Blue24 "secondary": "#2ecc71", # Green25 "accent": "#e74c3c", # Red26 "background": "#f5f5f5", # Light gray27 "text": "#2c3e50", # Dark blue/gray28 "light_text": "#7f8c8d", # Light gray text29 "success": "#27ae60", # Dark green30 "warning": "#f39c12", # Orange31 "error": "#c0392b" # Dark red32}33 34# Redirect stdout to capture print statements35class StdoutRedirector:36 def __init__(self, text_widget):37 self.text_widget = text_widget38 self.buffer = io.StringIO()39 40 def write(self, string):41 self.buffer.write(string)42 self.text_widget.insert(tk.END, string)43 self.text_widget.see(tk.END) # Auto-scroll to the end44 45 def flush(self):46 pass47 48class ModernTimetableApp:49 def __init__(self, root):50 self.root = root51 self.root.title("Fast Timetable Generator")52 self.root.geometry("1200x800")53 self.root.minsize(1000, 700)54 55 # Configure the style56 self.configure_style()57 58 # Store the last generated timetable and professor schedules59 self.last_timetable = None60 self.last_professor_schedules = None61 62 # Create main frame63 self.main_frame = ttk.Frame(root)64 self.main_frame.pack(fill=tk.BOTH, expand=True)65 66 # Create sidebar and content area67 self.create_sidebar()68 self.create_content_area()69 70 # Initialize the database71 self.initialize_db()72 73 # Show the home screen by default74 self.show_home()75 76 def configure_style(self):77 """Configure the ttk styles for a modern look"""78 style = ttk.Style()79 style.theme_use('clam') # Use the 'clam' theme as a base80 81 # Configure colors82 style.configure('TFrame', background=COLORS["background"])83 style.configure('TLabel', background=COLORS["background"], foreground=COLORS["text"])84 style.configure('TButton', background=COLORS["primary"], foreground="white", font=('Arial', 10))85 style.map('TButton', background=[('active', COLORS["secondary"])])86 87 # Sidebar button style88 style.configure('Sidebar.TButton', font=('Arial', 11), padding=10)89 90 # Heading style91 style.configure('Heading.TLabel', font=('Arial', 16, 'bold'), foreground=COLORS["primary"])92 style.configure('SubHeading.TLabel', font=('Arial', 12, 'italic'), foreground=COLORS["light_text"])93 94 # Card style95 style.configure('Card.TFrame', background="white", relief="raised", borderwidth=1)96 97 # Success and error styles98 style.configure('Success.TLabel', foreground=COLORS["success"])99 style.configure('Error.TLabel', foreground=COLORS["error"])100 101 def create_sidebar(self):102 """Create the sidebar with navigation buttons"""103 self.sidebar = ttk.Frame(self.main_frame, width=200, style='TFrame')104 self.sidebar.pack(side=tk.LEFT, fill=tk.Y, padx=0, pady=0)105 106 # Make the sidebar fixed width107 self.sidebar.pack_propagate(False)108 109 # App title110 title_frame = ttk.Frame(self.sidebar, style='TFrame')111 title_frame.pack(fill=tk.X, padx=10, pady=20)112 113 ttk.Label(title_frame, text="Timetable", font=('Arial', 18, 'bold'), 114 foreground=COLORS["primary"], style='TLabel').pack(anchor=tk.W)115 ttk.Label(title_frame, text="Generator", font=('Arial', 14), 116 foreground=COLORS["secondary"], style='TLabel').pack(anchor=tk.W)117 118 # Navigation buttons119 self.nav_buttons = []120 121 nav_items = [122 ("Home", self.show_home),123 ("Database", self.show_database),124 ("Courses", self.show_courses),125 ("Classes", self.show_classes),126 ("Professor Courses", self.show_professor_courses),127 ("Generate", self.show_generate),128 ("View Timetable", self.show_timetable),129 ("Professor Schedules", self.show_professors)130 ]131 132 for text, command in nav_items:133 btn = ttk.Button(self.sidebar, text=text, command=command, style='Sidebar.TButton')134 btn.pack(fill=tk.X, padx=10, pady=5)135 self.nav_buttons.append(btn)136 137 # Status section at bottom of sidebar138 status_frame = ttk.Frame(self.sidebar, style='TFrame')139 status_frame.pack(side=tk.BOTTOM, fill=tk.X, padx=10, pady=20)140 141 ttk.Label(status_frame, text="Status:", style='TLabel').pack(anchor=tk.W)142 self.status_var = tk.StringVar(value="Ready")143 ttk.Label(status_frame, textvariable=self.status_var, style='TLabel').pack(anchor=tk.W, pady=5)144 145 def create_content_area(self):146 """Create the main content area"""147 self.content = ttk.Frame(self.main_frame, style='TFrame')148 self.content.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=20, pady=20)149 150 # Create frames for each section (only one will be visible at a time)151 self.home_frame = ttk.Frame(self.content, style='TFrame')152 self.database_frame = ttk.Frame(self.content, style='TFrame')153 self.courses_frame = ttk.Frame(self.content, style='TFrame')154 self.classes_frame = ttk.Frame(self.content, style='TFrame') # New frame for classes management155 self.professor_courses_frame = ttk.Frame(self.content, style='TFrame') # New frame for professor courses156 self.generate_frame = ttk.Frame(self.content, style='TFrame')157 self.timetable_frame = ttk.Frame(self.content, style='TFrame')158 self.professors_frame = ttk.Frame(self.content, style='TFrame')159 160 # Set up each frame161 self.setup_home_frame()162 self.setup_database_frame()163 self.setup_courses_frame()164 self.setup_classes_frame() # Setup the new classes frame165 self.setup_professor_courses_frame() # Setup the professor courses frame166 self.setup_generate_frame()167 self.setup_timetable_frame()168 self.setup_professors_frame()169 170 def hide_all_frames(self):171 """Hide all content frames"""172 for frame in [self.home_frame, self.database_frame, self.courses_frame, self.classes_frame,173 self.professor_courses_frame, self.generate_frame, self.timetable_frame, self.professors_frame]:174 frame.pack_forget()175 176 def show_home(self):177 self.hide_all_frames()178 self.home_frame.pack(fill=tk.BOTH, expand=True)179 180 def show_database(self):181 self.hide_all_frames()182 self.database_frame.pack(fill=tk.BOTH, expand=True)183 184 def show_courses(self):185 self.hide_all_frames()186 self.courses_frame.pack(fill=tk.BOTH, expand=True)187 self.refresh_courses()188 self.refresh_branches()189 self.refresh_mapping_lists()190 191 def show_classes(self):192 self.hide_all_frames()193 self.classes_frame.pack(fill=tk.BOTH, expand=True)194 self.refresh_courses_for_classes()195 196 def show_professor_courses(self):197 self.hide_all_frames()198 self.professor_courses_frame.pack(fill=tk.BOTH, expand=True)199 self.refresh_professors_list()200 201 def show_generate(self):202 self.hide_all_frames()203 self.generate_frame.pack(fill=tk.BOTH, expand=True)204 205 def show_timetable(self):206 self.hide_all_frames()207 self.timetable_frame.pack(fill=tk.BOTH, expand=True)208 209 def show_professors(self):210 self.hide_all_frames()211 self.professors_frame.pack(fill=tk.BOTH, expand=True)212 self.refresh_professors()213 214 def setup_home_frame(self):215 """Set up the home screen with a modern, clean design"""216 # Title with improved styling217 title_frame = ttk.Frame(self.home_frame, style='TFrame')218 title_frame.pack(fill=tk.X, pady=(0, 20))219 220 ttk.Label(title_frame, text="Timetable Generator",221 font=('Arial', 24, 'bold'), foreground=COLORS["primary"],222 style='TLabel').pack(pady=(0, 5))223 ttk.Label(title_frame, text="Create optimal timetables using genetic algorithms",224 style='SubHeading.TLabel').pack(pady=(0, 10))225 226 # Main content in a two-column layout227 content_frame = ttk.Frame(self.home_frame, style='TFrame')228 content_frame.pack(fill=tk.BOTH, expand=True, pady=10)229 content_frame.columnconfigure(0, weight=1)230 content_frame.columnconfigure(1, weight=1)231 232 # Left column: Stats and actions233 left_col = ttk.Frame(content_frame, style='TFrame')234 left_col.grid(row=0, column=0, padx=10, pady=10, sticky=tk.N+tk.S+tk.W+tk.E)235 236 # Stats cards237 stats_frame = ttk.LabelFrame(left_col, text="Database Statistics", style='TFrame')238 stats_frame.pack(fill=tk.X, pady=10)239 240 stats_grid = ttk.Frame(stats_frame, style='TFrame')241 stats_grid.pack(fill=tk.X, padx=10, pady=10)242 stats_grid.columnconfigure(0, weight=1)243 stats_grid.columnconfigure(1, weight=1)244 stats_grid.columnconfigure(2, weight=1)245 246 # Create stat cards in a grid247 self.create_stat_card(stats_grid, "Branches", "branch_count", 0, 0)248 self.create_stat_card(stats_grid, "Courses", "course_count", 0, 1)249 self.create_stat_card(stats_grid, "Teachers", "teacher_count", 0, 2)250 251 # Quick actions252 actions_frame = ttk.LabelFrame(left_col, text="Quick Actions", style='TFrame')253 actions_frame.pack(fill=tk.X, pady=10)254 255 # Create a grid for buttons with 2 columns256 button_grid = ttk.Frame(actions_frame, style='TFrame')257 button_grid.pack(fill=tk.X, padx=10, pady=10)258 button_grid.columnconfigure(0, weight=1)259 button_grid.columnconfigure(1, weight=1)260 261 # Add buttons with icons (using text for now)262 ttk.Button(button_grid, text="Initialize Database",263 command=self.initialize_db).grid(row=0, column=0, padx=5, pady=5, sticky=tk.W+tk.E)264 ttk.Button(button_grid, text="Generate Timetable",265 command=self.generate_timetable).grid(row=0, column=1, padx=5, pady=5, sticky=tk.W+tk.E)266 ttk.Button(button_grid, text="View Timetable",267 command=self.show_timetable).grid(row=1, column=0, padx=5, pady=5, sticky=tk.W+tk.E)268 ttk.Button(button_grid, text="View Professor Schedules",269 command=self.show_professors).grid(row=1, column=1, padx=5, pady=5, sticky=tk.W+tk.E)270 271 # Right column: Console output272 right_col = ttk.Frame(content_frame, style='TFrame')273 right_col.grid(row=0, column=1, padx=10, pady=10, sticky=tk.N+tk.S+tk.W+tk.E)274 275 console_frame = ttk.LabelFrame(right_col, text="Console Output", style='TFrame')276 console_frame.pack(fill=tk.BOTH, expand=True)277 278 self.console = scrolledtext.ScrolledText(console_frame, wrap=tk.WORD, height=15,279 font=('Consolas', 9))280 self.console.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)281 282 # Add a welcome message to the console283 welcome_msg = (284 "Welcome to Timetable Generator!\n\n"285 "This application helps you create optimal timetables using genetic algorithms.\n\n"286 "To get started:\n"287 "1. Initialize the database\n"288 "2. Add branches, professors, and courses\n"289 "3. Generate a timetable\n\n"290 "System ready and waiting for commands...\n"291 )292 self.console.insert(tk.END, welcome_msg)293 294 # Redirect stdout to the console295 sys.stdout = StdoutRedirector(self.console)296 297 def create_stat_card(self, parent, title, var_name, row, col):298 """Create a statistics card"""299 card = ttk.Frame(parent, style='Card.TFrame')300 card.grid(row=row, column=col, padx=10, pady=10, sticky=tk.W+tk.E)301 302 ttk.Label(card, text=title, font=('Arial', 12), background="white").pack(pady=(10, 5))303 304 # Create a variable to hold the count305 setattr(self, var_name, tk.StringVar(value="0"))306 ttk.Label(card, textvariable=getattr(self, var_name), 307 font=('Arial', 24, 'bold'), foreground=COLORS["primary"], 308 background="white").pack(pady=(0, 10))309 310 def setup_database_frame(self):311 """Set up the database management frame"""312 # Title313 ttk.Label(self.database_frame, text="Database Management", 314 style='Heading.TLabel').pack(pady=(0, 20))315 316 # Create a notebook for tabs317 notebook = ttk.Notebook(self.database_frame)318 notebook.pack(fill=tk.BOTH, expand=True)319 320 # Create tabs321 actions_tab = ttk.Frame(notebook, style='TFrame')322 branch_tab = ttk.Frame(notebook, style='TFrame')323 professor_tab = ttk.Frame(notebook, style='TFrame')324 325 notebook.add(actions_tab, text="Database Actions")326 notebook.add(branch_tab, text="Add Branch")327 notebook.add(professor_tab, text="Add Professor")328 329 # Database actions tab330 actions_frame = ttk.Frame(actions_tab, style='TFrame')331 actions_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)332 333 ttk.Button(actions_frame, text="Initialize Database", 334 command=self.initialize_db).pack(fill=tk.X, pady=10)335 ttk.Button(actions_frame, text="Debug Database", 336 command=self.debug_database).pack(fill=tk.X, pady=10)337 ttk.Button(actions_frame, text="Verify Database Integrity", 338 command=self.verify_database).pack(fill=tk.X, pady=10)339 340 # Console output for actions341 console_frame = ttk.LabelFrame(actions_frame, text="Console Output", style='TFrame')342 console_frame.pack(fill=tk.BOTH, expand=True, pady=20)343 344 self.db_console = scrolledtext.ScrolledText(console_frame, wrap=tk.WORD, height=10)345 self.db_console.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)346 347 # Branch tab348 branch_frame = ttk.Frame(branch_tab, style='TFrame')349 branch_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)350 351 ttk.Label(branch_frame, text="Branch Name:").pack(anchor=tk.W, pady=(0, 5))352 self.branch_name_var = tk.StringVar()353 ttk.Entry(branch_frame, textvariable=self.branch_name_var, width=40).pack(fill=tk.X, pady=(0, 10))354 355 ttk.Button(branch_frame, text="Add Branch", 356 command=self.add_branch).pack(anchor=tk.W, pady=10)357 358 # Professor tab359 prof_frame = ttk.Frame(professor_tab, style='TFrame')360 prof_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)361 362 ttk.Label(prof_frame, text="Professor Name:").pack(anchor=tk.W, pady=(0, 5))363 self.prof_name_var = tk.StringVar()364 ttk.Entry(prof_frame, textvariable=self.prof_name_var, width=40).pack(fill=tk.X, pady=(0, 10))365 366 ttk.Label(prof_frame, text="Branch:").pack(anchor=tk.W, pady=(10, 5))367 self.prof_branch_var = tk.StringVar()368 self.prof_branch_combo = ttk.Combobox(prof_frame, textvariable=self.prof_branch_var, width=40)369 self.prof_branch_combo.pack(fill=tk.X, pady=(0, 10))370 371 button_frame = ttk.Frame(prof_frame, style='TFrame')372 button_frame.pack(fill=tk.X, pady=10)373 374 ttk.Button(button_frame, text="Add Professor", 375 command=self.add_professor).pack(side=tk.LEFT, padx=(0, 10))376 ttk.Button(button_frame, text="Refresh Branches", 377 command=self.refresh_branches).pack(side=tk.LEFT)378 379 def setup_courses_frame(self):380 """Set up the course management frame"""381 # Title382 ttk.Label(self.courses_frame, text="Course Management", 383 style='Heading.TLabel').pack(pady=(0, 20))384 385 # Create a notebook for tabs386 notebook = ttk.Notebook(self.courses_frame)387 notebook.pack(fill=tk.BOTH, expand=True)388 389 # Create tabs390 add_course_tab = ttk.Frame(notebook, style='TFrame')391 mapping_tab = ttk.Frame(notebook, style='TFrame')392 delete_tab = ttk.Frame(notebook, style='TFrame')393 394 notebook.add(add_course_tab, text="Add Course")395 notebook.add(mapping_tab, text="Add Mapping")396 notebook.add(delete_tab, text="Delete Course")397 398 # Add course tab399 course_frame = ttk.Frame(add_course_tab, style='TFrame')400 course_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)401 402 ttk.Label(course_frame, text="Course Code:").pack(anchor=tk.W, pady=(0, 5))403 self.course_code_var = tk.StringVar()404 ttk.Entry(course_frame, textvariable=self.course_code_var, width=40).pack(fill=tk.X, pady=(0, 10))405 406 ttk.Label(course_frame, text="Course Name:").pack(anchor=tk.W, pady=(10, 5))407 self.course_name_var = tk.StringVar()408 ttk.Entry(course_frame, textvariable=self.course_name_var, width=40).pack(fill=tk.X, pady=(0, 10))409 410 ttk.Label(course_frame, text="Branch:").pack(anchor=tk.W, pady=(10, 5))411 self.course_branch_var = tk.StringVar()412 self.course_branch_combo = ttk.Combobox(course_frame, textvariable=self.course_branch_var, width=40)413 self.course_branch_combo.pack(fill=tk.X, pady=(0, 10))414 415 button_frame = ttk.Frame(course_frame, style='TFrame')416 button_frame.pack(fill=tk.X, pady=10)417 418 ttk.Button(button_frame, text="Add Course", 419 command=self.add_course).pack(side=tk.LEFT, padx=(0, 10))420 ttk.Button(button_frame, text="Refresh Branches", 421 command=self.refresh_branches).pack(side=tk.LEFT)422 423 # Mapping tab424 mapping_frame = ttk.Frame(mapping_tab, style='TFrame')425 mapping_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)426 427 ttk.Label(mapping_frame, text="Branch:").pack(anchor=tk.W, pady=(0, 5))428 self.mapping_branch_var = tk.StringVar()429 self.mapping_branch_combo = ttk.Combobox(mapping_frame, textvariable=self.mapping_branch_var, width=40)430 self.mapping_branch_combo.pack(fill=tk.X, pady=(0, 10))431 432 ttk.Label(mapping_frame, text="Teacher:").pack(anchor=tk.W, pady=(10, 5))433 self.mapping_teacher_var = tk.StringVar()434 self.mapping_teacher_combo = ttk.Combobox(mapping_frame, textvariable=self.mapping_teacher_var, width=40)435 self.mapping_teacher_combo.pack(fill=tk.X, pady=(0, 10))436 437 ttk.Label(mapping_frame, text="Course:").pack(anchor=tk.W, pady=(10, 5))438 self.mapping_course_var = tk.StringVar()439 self.mapping_course_combo = ttk.Combobox(mapping_frame, textvariable=self.mapping_course_var, width=40)440 self.mapping_course_combo.pack(fill=tk.X, pady=(0, 10))441 442 button_frame = ttk.Frame(mapping_frame, style='TFrame')443 button_frame.pack(fill=tk.X, pady=10)444 445 ttk.Button(button_frame, text="Add Mapping", 446 command=self.add_mapping).pack(side=tk.LEFT, padx=(0, 10))447 ttk.Button(button_frame, text="Refresh Lists", 448 command=self.refresh_mapping_lists).pack(side=tk.LEFT)449 450 # Delete course tab451 delete_frame = ttk.Frame(delete_tab, style='TFrame')452 delete_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)453 454 ttk.Label(delete_frame, text="Course:").pack(anchor=tk.W, pady=(0, 5))455 self.delete_course_var = tk.StringVar()456 self.delete_course_combo = ttk.Combobox(delete_frame, textvariable=self.delete_course_var, width=40)457 self.delete_course_combo.pack(fill=tk.X, pady=(0, 10))458 459 button_frame = ttk.Frame(delete_frame, style='TFrame')460 button_frame.pack(fill=tk.X, pady=10)461 462 ttk.Button(button_frame, text="Delete Course", 463 command=self.delete_course).pack(side=tk.LEFT, padx=(0, 10))464 ttk.Button(button_frame, text="Refresh Courses", 465 command=self.refresh_courses).pack(side=tk.LEFT)466 467 # Console output for all tabs468 console_frame = ttk.LabelFrame(self.courses_frame, text="Console Output", style='TFrame')469 console_frame.pack(fill=tk.BOTH, expand=True, pady=20)470 471 self.course_console = scrolledtext.ScrolledText(console_frame, wrap=tk.WORD, height=10)472 self.course_console.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)473 474 def setup_generate_frame(self):475 """Set up the timetable generation frame"""476 # Title477 ttk.Label(self.generate_frame, text="Generate Timetable",478 style='Heading.TLabel').pack(pady=(0, 20))479 ttk.Label(self.generate_frame, text="Using genetic algorithm for optimal timetable generation",480 style='SubHeading.TLabel').pack(pady=(0, 10))481 482 # Generation options483 options_frame = ttk.LabelFrame(self.generate_frame, text="Generation Options", style='TFrame')484 options_frame.pack(fill=tk.X, pady=10)485 486 # Create a grid layout with 2 columns487 options_frame.columnconfigure(0, weight=0)488 options_frame.columnconfigure(1, weight=1)489 490 # Generations option491 ttk.Label(options_frame, text="Generations:").grid(row=0, column=0, padx=10, pady=10, sticky=tk.W)492 self.generations_var = tk.StringVar(value="100")493 ttk.Spinbox(options_frame, from_=30, to=300, increment=10,494 textvariable=self.generations_var, width=10).grid(row=0, column=1, padx=10, pady=10, sticky=tk.W)495 496 # Generate button497 generate_btn = ttk.Button(options_frame, text="Generate Timetable",498 command=self.generate_timetable)499 generate_btn.grid(row=1, column=0, columnspan=2, padx=10, pady=10, sticky=tk.W+tk.E)500 501 # Status indicator502 status_frame = ttk.Frame(self.generate_frame, style='TFrame')503 status_frame.pack(fill=tk.X, pady=10)504 505 self.progress_var = tk.StringVar(value="Not started")506 ttk.Label(status_frame, text="Status:").pack(side=tk.LEFT, padx=(0, 10))507 ttk.Label(status_frame, textvariable=self.progress_var,508 font=('Arial', 10, 'bold')).pack(side=tk.LEFT)509 510 # Information panel511 info_frame = ttk.LabelFrame(self.generate_frame, text="Information", style='TFrame')512 info_frame.pack(fill=tk.BOTH, expand=True, pady=10)513 514 info_text = (515 "The timetable generation process will run in the background.\n\n"516 "Once complete, you will be automatically redirected to the timetable view.\n\n"517 "For large datasets, this process may take several minutes.\n"518 "Please be patient while the genetic algorithm optimizes your timetable."519 )520 521 ttk.Label(info_frame, text=info_text, wraplength=500, justify="center").pack(522 fill=tk.BOTH, expand=True, padx=20, pady=20523 )524 525 def setup_timetable_frame(self):526 """Set up the timetable display frame"""527 # Title528 ttk.Label(self.timetable_frame, text="Timetable View",529 style='Heading.TLabel').pack(pady=(0, 20))530 531 # Actions toolbar532 actions_frame = ttk.Frame(self.timetable_frame, style='TFrame')533 actions_frame.pack(fill=tk.X, pady=10)534 535 # Create a more organized button layout536 button_frame = ttk.Frame(actions_frame, style='TFrame')537 button_frame.pack(fill=tk.X)538 539 # Configure columns for even spacing540 button_frame.columnconfigure(0, weight=1)541 button_frame.columnconfigure(1, weight=1)542 button_frame.columnconfigure(2, weight=1)543 544 # Add buttons in a grid layout545 ttk.Button(button_frame, text="Generate New Timetable",546 command=self.show_generate).grid(row=0, column=0, padx=5, pady=5, sticky=tk.W+tk.E)547 ttk.Button(button_frame, text="View Professor Schedules",548 command=self.show_professors).grid(row=0, column=1, padx=5, pady=5, sticky=tk.W+tk.E)549 ttk.Button(button_frame, text="Export to Excel",550 command=self.export_timetable_to_excel).grid(row=0, column=2, padx=5, pady=5, sticky=tk.W+tk.E)551 552 # Timetable display with improved styling553 timetable_frame = ttk.LabelFrame(self.timetable_frame, text="Current Timetable", style='TFrame')554 timetable_frame.pack(fill=tk.BOTH, expand=True, pady=10)555 556 # Use a monospaced font for better timetable display557 self.timetable_text = scrolledtext.ScrolledText(558 timetable_frame,559 wrap=tk.WORD,560 font=('Courier New', 10)561 )562 self.timetable_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)563 564 # Initial message with better formatting565 self.timetable_text.insert(tk.END,566 "No timetable has been generated yet.\n\n"567 "Please go to the Generate tab to create a new timetable."568 )569 570 def setup_classes_frame(self):571 """Set up the course classes management frame"""572 # Title573 ttk.Label(self.classes_frame, text="Course Classes Management",574 style='Heading.TLabel').pack(pady=(0, 20))575 ttk.Label(self.classes_frame, text="Set the number of classes per week for each course",576 style='SubHeading.TLabel').pack(pady=(0, 20))577 578 # Create a notebook for tabs579 notebook = ttk.Notebook(self.classes_frame)580 notebook.pack(fill=tk.BOTH, expand=True)581 582 # Create tabs583 view_tab = ttk.Frame(notebook, style='TFrame')584 update_tab = ttk.Frame(notebook, style='TFrame')585 update_all_tab = ttk.Frame(notebook, style='TFrame')586 587 notebook.add(view_tab, text="View Classes")588 notebook.add(update_tab, text="Update Course")589 notebook.add(update_all_tab, text="Update All Courses")590 591 # View classes tab592 view_frame = ttk.Frame(view_tab, style='TFrame')593 view_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)594 595 ttk.Button(view_frame, text="View Course Classes Configuration",596 command=self.view_course_classes).pack(fill=tk.X, pady=10)597 598 # Update single course tab599 update_frame = ttk.Frame(update_tab, style='TFrame')600 update_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)601 602 ttk.Label(update_frame, text="Course:").pack(anchor=tk.W, pady=(0, 5))603 self.update_course_var = tk.StringVar()604 self.update_course_combo = ttk.Combobox(update_frame, textvariable=self.update_course_var, width=40)605 self.update_course_combo.pack(fill=tk.X, pady=(0, 10))606 607 ttk.Label(update_frame, text="Classes per Week:").pack(anchor=tk.W, pady=(10, 5))608 self.update_classes_var = tk.StringVar(value="2")609 ttk.Spinbox(update_frame, from_=1, to=5, textvariable=self.update_classes_var, width=10).pack(anchor=tk.W, pady=(0, 10))610 611 button_frame = ttk.Frame(update_frame, style='TFrame')612 button_frame.pack(fill=tk.X, pady=10)613 614 ttk.Button(button_frame, text="Update Course",615 command=self.update_course_classes).pack(side=tk.LEFT, padx=(0, 10))616 ttk.Button(button_frame, text="Refresh Courses",617 command=self.refresh_courses_for_classes).pack(side=tk.LEFT)618 619 # Update all courses tab620 update_all_frame = ttk.Frame(update_all_tab, style='TFrame')621 update_all_frame.pack(fill=tk.BOTH, expand=True, padx=20, pady=20)622 623 ttk.Label(update_all_frame, text="Classes per Week for ALL courses:").pack(anchor=tk.W, pady=(0, 5))624 self.update_all_classes_var = tk.StringVar(value="2")625 ttk.Spinbox(update_all_frame, from_=1, to=5, textvariable=self.update_all_classes_var, width=10).pack(anchor=tk.W, pady=(0, 10))626 627 ttk.Button(update_all_frame, text="Update All Courses",628 command=self.update_all_course_classes).pack(anchor=tk.W, pady=10)629 630 # Console output for all tabs631 console_frame = ttk.LabelFrame(self.classes_frame, text="Console Output", style='TFrame')632 console_frame.pack(fill=tk.BOTH, expand=True, pady=20)633 634 self.classes_console = scrolledtext.ScrolledText(console_frame, wrap=tk.WORD, height=10)635 self.classes_console.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)636 637 def setup_professor_courses_frame(self):638 """Set up the professor courses management frame"""639 # Title640 ttk.Label(self.professor_courses_frame, text="Professor Course Management",641 style='Heading.TLabel').pack(pady=(0, 20))642 643 # Create a split view with professors on the left and courses on the right644 main_pane = ttk.PanedWindow(self.professor_courses_frame, orient=tk.HORIZONTAL)645 main_pane.pack(fill=tk.BOTH, expand=True, pady=10)646 647 # Left frame for professor selection648 left_frame = ttk.Frame(main_pane, style='TFrame')649 main_pane.add(left_frame, weight=1)650 651 # Professor selection652 ttk.Label(left_frame, text="Select Professor:", style='SubHeading.TLabel').pack(anchor=tk.W, pady=(0, 10))653 654 # Create a frame for the professor list655 prof_list_frame = ttk.Frame(left_frame, style='TFrame')656 prof_list_frame.pack(fill=tk.BOTH, expand=True, pady=5)657 658 # Create a treeview for the professors659 self.professors_tree = ttk.Treeview(prof_list_frame, columns=("id", "name", "branch"), show="headings")660 self.professors_tree.heading("id", text="ID")661 self.professors_tree.heading("name", text="Professor Name")662 self.professors_tree.heading("branch", text="Branch")663 664 self.professors_tree.column("id", width=50)665 self.professors_tree.column("name", width=200)666 self.professors_tree.column("branch", width=150)667 668 self.professors_tree.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)669 670 # Add a scrollbar671 scrollbar = ttk.Scrollbar(prof_list_frame, orient=tk.VERTICAL, command=self.professors_tree.yview)672 self.professors_tree.configure(yscroll=scrollbar.set)673 scrollbar.pack(side=tk.RIGHT, fill=tk.Y)674 675 # Bind selection event676 self.professors_tree.bind("<<TreeviewSelect>>", self.on_professor_selected)677 678 # Refresh button679 ttk.Button(left_frame, text="Refresh Professors",680 command=self.refresh_professors_list).pack(fill=tk.X, pady=10)681 682 # Right frame for courses683 right_frame = ttk.Frame(main_pane, style='TFrame')684 main_pane.add(right_frame, weight=2)685 686 # Create a notebook for tabs687 self.prof_courses_notebook = ttk.Notebook(right_frame)688 self.prof_courses_notebook.pack(fill=tk.BOTH, expand=True)689 690 # Current courses tab691 current_courses_tab = ttk.Frame(self.prof_courses_notebook, style='TFrame')692 self.prof_courses_notebook.add(current_courses_tab, text="Current Courses")693 694 # Create a treeview for current courses695 self.current_courses_tree = ttk.Treeview(current_courses_tab, columns=("code", "name", "branch"), show="headings")696 self.current_courses_tree.heading("code", text="Course Code")697 self.current_courses_tree.heading("name", text="Course Name")698 self.current_courses_tree.heading("branch", text="Branch")699 700 self.current_courses_tree.column("code", width=100)701 self.current_courses_tree.column("name", width=250)702 self.current_courses_tree.column("branch", width=150)703 704 self.current_courses_tree.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)705 706 # Add a scrollbar707 scrollbar = ttk.Scrollbar(current_courses_tab, orient=tk.VERTICAL, command=self.current_courses_tree.yview)708 self.current_courses_tree.configure(yscroll=scrollbar.set)709 scrollbar.pack(side=tk.RIGHT, fill=tk.Y)710 711 # Remove course button712 ttk.Button(current_courses_tab, text="Remove Selected Course",713 command=self.remove_course_from_selected_professor).pack(fill=tk.X, pady=10)714 715 # Available courses tab716 available_courses_tab = ttk.Frame(self.prof_courses_notebook, style='TFrame')717 self.prof_courses_notebook.add(available_courses_tab, text="Available Courses")718 719 # Create a treeview for available courses720 self.available_courses_tree = ttk.Treeview(available_courses_tab, columns=("code", "name"), show="headings")721 self.available_courses_tree.heading("code", text="Course Code")722 self.available_courses_tree.heading("name", text="Course Name")723 724 self.available_courses_tree.column("code", width=100)725 self.available_courses_tree.column("name", width=300)726 727 self.available_courses_tree.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)728 729 # Add a scrollbar730 scrollbar = ttk.Scrollbar(available_courses_tab, orient=tk.VERTICAL, command=self.available_courses_tree.yview)731 self.available_courses_tree.configure(yscroll=scrollbar.set)732 scrollbar.pack(side=tk.RIGHT, fill=tk.Y)733 734 # Add course button735 ttk.Button(available_courses_tab, text="Add Selected Course",736 command=self.add_course_to_selected_professor).pack(fill=tk.X, pady=10)737 738 def setup_professors_frame(self):739 """Set up the professor schedules frame"""740 # Title741 ttk.Label(self.professors_frame, text="Professor Schedules",742 style='Heading.TLabel').pack(pady=(0, 20))743 744 # Professor selection with improved layout745 select_frame = ttk.Frame(self.professors_frame, style='TFrame')746 select_frame.pack(fill=tk.X, pady=10)747 748 # Create a grid layout for better organization749 select_frame.columnconfigure(0, weight=0) # Label750 select_frame.columnconfigure(1, weight=1) # Combobox751 select_frame.columnconfigure(2, weight=0) # Buttons752 753 # Row 1: Professor selection754 ttk.Label(select_frame, text="Select Professor:").grid(row=0, column=0, padx=5, pady=5, sticky=tk.W)755 self.professor_var = tk.StringVar()756 self.professor_combo = ttk.Combobox(select_frame, textvariable=self.professor_var, width=30)757 self.professor_combo.grid(row=0, column=1, padx=5, pady=5, sticky=tk.W+tk.E)758 759 # Button frame for row 1760 button_frame = ttk.Frame(select_frame, style='TFrame')761 button_frame.grid(row=0, column=2, padx=5, pady=5, sticky=tk.E)762 763 ttk.Button(button_frame, text="View Schedule",764 command=self.view_professor_schedule).pack(side=tk.LEFT, padx=2)765 ttk.Button(button_frame, text="Refresh",766 command=self.refresh_professors).pack(side=tk.LEFT, padx=2)767 768 # Row 2: Export button769 export_frame = ttk.Frame(self.professors_frame, style='TFrame')770 export_frame.pack(fill=tk.X, pady=(0, 10))771 772 ttk.Button(export_frame, text="Export All Schedules to Excel",773 command=self.export_professor_schedule_to_excel).pack(anchor=tk.E, padx=5)774 775 # Schedule display with improved styling776 schedule_frame = ttk.LabelFrame(self.professors_frame, text="Professor Schedule", style='TFrame')777 schedule_frame.pack(fill=tk.BOTH, expand=True, pady=10)778 779 # Use a monospaced font for better schedule display780 self.professor_text = scrolledtext.ScrolledText(781 schedule_frame,782 wrap=tk.WORD,783 font=('Courier New', 10)784 )785 self.professor_text.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)786 787 # Initial message with better formatting788 self.professor_text.insert(tk.END,789 "No professor schedules available. Please generate a timetable first using the Generate tab.\n\n"790 "Once a timetable is generated, you can view the schedule for a selected professor."791 )792 793 # Database functions794 def initialize_db(self):795 """Initialize the database with sample data"""796 try:797 initialize_database()798 self.console.insert(tk.END, "Database initialized successfully!\n")799 self.status_var.set("Database initialized")800 self.refresh_branches()801 self.refresh_courses()802 self.refresh_mapping_lists()803 self.refresh_courses_for_classes()804 self.update_stats()805 except Exception as e:806 messagebox.showerror("Error", f"Failed to initialize database: {str(e)}")807 self.console.insert(tk.END, f"Error initializing database: {str(e)}\n")808 self.status_var.set("Database initialization failed")809 810 def update_stats(self):811 """Update the statistics on the home screen"""812 try:813 with sqlite3.connect("timetable.db") as conn:814 cursor = conn.cursor()815 816 # Count branches817 cursor.execute("SELECT COUNT(*) FROM branches")818 branch_count = cursor.fetchone()[0]819 self.branch_count.set(str(branch_count))820 821 # Count courses822 cursor.execute("SELECT COUNT(*) FROM courses")823 course_count = cursor.fetchone()[0]824 self.course_count.set(str(course_count))825 826 # Count teachers827 cursor.execute("SELECT COUNT(*) FROM teachers")828 teacher_count = cursor.fetchone()[0]829 self.teacher_count.set(str(teacher_count))830 except Exception as e:831 print(f"Error updating stats: {str(e)}")832 833 def debug_database(self):834 """Debug the database contents"""835 # Redirect stdout to the database console836 old_stdout = sys.stdout837 sys.stdout = StdoutRedirector(self.db_console)838 839 try:840 # Clear the console first841 self.db_console.delete(1.0, tk.END)842 843 debug_database()844 self.status_var.set("Database debug completed")845 except Exception as e:846 messagebox.showerror("Error", f"Failed to debug database: {str(e)}")847 self.db_console.insert(tk.END, f"Error debugging database: {str(e)}\n")848 self.status_var.set("Database debug failed")849 finally:850 # Restore stdout851 sys.stdout = old_stdout852 853 def verify_database(self):854 """Verify the database integrity"""855 # Redirect stdout to the database console856 old_stdout = sys.stdout857 sys.stdout = StdoutRedirector(self.db_console)858 859 try:860 # Clear the console first861 self.db_console.delete(1.0, tk.END)862 863 result = verify_database_integrity()864 if result:865 self.status_var.set("Database verification passed")866 else:867 self.status_var.set("Database verification failed")868 except Exception as e:869 messagebox.showerror("Error", f"Failed to verify database: {str(e)}")870 self.db_console.insert(tk.END, f"Error verifying database: {str(e)}\n")871 self.status_var.set("Database verification failed")872 finally:873 # Restore stdout874 sys.stdout = old_stdout875 876 def refresh_branches(self):877 """Refresh the branch comboboxes"""878 try:879 with sqlite3.connect("timetable.db") as conn:880 cursor = conn.cursor()881 cursor.execute("SELECT branch_name FROM branches ORDER BY branch_name")882 branches = [branch[0] for branch in cursor.fetchall()]883 884 # Update all branch comboboxes885 self.prof_branch_combo['values'] = branches886 self.course_branch_combo['values'] = branches887 self.mapping_branch_combo['values'] = branches888 889 if branches:890 self.prof_branch_combo.current(0)891 self.course_branch_combo.current(0)892 self.mapping_branch_combo.current(0)893 except Exception as e:894 messagebox.showerror("Error", f"Failed to refresh branches: {str(e)}")895 896 def add_branch(self):897 """Add a new branch to the database"""898 branch_name = self.branch_name_var.get().strip()899 if not branch_name:900 messagebox.showerror("Error", "Branch name cannot be empty")901 return902 903 try:904 # Redirect stdout to the database console905 old_stdout = sys.stdout906 sys.stdout = StdoutRedirector(self.db_console)907 908 add_branch(branch_name)909 self.branch_name_var.set("") # Clear the entry910 self.refresh_branches()911 self.update_stats()912 self.status_var.set(f"Branch '{branch_name}' added")913 except Exception as e:914 messagebox.showerror("Error", f"Failed to add branch: {str(e)}")915 self.db_console.insert(tk.END, f"Error adding branch: {str(e)}\n")916 finally:917 # Restore stdout918 sys.stdout = old_stdout919 920 def add_professor(self):921 """Add a new professor to the database"""922 prof_name = self.prof_name_var.get().strip()923 branch_name = self.prof_branch_var.get()924 925 if not prof_name:926 messagebox.showerror("Error", "Professor name cannot be empty")927 return928 929 if not branch_name:930 messagebox.showerror("Error", "Please select a branch")931 return932 933 try:934 # Redirect stdout to the database console935 old_stdout = sys.stdout936 sys.stdout = StdoutRedirector(self.db_console)937 938 add_professor(prof_name, branch_name)939 self.prof_name_var.set("") # Clear the entry940 self.update_stats()941 self.status_var.set(f"Professor '{prof_name}' added")942 self.refresh_mapping_lists()943 except Exception as e:944 messagebox.showerror("Error", f"Failed to add professor: {str(e)}")945 self.db_console.insert(tk.END, f"Error adding professor: {str(e)}\n")946 finally:947 # Restore stdout948 sys.stdout = old_stdout949 950 # Course management functions951 def add_course(self):952 """Add a new course to the database"""953 course_code = self.course_code_var.get().strip().upper()954 course_name = self.course_name_var.get().strip()955 branch_name = self.course_branch_var.get()956 957 if not course_code or not course_name:958 messagebox.showerror("Error", "Course code and name cannot be empty")959 return960 961 if not branch_name:962 messagebox.showerror("Error", "Please select a branch")963 return964 965 try:966 # Redirect stdout to the course console967 old_stdout = sys.stdout968 sys.stdout = StdoutRedirector(self.course_console)969 970 # Add course with branch971 with sqlite3.connect("timetable.db") as conn:972 cursor = conn.cursor()973 cursor.execute(974 "INSERT INTO courses (course_code, course_name, branch_name) VALUES (?, ?, ?)",975 (course_code, course_name, branch_name)976 )977 978 # Initialize course_classes with default value (2 classes per week)979 cursor.execute(980 "INSERT INTO course_classes (course_code, num_classes) VALUES (?, ?)",981 (course_code, 2)982 )983 984 conn.commit()985 self.course_console.insert(tk.END, f"โ
Course '{course_code}' added successfully!\n")986 987 self.course_code_var.set("") # Clear the entries988 self.course_name_var.set("")989 self.update_stats()990 self.status_var.set(f"Course '{course_code}' added")991 self.refresh_courses()992 self.refresh_mapping_lists()993 except Exception as e:994 messagebox.showerror("Error", f"Failed to add course: {str(e)}")995 self.course_console.insert(tk.END, f"Error adding course: {str(e)}\n")996 finally:997 # Restore stdout998 sys.stdout = old_stdout999 1000 def refresh_courses(self):1001 """Refresh the course comboboxes"""1002 try:1003 with sqlite3.connect("timetable.db") as conn:1004 cursor = conn.cursor()1005 cursor.execute("SELECT course_code, course_name FROM courses ORDER BY course_code")1006 courses = cursor.fetchall()1007 1008 # Format courses for display1009 course_display = [f"{code}: {name}" for code, name in courses]1010 1011 # Update course comboboxes1012 self.delete_course_combo['values'] = course_display1013 self.mapping_course_combo['values'] = course_display1014 1015 if course_display:1016 self.delete_course_combo.current(0)1017 self.mapping_course_combo.current(0)1018 except Exception as e:1019 messagebox.showerror("Error", f"Failed to refresh courses: {str(e)}")1020 1021 def refresh_mapping_lists(self):1022 """Refresh all lists for the mapping tab"""1023 try:1024 # Refresh branches1025 self.refresh_branches()1026 1027 # Refresh courses1028 self.refresh_courses()1029 1030 # Refresh teachers1031 with sqlite3.connect("timetable.db") as conn:1032 cursor = conn.cursor()1033 cursor.execute("SELECT teacher_name FROM teachers ORDER BY teacher_name")1034 teachers = [teacher[0] for teacher in cursor.fetchall()]1035 1036 self.mapping_teacher_combo['values'] = teachers1037 1038 if teachers:1039 self.mapping_teacher_combo.current(0)1040 except Exception as e:1041 messagebox.showerror("Error", f"Failed to refresh mapping lists: {str(e)}")1042 1043 def add_mapping(self):1044 """Add a branch-teacher-course mapping"""1045 branch_name = self.mapping_branch_var.get()1046 teacher_name = self.mapping_teacher_var.get()1047 course_full = self.mapping_course_var.get()1048 1049 if not branch_name or not teacher_name or not course_full:1050 messagebox.showerror("Error", "Please select branch, teacher, and course")1051 return1052 1053 # Extract course code from the display string (e.g., "CS101: Introduction to Programming")1054 course_code = course_full.split(":")[0].strip()1055 1056 try:1057 # Redirect stdout to the course console1058 old_stdout = sys.stdout1059 sys.stdout = StdoutRedirector(self.course_console)1060 1061 add_branch_teacher_course_mapping(branch_name, teacher_name, course_code)1062 self.status_var.set(f"Mapping added for {course_code}")1063 except Exception as e:1064 messagebox.showerror("Error", f"Failed to add mapping: {str(e)}")1065 self.course_console.insert(tk.END, f"Error adding mapping: {str(e)}\n")1066 finally:1067 # Restore stdout1068 sys.stdout = old_stdout1069 1070 def delete_course(self):1071 """Delete a course from the database"""1072 course_full = self.delete_course_var.get()1073 1074 if not course_full:1075 messagebox.showerror("Error", "Please select a course to delete")1076 return1077 1078 # Extract course code from the display string1079 course_code = course_full.split(":")[0].strip()1080 1081 # Confirm deletion1082 confirm = messagebox.askyesno("Confirm Deletion", 1083 f"Are you sure you want to delete course '{course_code}'?\n"1084 "This will also delete all mappings for this course.")1085 if not confirm:1086 return1087 1088 try:1089 # Redirect stdout to the course console1090 old_stdout = sys.stdout1091 sys.stdout = StdoutRedirector(self.course_console)1092 1093 delete_course(course_code)1094 self.update_stats()1095 self.status_var.set(f"Course '{course_code}' deleted")1096 self.refresh_courses()1097 except Exception as e:1098 messagebox.showerror("Error", f"Failed to delete course: {str(e)}")1099 self.course_console.insert(tk.END, f"Error deleting course: {str(e)}\n")1100 finally:1101 # Restore stdout1102 sys.stdout = old_stdout1103 1104 # Timetable generation functions1105 def generate_timetable(self):1106 """Generate a timetable using the genetic algorithm"""1107 # Verify database integrity first1108 try:1109 if not verify_database_integrity():1110 messagebox.showerror("Error", "Database integrity check failed. Please fix the issues before generating a timetable.")1111 return1112 except Exception as e:1113 messagebox.showerror("Error", f"Failed to verify database: {str(e)}")1114 return1115 1116 # Get branches1117 try:1118 with sqlite3.connect("timetable.db") as conn:1119 cursor = conn.cursor()1120 cursor.execute("SELECT branch_name FROM branches")1121 branches = [branch[0] for branch in cursor.fetchall()]1122 1123 if not branches:1124 messagebox.showerror("Error", "No branches found. Please add branches first.")1125 return1126 except Exception as e:1127 messagebox.showerror("Error", f"Failed to get branches: {str(e)}")1128 return1129 1130 # Get generations1131 try:1132 generations = int(self.generations_var.get())1133 if generations < 30:1134 messagebox.showerror("Error", "Generations must be at least 30")1135 return1136 # Limit to 200 for better performance1137 if generations > 200:1138 if not messagebox.askyesno("Warning", "Using more than 200 generations may cause the application to become unresponsive. Continue anyway?"):1139 generations = 2001140 self.generations_var.set("200")1141 except ValueError:1142 messagebox.showerror("Error", "Please enter a valid number for generations")1143 return1144 1145 # Show progress dialog1146 progress_window = tk.Toplevel(self.root)1147 progress_window.title("Generating Timetable")1148 progress_window.geometry("400x150")1149 progress_window.transient(self.root)1150 progress_window.grab_set()1151 1152 ttk.Label(progress_window, text="Generating timetable...", font=("Arial", 12)).pack(pady=10)1153 progress = ttk.Progressbar(progress_window, mode="indeterminate")1154 progress.pack(fill=tk.X, padx=20, pady=10)1155 progress.start()1156 1157 status_var = tk.StringVar(value="Starting genetic algorithm...")1158 ttk.Label(progress_window, textvariable=status_var).pack(pady=10)1159 1160 # Update the progress variable1161 self.progress_var.set("Generating...")1162 1163 # Function to run in a separate thread1164 def generate():1165 try:1166 # Update status in the progress window1167 def update_status(msg):1168 self.root.after(0, lambda: status_var.set(msg))1169 self.root.after(0, lambda: self.progress_var.set(msg))1170 1171 # Update status to show progress1172 update_status("Initializing genetic algorithm...")1173 1174 # Generate timetable using the genetic algorithm1175 # Suppress verbose output by setting verbose=False1176 result = genetic_algorithm(branches=branches, generations=generations, verbose=False)1177 1178 # Update status to show completion1179 update_status("Timetable generation complete!")1180 1181 if result:1182 schedule, professor_schedules = result1183 # Store for later access1184 self.last_timetable = schedule1185 self.last_professor_schedules = professor_schedules1186 1187 # Display timetable1188 timetable_output = io.StringIO()1189 print_timetable(schedule, file=timetable_output)1190 timetable_text = timetable_output.getvalue()1191 1192 # Update UI in the main thread1193 self.root.after(0, lambda: self.update_timetable_display(timetable_text))1194 self.root.after(0, lambda: self.refresh_professors())1195 self.root.after(0, lambda: self.status_var.set("Timetable generated successfully"))1196 self.root.after(0, lambda: self.progress_var.set("Completed"))1197 self.root.after(0, lambda: progress_window.destroy())1198 else:1199 self.root.after(0, lambda: messagebox.showerror("Error", "Failed to generate a valid timetable."))1200 self.root.after(0, lambda: self.progress_var.set("Failed"))