I recently got feedback for a programming assignment that was positive but mentioned that 'some things could be done better', and I was wondering if anybody here might be able to give me a more specific idea of what could to be improved & why.
There's quite a bit of code, but as I'm at a very introductory level I'm hoping it won't be too difficult to pick out some basic concepts that need work. (Also, I'm not too worried about how the functionality of the program could be improved - that was informed by the guidelines of the assignment - I'm more looking for advice on how to improve the code for the functionality that is currently there).
The assignment was to create a food quiz GUI that reads data from a txt file, & a CLI admin that creates/edits that file.
CLI:
import json
# Repeatedly prompt for input until an integer is entered.
def inputInt(prompt):
while True:
try:
answer = int(input(prompt))
except ValueError:
print('Please enter a number.')
continue
break
return answer
# Repeatedly prompt for input until something (not whitespace) is entered.
def inputSomething(prompt):
answer = input(prompt)
while not answer.strip():
answer = input('Please enter a non-whitespace value: ')
return answer
# Open "data.txt" in write mode and write the data to it in JSON format.
def saveChanges(dataList):
f = open('data.txt', 'w')
json.dump(dataList, f, indent=4)
f.close()
# If the file does not exist or does not contain JSON data, set "data" to an empty list instead.
try:
f = open('data.txt', 'r')
data = json.load(f)
f.close()
except (FileNotFoundError, ValueError):
data = []
# Print welcome message, then enter the endless loop which prompts the user for a choice.
print('Welcome to the Food Quiz Admin Program.')
while True:
print('Choose [a]dd, [l]ist, [s]earch, [v]iew, [d]elete or [q]uit.')
choice = input('> ')
if choice == 'a':
print('Enter details for new menu item.')
#Create new item and prompt for values
item = {}
item['name'] = inputSomething('Name: ')
item['calories'] = inputInt('Calories: ')
item['fat'] = inputInt('Fat: ')
item['protien'] = inputInt('Protien: ')
item['carbohydrates'] = inputInt('Carbohydrates: ')
item['sodium'] = inputInt('Sodium: ')
item['cholesterol'] = inputInt('Cholesterol: ')
#Add item to data list and save changes
data.append(item)
saveChanges(data)
elif choice == 'l':
#Loop though and list the current menu items
for i, item in enumerate(data):
print(i, ': ', item)
elif choice == 's':
#Prompt for search term and convert to lowercase
search = inputSomething('Search items by name: ').lower()
#Loop through data and print items with search term in name
for i, item in enumerate(data):
if search in item['name'].lower():
print(i, ': ', item)
elif choice == 'v':
while True:
#Try to print item by index, reprompt if index is not valid
try:
indexItem = data[inputInt('Enter index of item to view: ')]
print('{name}, Calories: {calories}, Fat: {fat} g, Protien: {protien} g, Carbohydrates: {carbohydrates} g, Cholesterol: {cholesterol} mg, Sodium: {sodium} mg'.format(**indexItem))
except IndexError:
print('Invalid index. Please enter the index of an existing item.')
continue
break
elif choice == 'd':
while True:
#Try to delete item by index, reprompt if index is not valid
try:
del data[inputInt(prompt)]
except IndexError:
print('Invalid index. Please enter the index of an existing item.')
continue
break
saveChanges(data)
elif choice == 'q':
# Quit the program
print('Goodbye!')
break
else:
# Print 'invalid choice' message
print('Invalid choice.')
GUI:
import tkinter
import tkinter.messagebox
import tkinter.font
import json
import random
class ProgramGUI:
def __init__(self):
#Create main window
self.main = tkinter.Tk()
self.main.title('Food Quiz!')
self.main.geometry('450x225')
self.main.configure(bg='#5c9ead')
self.font1 = tkinter.font.Font(family='Helvetica', size=18, weight='bold')
self.font2 = tkinter.font.Font(family='Courier', size=14, weight='bold')
#Load data from data.txt, show error message & terminate if file does not exist or is invalid
try:
self.__f = open('data.txt', 'r')
self.data = json.load(self.__f)
self.__f.close()
except (FileNotFoundError, ValueError):
tkinter.messagebox.showerror('Oops!', 'Missing/Invalid file.')
self.main.destroy()
return
#Define components, initiliase score and label variables
self.components = ['calories', 'fat', 'cholesterol', 'sodium', 'carbohydrates', 'protien']
self.qsAsked = 0
self.qsCorrect = 0
self.name1 = tkinter.StringVar()
self.name2 = tkinter.StringVar()
self.componentLab = tkinter.StringVar()
#Create frames
self.nameBox = tkinter.Frame(self.main, bg='#5c9ead')
self.questionBox = tkinter.Frame(self.main, bg='#5c9ead')
self.buttonBox = tkinter.Frame(self.main, bg='#5c9ead')
#Create buttons and labels
tkinter.Label(self.nameBox, textvariable=self.name1, font=self.font1, bg='#5c9ead', fg='#EFBC9B').pack(side='left')
tkinter.Label(self.nameBox, text='vs', font=self.font2, bg='#5c9ead', fg='#fffdf7').pack(side='left')
tkinter.Label(self.nameBox, textvariable=self.name2, font=self.font1, bg='#5c9ead', fg='#EFBC9B').pack(side='left')
tkinter.Label(self.questionBox, text='Which one has more...', font=self.font2, bg='#5c9ead', fg='#fffdf7').pack()
tkinter.Label(self.questionBox, textvariable=self.componentLab, font=self.font1, bg='#5c9ead', fg='#EFBC9B').pack()
tkinter.Button(self.buttonBox, textvariable=self.name1, font=self.font2, bg='#EFBC9B', fg='#FFFDF7', command=lambda: self.checkAnswer('left')).pack(side='left')
tkinter.Button(self.buttonBox, text='Roughly Equal', font=self.font2, bg='#EFBC9B', fg='#FFFDF7', command=lambda: self.checkAnswer('middle')).pack(side='left', padx=5)
tkinter.Button(self.buttonBox, textvariable=self.name2, font=self.font2, bg='#EFBC9B', fg='#FFFDF7',command=lambda: self.checkAnswer('right')).pack(side='left')
self.nameBox.pack(pady=10)
self.questionBox.pack(pady=10)
self.buttonBox.pack(pady=20)
#Show first question
self.showQuestion()
#Start main loop
tkinter.mainloop()
def showQuestion(self):
#Create/update question attributes
self.items = random.sample(self.data, 2)
self.component = random.choice(self.components)
#Update labels
self.name1.set(self.items[0].get('name'))
self.name2.set(self.items[1].get('name'))
self.componentLab.set(self.component)
def checkAnswer(self, clickedButton):
#Update question counter and get values to compare
self.qsAsked += 1
self.val1 = self.items[0].get(self.component)
self.val2 = self.items[1].get(self.component)
#Define left button check (left > right), right button check (right > left) and middle button check (both values are within 10% of each other, assuming val1 is > 0)
self.buttons = {'left': self.val1 > self.val2, 'right': self.val2 > self.val1, 'middle': self.val1 * 10 / 11 <= self.val2 <= self.val1 * 11 / 10}
#Select check, update correct counter and display appropriate message
if self.buttons.get(clickedButton):
self.qsCorrect +=1
self.scoreMsg = 'Your score so far is ' + str(self.qsCorrect) + '/' + str(self.qsAsked)
tkinter.messagebox.showinfo('Correct!', 'You got it right. Your score so far is ' + str(self.qsCorrect) + '/' + str(self.qsAsked))
else:
tkinter.messagebox.showerror('Incorrect!', 'You got it wrong. Your score so far is ' + str(self.qsCorrect) + '/' + str(self.qsAsked))
#Generate new question
self.showQuestion()
# Create an object of the ProgramGUI class to begin the program.
gui = ProgramGUI()
if __name__ == "__main__"
and add your main loop to that. Also there is no need to use exceptions to check for invalid indices, these can be checked explicitly. \$\endgroup\$