import tkinter import tkinter.ttk import constants from models import Programme from widgets import ModuleWidget from pprint import pprint # app.py - Main GUI dialog class App(tkinter.Tk): def __init__(self, db): super().__init__() self.__db = db self.__programme = None self.__modules = None self.refresh_db() self.setup_ui() # NOTE: Do not modify this method def report_callback_exception(self, *args): """When an error is occurred, raise the exception so main_gui.py will handle the display of error. """ raise args[1].with_traceback(args[2]) def refresh_db(self): # Fetch all program records from database, and store it in __programme as a list self.__programme = Programme.fetchall(self.__db) def setup_ui(self): self.title(constants.APP_NAME) self.minsize(640, 480) # Set weight of column 0 for auto resizing on X axis self.columnconfigure(0, weight=1) # Header frm_top_bar = tkinter.Frame(self) frm_top_bar.grid(row=0, sticky=tkinter.EW) tkinter.Label( frm_top_bar, text = constants.APP_NAME, font = ('bold', 16) ).pack(side = tkinter.LEFT, padx=8, pady=8) tkinter.ttk.Separator(self).grid(row=1, sticky=tkinter.EW) # Content frm_content = tkinter.Frame(self) frm_content.grid(row=2, sticky=tkinter.NSEW) self.rowconfigure(2, weight=1) # Options frm_filter = tkinter.LabelFrame(frm_content, text = "Options") frm_filter.pack(fill=tkinter.X, side=tkinter.TOP, padx=8, pady=(4, 0)) frm_filter_container = tkinter.Frame(frm_filter) frm_filter_container.pack(fill=tkinter.BOTH, side=tkinter.TOP, padx=6, pady=(0, 4)) # Options variables # Option widgets tkinter.Label(frm_filter_container,text = "Programme",).grid(row=0, column=0, padx=(0, 4)) list_of_programme=[] for p in self.__programme: list_of_programme.append(p.get_name()) self.selected_programme = tkinter.StringVar() self.__om_programme_filter = tkinter.ttk.Combobox(frm_filter_container,textvariable=self.selected_programme, width=50) self.__om_programme_filter.set('Please select') self.__om_programme_filter['values'] = list_of_programme self.__om_programme_filter['state'] = 'readonly' self.__om_programme_filter.grid(row=0, column=1, padx=(0, 4), sticky=tkinter.EW) self.__om_programme_filter.bind('<>', self.on_programme_changed ) # Semester select tkinter.Label( frm_filter_container, text = "Semester").grid(row=0, column=2, padx=(0, 4)) self.selected_semester = tkinter.StringVar() self.__om_semester_filter = tkinter.ttk.Combobox(frm_filter_container, textvariable=self.selected_semester) self.__om_semester_filter.set('Please select') self.__om_semester_filter['state'] = 'disabled' self.__om_semester_filter.grid(row=0, column=3, padx=(4, 4)) self.__om_semester_filter.bind('<>', self.on_semester_changed) # Modules frm_modules = tkinter.LabelFrame(frm_content, text = "Modules") frm_modules.pack(fill=tkinter.BOTH, side=tkinter.TOP, expand=1, padx=8, pady=(4, 0)) self.__frm_modules_container = tkinter.Frame(frm_modules) self.__frm_modules_container.pack(fill=tkinter.BOTH, side=tkinter.TOP, padx=6, pady=(0, 4)) self.__module_widgets = list() for _ in range(constants.APP_MAX_MODULES): widget = ModuleWidget(self.__frm_modules_container, self.on_gpa_changed) widget.get_frame().pack(fill=tkinter.X, side=tkinter.TOP, expand=1, pady=8) widget.set_module(None) self.__module_widgets.append(widget) # Bottom bar frm_bottom_bar = tkinter.Frame(self) frm_bottom_bar.grid(row=3, column=0,padx=(0, 10), pady=(4,4), sticky=tkinter.E) # Result Label self.semester_gpa = tkinter.StringVar() self.lbl_stu_list = tkinter.ttk.Label(frm_bottom_bar, text='Semester GPA: %.2f' % 0, font=("bold",11)) self.lbl_stu_list.grid(row=0, column=0, sticky=tkinter.E) def setup_module_list(self): # Update the ModuleWidget in list __module_widgets to display # modules for the selected programme and semester current_select_idx = self.__om_semester_filter.current() selected_semester = list(sorted(self.__modules.keys()))[current_select_idx] module_in_semester = self.__modules[selected_semester] for widget in self.__frm_modules_container.winfo_children(): widget.destroy() self.__module_widgets = list() i = 0 for _ in range(constants.APP_MAX_MODULES): widget = ModuleWidget(self.__frm_modules_container, self.on_gpa_changed) widget.get_frame().pack(fill=tkinter.X, side=tkinter.TOP, expand=1, pady=8) if (i < len(module_in_semester)): widget.set_module(module_in_semester[i]) self.__module_widgets.append(widget) i = i + 1 def on_programme_changed(self, _): # Get selected programme based on option menu index, and update GUI for program in self.__programme: if (self.selected_programme.get() == program.get_name()): self.__om_semester_filter['values'] = sorted(program.get_modules().keys()) self.__modules = program.get_modules() break self.__om_semester_filter['state'] = 'readonly' self.__om_semester_filter.set('Please select') for widget in self.__frm_modules_container.winfo_children(): widget.destroy() self.__module_widgets = list() for _ in range(constants.APP_MAX_MODULES): widget = ModuleWidget(self.__frm_modules_container, self.on_gpa_changed) widget.get_frame().pack(fill=tkinter.X, side=tkinter.TOP, expand=1, pady=8) widget.set_module(None) self.__module_widgets.append(widget) # reset gpa self.lbl_stu_list.configure(text='Semester GPA: %.2f' % (0)) def on_semester_changed(self, _): # Get modules based on option menu value, and update GUI self.setup_module_list() # reset gpa self.lbl_stu_list.configure(text='Semester GPA: %.2f' % (0)) def on_gpa_changed(self): # Calcuate semester GPA based on self.__modules # 1. For each modules in self.__modules, App class # 2. Obtain the module credit of current module and add it to the total credits # 3. Calculate the module gpa by multiplying the current module gpa and module credit # 4. Add the calculated module gpa to total module gpa # 5. After all module gpa are calculated, divide the total module gpa by total credits total = 0 current_select_idx = self.__om_semester_filter.current() selected_semester = list(sorted(self.__modules.keys()))[current_select_idx] module_in_semester = self.__modules[selected_semester] number_of_modules = len(module_in_semester) should_update = True all_credit = 0 for i in range(0, number_of_modules): gpa = self.__module_widgets[i].get_gpa() if (gpa == -1): # not selected should_update = False else: credit =self.__module_widgets[i].get_module().get_credit() all_credit = all_credit + credit total = total + (gpa * credit) if should_update: self.lbl_stu_list.configure(text='Semester GPA: %.2f' % (total / all_credit))