1+ # Imports library for GUI and Message box.
2+ import tkinter as TK
3+ from tkinter import messagebox
4+
5+ # Creates the class for the GUI, allowing all of its objects and attributes to be acted upon properly.
6+ class Gradebook_Calculator :
7+ def __init__ (self , root ):
8+ self .root = root
9+
10+ # Checks if the user has used the program before and skips the intro and instructions.
11+ def Returning_User_Check (self ):
12+
13+ # Reads config file to check user status.
14+ self .config = open ('GBC_Config.txt' , 'a+' )
15+ self .config .seek (0 )
16+ self .returning_user = self .config .readline ()
17+
18+ # First time user is sent through whole path and returning users are sent to main page.
19+ if self .returning_user != 'Returning' :
20+ self .returning = False
21+ self .config .write ('Returning' )
22+ self .config .close ()
23+ self .Get_Started ()
24+
25+ elif self .returning_user == 'Returning' :
26+ self .returning = True
27+ self .Create_Gradebook ()
28+
29+ # Creates the first window.
30+ def Get_Started (self ):
31+
32+ # Moves to second page
33+ self .get_started = TK .Button (self .root , text = 'Get Started' , command = self .Intro )
34+ self .get_started .pack ()
35+
36+ # Creates the instructions page.
37+ def Intro (self ):
38+
39+ # Removes the first page.
40+ self .get_started .destroy ()
41+
42+ # Auto maximizes the window.
43+ self .root .state ('zoomed' )
44+
45+ # Pulls up the instructions.
46+ self .gradebook_instructions_intro = open ('GBC_Instructions.txt' , 'r' )
47+ self .temp_GB_I_I = self .gradebook_instructions_intro .read ()
48+ self .gradebook_instructions_intro .close ()
49+
50+ self .introduction = TK .Label (self .root , text = self .temp_GB_I_I )
51+ self .introduction .pack ()
52+
53+ # Moves to the next page.
54+ self .start_working = TK .Button (self .root , text = 'Start' , command = self .Create_Gradebook )
55+ self .start_working .pack ()
56+
57+ self .config = open ('GBC_Config.txt' , 'w' )
58+ self .temp_config = self .config .write ('Returning' )
59+
60+ ''' Creates the space for the grade book, establishes the buttons for: pulling up reference instructions, adding a class cell, removing a class cell,
61+ and calculating grades. This also allows the grade book to adapt to any screen size, and creates most of the variables to be used later.'''
62+ def Create_Gradebook (self ):
63+
64+ # Removes the previous page if user is first time.
65+ if self .returning == False :
66+ self .introduction .destroy ()
67+ self .start_working .destroy ()
68+
69+ if self .returning == True :
70+ self .root .state ('zoomed' )
71+
72+ # Creates function buttons.
73+ self .instructions_button = TK .Button (self .root , text = 'Instructions' , command = self .Instructions )
74+ self .instructions_button .grid (row = 0 , column = 0 )
75+
76+ self .add_class_button = TK .Button (self .root , text = 'Add Class' , command = self .Add_Class )
77+ self .add_class_button .grid (row = 0 , column = 1 )
78+
79+ self .remove_class_button = TK .Button (self .root , text = 'Remove Class' , command = self .Remove_Class )
80+ self .remove_class_button .grid (row = 0 , column = 2 )
81+
82+ self .calculate_button = TK .Button (self .root , text = 'Calculate Average' , command = self .Calculate )
83+ self .calculate_button .grid (row = 0 , column = 3 )
84+
85+ # Adapts GUI for any screen.
86+ for c in range (13 ):
87+ root .grid_columnconfigure (c , weight = 1 )
88+ for r in range (45 ):
89+ root .grid_rowconfigure (r , weight = 1 )
90+
91+ # Variables for the one per class cell.
92+ self .class_name_labels = list ()
93+ self .names_of_classes = list ()
94+ self .category_name_labels = list ()
95+ self .category_value_labels = list ()
96+ self .category_average_labels = list ()
97+ self .column_spacers = list ()
98+
99+ # Variables for the ten per class cell.
100+ self .names_of_categories = list ()
101+ self .values_of_categories = list ()
102+ self .category_grades = list ()
103+ self .row_spacers = list ()
104+
105+ # Counts the number of classes and the cells within the class.
106+ self .class_cell_counter = 0
107+ self .cell_counter = 0
108+
109+ # Places the widgets in the proper location by tracking current row and column.
110+ self .grid_tracker_row = 0
111+ self .grid_tracker_column = 0
112+
113+ # Is used to see if a calculation has been performed or not.
114+ self .calculated = False
115+ self .all_calculated = False
116+
117+ # Variables for calculating and displaying averages.
118+ self .class_averages = list ()
119+ self .averages_of_classes = list ()
120+
121+ # Pulls up the instructions in a separate window for reference.
122+ def Instructions (self ):
123+
124+ self .gradebook_instructions = open ('GBC_Instructions.txt' , 'r' )
125+ self .temp_GB_I = self .gradebook_instructions .read ()
126+ self .gradebook_instructions .close ()
127+
128+ self .instructions_window = TK .Toplevel ()
129+ self .instructions_window .title ('Instructions' )
130+
131+ self .instructions = TK .Label (self .instructions_window , text = self .temp_GB_I )
132+ self .instructions .pack ()
133+
134+ self .close_instructions = TK .Button (self .instructions_window , text = 'Dismiss' , command = self .instructions_window .destroy )
135+ self .close_instructions .pack ()
136+
137+ self .instructions_window .mainloop ()
138+
139+
140+ '''Adds a class(A class includes a name label with one name entry, a category name label with up to ten category name entries, a category value label
141+ with up to ten category value entries, a category average label with ten category average entries. Also included are blank column, and row labels to
142+ space and organize the grade book.)'''
143+ def Add_Class (self ):
144+
145+ # Tells user about the class limit.
146+ if self .class_cell_counter == 9 :
147+ self .class_limit_max = TK .messagebox .showinfo ('' , 'You may only have nine classes.' )
148+
149+ else :
150+
151+ # Creates the one per class widgets.
152+ self .class_name_label = TK .Label (self .root , text = 'Class Name' )
153+ self .class_name_label .grid (row = (1 + self .grid_tracker_row ), column = (2 + self .grid_tracker_column ))
154+ self .class_name_labels .append (self .class_name_label )
155+
156+ self .name_of_class = TK .Entry (self .root )
157+ self .name_of_class .grid (row = (2 + self .grid_tracker_row ), column = (2 + self .grid_tracker_column ))
158+ self .names_of_classes .append (self .name_of_class )
159+
160+ self .category_name_label = TK .Label (self .root , text = 'Category Name' )
161+ self .category_name_label .grid (row = (3 + self .grid_tracker_row ), column = (1 + self .grid_tracker_column ))
162+ self .category_name_labels .append (self .category_name_label )
163+
164+ self .category_value_label = TK .Label (self .root , text = 'Category Value' )
165+ self .category_value_label .grid (row = (3 + self .grid_tracker_row ), column = (2 + self .grid_tracker_column ))
166+ self .category_value_labels .append (self .category_value_label )
167+
168+ self .category_average_label = TK .Label (self .root , text = 'Category Average' )
169+ self .category_average_label .grid (row = (3 + self .grid_tracker_row ), column = (3 + self .grid_tracker_column ))
170+ self .category_average_labels .append (self .category_average_label )
171+
172+
173+ self .column_spacer = TK .Label (self .root , text = ' ' )
174+ self .column_spacer .grid (row = (3 + self .grid_tracker_row ), column = (4 + self .grid_tracker_column ))
175+ self .column_spacers .append (self .column_spacer )
176+
177+ # Creates the ten per class widgets.
178+ for z in range (10 ):
179+
180+ self .name_of_category = TK .Entry (self .root )
181+ self .name_of_category .grid (row = (4 + z + self .grid_tracker_row ), column = (1 + self .grid_tracker_column ))
182+ self .names_of_categories .append (self .name_of_category )
183+
184+ self .value_of_category = TK .Entry (self .root )
185+ self .value_of_category .grid (row = (4 + z + self .grid_tracker_row ), column = (2 + self .grid_tracker_column ))
186+ self .values_of_categories .append (self .value_of_category )
187+
188+ self .category_grade = TK .Entry (self .root )
189+ self .category_grade .grid (row = (4 + z + self .grid_tracker_row ), column = (3 + self .grid_tracker_column ))
190+ self .category_grades .append (self .category_grade )
191+
192+ self .row_spacer = TK .Label (self .root , text = '\n ' )
193+ self .row_spacer .grid (row = (15 + self .grid_tracker_row ), column = (1 + self .grid_tracker_column ))
194+ self .row_spacers .append (self .row_spacer )
195+
196+ # Tracks amount of classes and amount of cells or widgets.
197+ self .class_cell_counter += 1
198+ self .cell_counter += 10
199+
200+ # Tracks cell locations.
201+ self .grid_tracker_column += 4
202+ if self .class_cell_counter == 3 or self .class_cell_counter == 6 :
203+ self .grid_tracker_row += 17
204+ self .grid_tracker_column = 0
205+
206+ # This variable is used to prevent logic errors when calculating classes after adding.
207+ self .class_status = 'Add'
208+
209+ # Prevents logic errors when classes are added and removed after a calculation and before another.
210+ if self .calculated == True :
211+ self .all_calculated = False
212+
213+ # Removes an entire class along with the average label if it exists.
214+ def Remove_Class (self ):
215+
216+ # Tells user to add a class.
217+ if self .class_cell_counter < 1 :
218+ self .class_limit_min = TK .messagebox .showinfo ('' , 'Please add a class first.' )
219+
220+ else :
221+
222+ # Removes the one per class widgets.
223+ self .class_name_labels [self .class_cell_counter - 1 ].destroy ()
224+ del self .class_name_labels [self .class_cell_counter - 1 ]
225+
226+ self .names_of_classes [self .class_cell_counter - 1 ].destroy ()
227+ del self .names_of_classes [self .class_cell_counter - 1 ]
228+
229+ self .category_name_labels [self .class_cell_counter - 1 ].destroy ()
230+ del self .category_name_labels [self .class_cell_counter - 1 ]
231+
232+ self .category_value_labels [self .class_cell_counter - 1 ].destroy ()
233+ del self .category_value_labels [self .class_cell_counter - 1 ]
234+
235+ self .category_average_labels [self .class_cell_counter - 1 ].destroy ()
236+ del self .category_average_labels [self .class_cell_counter - 1 ]
237+
238+ self .column_spacers [self .class_cell_counter - 1 ].destroy ()
239+ del self .column_spacers [self .class_cell_counter - 1 ]
240+
241+ # Removes the ten per class widgets.
242+ for z in range (10 ):
243+
244+ self .names_of_categories [self .cell_counter - z - 1 ].destroy ()
245+ del self .names_of_categories [self .cell_counter - z - 1 ]
246+
247+ self .values_of_categories [self .cell_counter - z - 1 ].destroy ()
248+ del self .values_of_categories [self .cell_counter - z - 1 ]
249+
250+ self .category_grades [self .cell_counter - z - 1 ].destroy ()
251+ del self .category_grades [self .cell_counter - z - 1 ]
252+
253+ self .row_spacers [self .cell_counter - z - 1 ].destroy ()
254+ del self .row_spacers [self .cell_counter - z - 1 ]
255+
256+ # Tracks the amount of classes and cells or widgets.
257+ self .class_cell_counter -= 1
258+ self .cell_counter -= 10
259+
260+ # Tracks cell locations.
261+ self .grid_tracker_column -= 4
262+ if self .class_cell_counter == 2 or self .class_cell_counter == 5 :
263+ self .grid_tracker_row -= 17
264+ self .grid_tracker_column = 8
265+
266+ # Removes the average label if it exists and prevents indexing errors if a calculation has been preformed before classes are removed.
267+ if self .calculated == True :
268+ if self .all_calculated == True :
269+ if self .class_status == 'Add' :
270+ self .averages_of_classes [self .temp_class_cell_counter - 1 ].destroy ()
271+ del self .averages_of_classes [self .temp_class_cell_counter - 1 ]
272+ self .temp_class_cell_counter -= 1
273+ if self .class_status == 'Remove' :
274+ self .averages_of_classes [self .class_cell_counter ].destroy ()
275+ del self .averages_of_classes [self .class_cell_counter ]
276+ self .temp_class_cell_counter -= 1
277+
278+ # Resets the calculated variable if all classes are removed.
279+ if self .class_cell_counter == 0 :
280+ self .calculated = False
281+
282+ # This variable is used to prevent logic errors when calculating classes after removing.
283+ self .class_status = 'Remove'
284+
285+ # Calculates the average
286+ def Calculate (self ):
287+
288+ # Stores average calculations.
289+ self .class_averages .clear ()
290+
291+ # Gets proper values for calculations without disturbing the tracking of the cells.
292+ self .average_cell_counter = 0
293+
294+ # Tracks the location for placing the average labels without disturbing the placement tracker for other cells.
295+ self .average_grid_tracker_row = 0
296+ self .average_grid_tracker_column = 0
297+
298+ # Informs user a class must be added.
299+ if self .class_cell_counter < 1 :
300+ self .need_class = TK .messagebox .showinfo ('' , 'Please add at least one class.' )
301+
302+ # Calculates the average and then adds the average label to the display.
303+ if self .class_cell_counter >= 1 :
304+
305+ # Prevents the labels from overlapping on multiple calculations by delete the current widgets.
306+ if self .calculated == True :
307+
308+ # Prevents a list indexing error if a class is added after a calculations.
309+ if self .class_status == 'Add' :
310+ for d in range (self .temp_class_cell_counter ):
311+ self .averages_of_classes [0 ].destroy ()
312+ del self .averages_of_classes [0 ]
313+
314+ # Prevents a list indexing error if a class is removed after a calculations.
315+ if self .class_status == 'Remove' :
316+ for e in range (self .temp_class_cell_counter ):
317+ self .averages_of_classes [0 ].destroy ()
318+ del self .averages_of_classes [0 ]
319+
320+ # Denotes that a calculation has been preformed.
321+ self .calculated = True
322+ self .all_calculated = True
323+
324+ # Calculates the grade, and creates the label for all class cells that exist at the time.
325+ for n in range (self .class_cell_counter ):
326+ self .category_averages = list ()
327+
328+ # Gets the values from the category value and category average entries if the entry is filled if not the entry is skipped.
329+ for m in range (10 ):
330+ if self .values_of_categories [m + self .average_cell_counter ].get () == '' or self .category_grades [m + self .average_cell_counter ].get () == '' :
331+ self .category_averages .append (0 )
332+ else :
333+ self .category_averages .append (float (self .values_of_categories [m + self .average_cell_counter ].get ())* float (self .category_grades [m + self .average_cell_counter ].get ()))
334+
335+ # Converts the grade average to a percent.
336+ self .class_averages .append ((sum (self .category_averages )* 100 ))
337+
338+ # Creates the label displaying the averages.
339+ self .average_of_class = TK .Label (self .root , text = 'Average for {} {} %' .format (self .names_of_classes [n ].get (), self .class_averages [n ]))
340+ self .average_of_class .grid (row = (15 + self .average_grid_tracker_row ), column = (2 + self .average_grid_tracker_column ))
341+ self .averages_of_classes .append (self .average_of_class )
342+
343+ # Tracks the cell location for calculations.
344+ self .average_cell_counter += 10
345+
346+ # Tracks the average label locations.
347+ self .average_grid_tracker_column += 4
348+ if n == 2 or n == 5 :
349+ self .average_grid_tracker_row += 17
350+ self .average_grid_tracker_column = 0
351+
352+ # Stores the current amount of classes at the time of calculation to prevent list indexing errors.
353+ self .temp_class_cell_counter = self .class_cell_counter
354+
355+ # Method to start the GUI.
356+ def Run (self ):
357+
358+ # Calls the first method to allow the user to begin acting within the program.
359+ self .Returning_User_Check ()
360+
361+ # Keeps the GUI running.
362+ self .root .mainloop ()
363+
364+ # Main (Where program begins.)
365+ if __name__ == '__main__' :
366+
367+ # GUI function, title.
368+ root = TK .Tk ()
369+ root .title ('Gradebook Calculator' )
370+
371+ # Pulls the Gradebook_Calculator class and begin (runs) it.
372+ GBC = Gradebook_Calculator (root )
373+ GBC .Run ()
374+
0 commit comments