This is the second time I am asking for a review of this code (first time can be found here: Arabic language lesson program). The goal of the program hasn't changed - to house Arabic language lessons and teach Arabic to English speakers, somewhat similar to how Duolingo works. My programmng style to date is very "linear" in fashion and I have almost no experience working with classes (though I have been reading the documentation).
The first time around I was instructed to move the questions out of the code and into a separate text file which I have now done. Now all my questions are sorted by lesson number and part number into a .json file and I call certain questions from the file in the code. However, I still have the same problem as before - that there is a great deal of repetition in my code. Eventually I want to have 50 to 100 question and the way my program is set up, that would be very difficult to maintain.
I would like help figuring out how to take my current code and migrate it into a more manageable setup (I'm assuming something class-based) that would allow me to continue to build this program in a manageable fashion.
My code is below:
def Part1():
Root_File_Name = "C:\\LearningArabic\\LiblibArriby\\"
JSON_File = Root_File_Name + "Lessons\\Lesson_1\\"
with open(JSON_File+"Arabic_Lessons.json", "r", encoding = "utf-8-sig") as question_file:
data = json.load(question_file)
def create_widgets_in_first_frame(): # Create the label for the frame
current_frame=first_frame #Make the frame number generic so as to make copy/paste easier. ##UPDATE PER QUESTION##
questionDirectory = data["lesson 1"]["part one"]["question1"] ##UPDATE PER QUESTION## This is the directory for the question.
wronganswer = questionDirectory["wronganswer"] #This is the directory for the wrong answers
question = questionDirectory.get("question") #This is the question text
correctanswerText = questionDirectory.get("answer") #This is the answer for whichever question has been selected.
arabic = questionDirectory.get("arabic") #This is the arabic text for the question
transliteration = questionDirectory.get("transliteration")
global score
score = 0 #Setting the initial score to zero.
print("Your score is: ", score)
global lives
lives = 3 #Setting the initial number of lives.
print("You have", lives, "lives")
#These lines of code randomly select three wrong images for the wrong answers and adds the proper file path to all four options (including the correct answer)
Image1 = ImagePath + random.choice(wronganswer)+ ".png"
Image2 = ImagePath + random.choice(wronganswer)+ ".png"
Image3 = ImagePath + random.choice(wronganswer)+ ".png"
correctanswerImage = ImagePath + correctanswerText + ".png"
#These four lines of code convert them to proper form for display in tkinter
answer1 = PhotoImage(file=Image1)
answer2 = PhotoImage(file=Image2)
answer3 = PhotoImage(file=Image3)
correctanswer = PhotoImage(file=correctanswerImage)
##### Maine Question is written here. #####
L1 = Label(current_frame, text=question, font=("Helvetica", 35))
L1.grid(columnspan=4, row=0)
global var
var = IntVar()
var.set(0) #Sets the initial radiobutton selection to nothing
def Transliteration():
Transliteration = Label(current_frame, text="'"+arabic+"'" + " is pronounced " + "'"+transliteration+"'", font=("Helvetica", 35))
Transliteration.grid(row=2, columnspan=4)
##### Makes the phonetic pronunciation button. #####
transliteration_button = Button(current_frame, text="Show Transliteration", command=Transliteration)
transliteration_button.grid(column=0, row=4)
choice1 = Radiobutton(current_frame, image=answer1, variable = var, value=1, command= Check_Answer)
choice1.image = answer1 # This prevents python garbage collection from deleting the image pre-maturely.
choice2 = Radiobutton(current_frame, image=answer2, variable = var, value=2, command= Check_Answer)
choice2.image = answer2 # This prevents python garbage collection from deleting the image pre-maturely.
choice3 = Radiobutton(current_frame, image=answer3, variable = var, value=3, command= Check_Answer)
choice3.image = answer3 # This prevents python garbage collection from deleting the image pre-maturely.
choice4 = Radiobutton(current_frame, image=correctanswer, variable = var, value=4, command= Check_Answer)
choice4.image = correctanswer # This prevents python garbage collection from deleting the image pre-maturely.
choices = [choice1, choice2, choice3, choice4]
random.shuffle(choices) #This line of code randomizes the order of the radiobuttons.
choices[0].grid(row=1, column=0)
choices[1].grid(row=1, column=1)
choices[2].grid(row=1, column=2)
choices[3].grid(row=1, column=3)
# Creats the quit button and displays it.
quit_button = Button(current_frame, text = "Quit", command = quit_program)
quit_button.grid(column=4, row=4)
def create_widgets_in_second_frame():
current_frame=second_frame #Make the frame number generic so as to make copy/paste easier. ##UPDATE PER QUESTION##
questionDirectory = data["lesson 1"]["part one"]["question2"] ##UPDATE PER QUESTION## This is the directory for the question.
wronganswer = questionDirectory["wronganswer"] #This is the directory for the wrong answers
question = questionDirectory.get("question") #This is the question text
correctanswerText = questionDirectory.get("answer") #This is the answer for whichever question has been selected.
arabic = questionDirectory.get("arabic") #This is the arabic text for the question
transliteration = questionDirectory.get("transliteration")
global score
score = 0 #Setting the initial score to zero.
print("Your score is: ", score)
global lives
lives = 3 #Setting the initial number of lives.
print("You have", lives, "lives")
#These lines of code randomly select three wrong images for the wrong answers and adds the proper file path to all four options (including the correct answer)
Image1 = ImagePath + random.choice(wronganswer)+ ".png"
Image2 = ImagePath + random.choice(wronganswer)+ ".png"
Image3 = ImagePath + random.choice(wronganswer)+ ".png"
correctanswerImage = ImagePath + correctanswerText + ".png"
#These four lines of code convert them to proper form for display in tkinter
answer1 = PhotoImage(file=Image1)
answer2 = PhotoImage(file=Image2)
answer3 = PhotoImage(file=Image3)
correctanswer = PhotoImage(file=correctanswerImage)
##### Maine Question is written here. #####
L1 = Label(current_frame, text=question, font=("Helvetica", 35))
L1.grid(columnspan=4, row=0)
global var
var = IntVar()
var.set(0) #Sets the initial radiobutton selection to nothing
def Transliteration():
Transliteration = Label(current_frame, text="'"+arabic+"'" + " is pronounced " + "'"+transliteration+"'", font=("Helvetica", 35))
Transliteration.grid(row=2, columnspan=4)
##### Makes the phonetic pronunciation button. #####
transliteration_button = Button(current_frame, text="Show Transliteration", command=Transliteration)
transliteration_button.grid(column=0, row=4)
choice1 = Radiobutton(current_frame, image=answer1, variable = var, value=1, command= Check_Answer)
choice1.image = answer1 # This prevents python garbage collection from deleting the image pre-maturely.
choice2 = Radiobutton(current_frame, image=answer2, variable = var, value=2, command= Check_Answer)
choice2.image = answer2 # This prevents python garbage collection from deleting the image pre-maturely.
choice3 = Radiobutton(current_frame, image=answer3, variable = var, value=3, command= Check_Answer)
choice3.image = answer3 # This prevents python garbage collection from deleting the image pre-maturely.
choice4 = Radiobutton(current_frame, image=correctanswer, variable = var, value=4, command= Check_Answer)
choice4.image = correctanswer # This prevents python garbage collection from deleting the image pre-maturely.
choices = [choice1, choice2, choice3, choice4]
random.shuffle(choices) #This line of code randomizes the order of the radiobuttons.
choices[0].grid(row=1, column=0)
choices[1].grid(row=1, column=1)
choices[2].grid(row=1, column=2)
choices[3].grid(row=1, column=3)
# Creats the quit button and displays it.
quit_button = Button(current_frame, text = "Quit", command = quit_program)
quit_button.grid(column=4, row=4)
def create_widgets_in_third_frame():
current_frame=third_frame #Make the frame number generic so as to make copy/paste easier
questionDirectory = data["lesson 1"]["part one"]["question3"] ##UPDATE PER QUESTION## This is the directory for the question.
wronganswer = questionDirectory["wronganswer"] #This is the directory for the wrong answers
question = questionDirectory.get("question") #This is the question text
correctanswerText = questionDirectory.get("answer") #This is the answer for whichever question has been selected.
arabic = questionDirectory.get("arabic") #This is the arabic text for the question
transliteration = questionDirectory.get("transliteration")
global score
score = 0 #Setting the initial score to zero.
print("Your score is: ", score)
global lives
lives = 3 #Setting the initial number of lives.
print("You have", lives, "lives")
#These lines of code randomly select three wrong images for the wrong answers and adds the proper file path to all four options (including the correct answer)
Image1 = ImagePath + random.choice(wronganswer)+ ".png"
Image2 = ImagePath + random.choice(wronganswer)+ ".png"
Image3 = ImagePath + random.choice(wronganswer)+ ".png"
correctanswerImage = ImagePath + correctanswerText + ".png"
#These four lines of code convert them to proper form for display in tkinter
answer1 = PhotoImage(file=Image1)
answer2 = PhotoImage(file=Image2)
answer3 = PhotoImage(file=Image3)
correctanswer = PhotoImage(file=correctanswerImage)
##### Maine Question is written here. #####
L1 = Label(current_frame, text=question, font=("Helvetica", 35))
L1.grid(columnspan=4, row=0)
global var
var = IntVar()
var.set(0) #Sets the initial radiobutton selection to nothing
def Transliteration():
Transliteration = Label(current_frame, text="'"+arabic+"'" + " is pronounced " + "'"+transliteration+"'", font=("Helvetica", 35))
Transliteration.grid(row=2, columnspan=4)
##### Makes the phonetic pronunciation button. #####
transliteration_button = Button(current_frame, text="Show Transliteration", command=Transliteration)
transliteration_button.grid(column=0, row=4)
choice1 = Radiobutton(current_frame, image=answer1, variable = var, value=1, command= Check_Answer)
choice1.image = answer1 # This prevents python garbage collection from deleting the image pre-maturely.
choice2 = Radiobutton(current_frame, image=answer2, variable = var, value=2, command= Check_Answer)
choice2.image = answer2 # This prevents python garbage collection from deleting the image pre-maturely.
choice3 = Radiobutton(current_frame, image=answer3, variable = var, value=3, command= Check_Answer)
choice3.image = answer3 # This prevents python garbage collection from deleting the image pre-maturely.
choice4 = Radiobutton(current_frame, image=correctanswer, variable = var, value=4, command= Check_Answer)
choice4.image = correctanswer # This prevents python garbage collection from deleting the image pre-maturely.
choices = [choice1, choice2, choice3, choice4]
random.shuffle(choices) #This line of code randomizes the order of the radiobuttons.
choices[0].grid(row=1, column=0)
choices[1].grid(row=1, column=1)
choices[2].grid(row=1, column=2)
choices[3].grid(row=1, column=3)
# Creats the quit button and displays it.
quit_button = Button(current_frame, text = "Quit", command = quit_program)
quit_button.grid(column=4, row=4)
def create_widgets_in_forth_frame():
current_frame=forth_frame #Make the frame number generic so as to make copy/paste easier
questionDirectory = data["lesson 1"]["part one"]["question4"] ##UPDATE PER QUESTION## This is the directory for the question.
wronganswer = questionDirectory["wronganswer"] #This is the directory for the wrong answers
question = questionDirectory.get("question") #This is the question text
correctanswerText = questionDirectory.get("answer") #This is the answer for whichever question has been selected.
arabic = questionDirectory.get("arabic") #This is the arabic text for the question
transliteration = questionDirectory.get("transliteration")
global score
score = 0 #Setting the initial score to zero.
print("Your score is: ", score)
global lives
lives = 3 #Setting the initial number of lives.
print("You have", lives, "lives")
#These lines of code randomly select three wrong images for the wrong answers and adds the proper file path to all four options (including the correct answer)
Image1 = ImagePath + random.choice(wronganswer)+ ".png"
Image2 = ImagePath + random.choice(wronganswer)+ ".png"
Image3 = ImagePath + random.choice(wronganswer)+ ".png"
correctanswerImage = ImagePath + correctanswerText + ".png"
#These four lines of code convert them to proper form for display in tkinter
answer1 = PhotoImage(file=Image1)
answer2 = PhotoImage(file=Image2)
answer3 = PhotoImage(file=Image3)
correctanswer = PhotoImage(file=correctanswerImage)
##### Maine Question is written here. #####
L1 = Label(current_frame, text=question, font=("Helvetica", 35))
L1.grid(columnspan=4, row=0)
global var
var = IntVar()
var.set(0) #Sets the initial radiobutton selection to nothing
def Transliteration():
Transliteration = Label(current_frame, text="'"+arabic+"'" + " is pronounced " + "'"+transliteration+"'", font=("Helvetica", 35))
Transliteration.grid(row=2, columnspan=4)
##### Makes the phonetic pronunciation button. #####
transliteration_button = Button(current_frame, text="Show Transliteration", command=Transliteration)
transliteration_button.grid(column=0, row=4)
choice1 = Radiobutton(current_frame, image=answer1, variable = var, value=1, command= Check_Answer)
choice1.image = answer1 # This prevents python garbage collection from deleting the image pre-maturely.
choice2 = Radiobutton(current_frame, image=answer2, variable = var, value=2, command= Check_Answer)
choice2.image = answer2 # This prevents python garbage collection from deleting the image pre-maturely.
choice3 = Radiobutton(current_frame, image=answer3, variable = var, value=3, command= Check_Answer)
choice3.image = answer3 # This prevents python garbage collection from deleting the image pre-maturely.
choice4 = Radiobutton(current_frame, image=correctanswer, variable = var, value=4, command= Check_Answer)
choice4.image = correctanswer # This prevents python garbage collection from deleting the image pre-maturely.
choices = [choice1, choice2, choice3, choice4]
random.shuffle(choices) #This line of code randomizes the order of the radiobuttons.
choices[0].grid(row=1, column=0)
choices[1].grid(row=1, column=1)
choices[2].grid(row=1, column=2)
choices[3].grid(row=1, column=3)
# Creats the quit button and displays it.
quit_button = Button(current_frame, text = "Quit", command = quit_program)
quit_button.grid(column=4, row=4)
def Check_Answer():
global lives
global score
if str(var.get()) !="4":
Answer_frame.grid_forget()
check_frame.grid(column=1, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.E))
lives -=1
Incorrect = Label(check_frame, text ="That's incorrect!\n Lives: " +str(lives) + "\n Score: " + str(score), font=("Helvetica", 35))
Incorrect.grid(row=0, rowspan=2, column=2, columnspan=2)
if str(var.get()) == "4":
score +=1
check_frame.grid_forget()
Answer_frame.grid(column=1, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.E))
Correct = Label(Answer_frame, text = "That's right!\n Lives: " +str(lives)+ "\n Score: " + str(score), font=("Helvetica", 35))
Correct.grid(row=0, rowspan=2, column=2, columnspan=2)
first_frame_button = Button(Answer_frame, text = "Question 1", command = call_first_frame_on_top)
first_frame_button.grid(column=1, row=3)
second_frame_button = Button(Answer_frame, text = "Question 2", command = call_second_frame_on_top)
second_frame_button.grid(column=2, row=3)
third_frame_button = Button(Answer_frame, text = "Question 3", command = call_third_frame_on_top)
third_frame_button.grid(column=3, row=3)
forth_frame_button = Button(Answer_frame, text = "Question 4", command = call_forth_frame_on_top)
forth_frame_button.grid(column=4, row=3)
def call_first_frame_on_top():
second_frame.grid_forget()
third_frame.grid_forget()
forth_frame.grid_forget()
check_frame.grid_forget()
Answer_frame.grid_forget()
create_widgets_in_first_frame()
first_frame.grid(column=0, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.N, tkinter.E))
def call_second_frame_on_top():
first_frame.grid_forget()
check_frame.grid_forget()
third_frame.grid_forget()
forth_frame.grid_forget()
create_widgets_in_second_frame()
Answer_frame.grid_forget()
print(lives)
if lives <= 0:
quit_program
second_frame.grid(column=0, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.N, tkinter.E))
def call_third_frame_on_top():
check_frame.grid_forget()
first_frame.grid_forget()
second_frame.grid_forget()
forth_frame.grid_forget()
create_widgets_in_third_frame()
Answer_frame.grid_forget()
print(lives)
if lives <= 0:
quit_program
third_frame.grid(column=0, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.N, tkinter.E))
def call_forth_frame_on_top():
check_frame.grid_forget()
first_frame.grid_forget()
second_frame.grid_forget()
third_frame.grid_forget()
create_widgets_in_forth_frame()
Answer_frame.grid_forget()
print(lives)
if lives <= 0:
quit_program
forth_frame.grid(column=0, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.N, tkinter.E))
def quit_program():
root_window.destroy()
##############################
# Main program starts here #
##############################
screen = pygame.display.set_mode((1,1))
Lesson1_FilePath = Root_File_Name + "Lessons\\Lesson_1\\"
ImagePath = Lesson1_FilePath + "Images\\"
# Create the root GUI window.
root_window = Tk()
root_window.title("Lesson 1: Part 1")
# Define window size
window_width = 200
window_heigth = 100
# Create frames inside the root window to hold other GUI elements. All frames must be created in the main program, otherwise they are not accessible in functions.
first_frame=tkinter.Frame(root_window, width=window_width, height=window_heigth)
first_frame['borderwidth'] = 2
first_frame['relief'] = 'sunken'
first_frame.grid(column=0, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.N, tkinter.E))
second_frame=tkinter.Frame(root_window, width=window_width, height=window_heigth)
second_frame['borderwidth'] = 2
second_frame['relief'] = 'sunken'
second_frame.grid(column=0, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.N, tkinter.E))
third_frame=tkinter.Frame(root_window, width=window_width, height=window_heigth)
third_frame['borderwidth'] = 2
third_frame['relief'] = 'sunken'
third_frame.grid(column=0, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.N, tkinter.E))
forth_frame=tkinter.Frame(root_window, width=window_width, height=window_heigth)
forth_frame['borderwidth'] = 2
forth_frame['relief'] = 'sunken'
forth_frame.grid(column=0, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.N, tkinter.E))
check_frame=tkinter.Frame(root_window, width=window_width, height=window_heigth)
check_frame['borderwidth'] = 2
check_frame['relief'] = 'sunken'
check_frame.grid(column=1, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.E))
check_frame.grid_forget()
Answer_frame=tkinter.Frame(root_window, width=window_width, height=window_heigth)
Answer_frame['borderwidth'] = 2
Answer_frame['relief'] = 'sunken'
Answer_frame.grid(column=1, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.E))
Answer_frame.grid_forget()
# Create the firist frame
call_first_frame_on_top()
# Start tkinter event - loop
root_window.mainloop()
Here is my .json file for reference:
{"lesson 1":
{"part one": {
"question1": {
"question": "What is the meaning of 'واد' in English?",
"arabic": "واد",
"transliteration": "walid",
"answer": "boy",
"wronganswer" : [
"girl", "woman", "man", "waiter", "mom",
"priest", "driver", "teacher", "doctor", "grandparents"
]
},
"question2": {
"question": "What is the meaning of 'بنت' in English?",
"arabic": "بنت",
"transliteration": "bint",
"answer": "girl",
"wronganswer" : [
"woman", "man", "waiter", "mom", "priest",
"driver", "teacher", "doctor", "grandparents"
]
},
"question3": {
"question": "What is the meaning of 'رخل' in English?",
"arabic": "رخل",
"transliteration": "ragul",
"answer": "man",
"wronganswer" : [
"girl", "woman", "boy", "waiter", "mom",
"priest", "driver", "teacher", "doctor", "grandparents"
]
},
"question4": {
"question": "What is the meaning of 'ست' in English?",
"arabic": "ست",
"transliteration": "sit",
"answer": "woman",
"wronganswer" : [
"girl", "boy", "man", "waiter", "mom", "priest",
"driver", "teacher", "doctor", "grandparents"
]
}
},
"part two": {
"question1": {
"question": "What is the meaning of '2test1'?",
"transliteration": "phonix",
"answer": "21",
"wronganswer" : [
"test1",
"test2",
"test3"
]
},
"question3": {
"question": "What is the meaning of '2test3'?",
"transliteration": "2test3",
"answer": "23"
}
},
"part three": [
{"question": "What is the meaning of '3test1'?",
"transliteration": "phonix",
"answer": "31"
},
{"question": "What is the meaning of '2test3'?",
"transliteration": "2test3",
"answer": "32"
}
]}
}
Here is a photo of one of the questions:
Any additional programming advice as I continue to code would, of course, be welcome. Thanks!