191 lines
7.7 KiB
Python
191 lines
7.7 KiB
Python
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('<<ComboboxSelected>>', 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('<<ComboboxSelected>>', 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))
|