5
\$\begingroup\$

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: enter image description here

Any additional programming advice as I continue to code would, of course, be welcome. Thanks!

\$\endgroup\$
  • \$\begingroup\$ Is there a reason you don't use arrays and loops? Three false images could be contained in an array and then you loop through to create the false images or show them. This could probably become a function. \$\endgroup\$ – pacmaninbw Sep 19 '18 at 12:33
  • 2
    \$\begingroup\$ At risk of sounding too inexperienced, I've pretty much just learned the necessary steps along the way as my program developed and so I don't really know anything about arrays (though I do know a little about loops). Would an array or loop be able to randomize the wrong answers and avoid showing duplicate wrong answers? \$\endgroup\$ – Jack Duane Sep 19 '18 at 12:38
  • \$\begingroup\$ this is not python but it gets the idea across. for (i = 1; i <= 3; i++) { Images[i] = ImagePath + random.choice(wronganswer)+ ".png; answers[i] = PhotoImage(file=Images[i]); } \$\endgroup\$ – pacmaninbw Sep 19 '18 at 12:49
2
\$\begingroup\$

Seems you already know how to use function, but still don't know how to use function with parameters, this will be a great tool for you to refractor your code.

I will give you some example about how to use function with parameters, list, loop and other useful skill for your code

For example:

def create_widgets_in_first_frame():
    ...
    #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)
    ...

def create_widgets_in_second_frame():
    ...
    #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)
    ...

def create_widgets_in_third_frame():
    ...
    #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)
    ...

def create_widgets_in_forth_frame():
    ...
    #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)
    ...

The structure of your create_widgets_in_xxxx_frame functions are almost the same, and for this part, they are exactly the same, so we can define a function for it and just call it in the create_widgets_in_xxxx_frame functions.

The wronganswer and correctanswerText are variable in the create_widgets_in_xxxx_frame functions, and the ImagePath are constance for the functions, so we define a function with wronganswer and correctanswerText as parameters

def generate_answers(wronganswer, correctanswerText):
    #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)
    return [answer1, answer2, answer3, correctanswer]

This means with different wronganswer and correctanswerText, the function will return different answers, so in all it looks like

def generate_answers(wronganswer, correctanswerText):
    ...

def create_widgets_in_first_frame():
    ...
    answer1, answer2, answer3, correctanswer = generate_answers(wronganswer, correctanswerText)
    ...
...

Now let's have a look at the generate_answers function

    Image1 = ImagePath + random.choice(wronganswer)+ ".png"
    Image2 = ImagePath + random.choice(wronganswer)+ ".png"
    Image3 = ImagePath + random.choice(wronganswer)+ ".png"

these are still exactly same code, how to just write one time and get three different images, now we need loop and array, we will use loop to run same code for 3 times and define a list to gather all these images we get from the loop

Images = []
for i in range(3):# run 3 times
    image = ImagePath + random.choice(wronganswer)+ ".png"
    Images.append(image)

Now in the Images there are three items, we can unpack them using

Image1, Image2, Image3 = Images

Or get particular item, for example get the first item Image1 = Images[0] in python the first item is No.0 item, and etc.

So now the generate_answer function looks like this

def generate_answers(wronganswer, correctanswerText):
    Images = []
    for i in range(3):
        image = ImagePath + random.choice(wronganswer)+ ".png"
        Images.append(image)
    Image1, Image2, Image3 = Images
    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)
    return [answer1, answer2, answer3, correctanswer]

Now let's deal with the answer1, answer2, answer3 They are not that exactly the same, but almost, still we need loop and list for sure, but how to deal with that different ImageX part, then we need Image1 = Images[0] skill

In loop for i in range(3), during the 3 times the loop run i equal 0,1,2, separately

answers = []
for i in range(3):
    answer = PhotoImage(file=Images[i])
    answers.append(answer)

so in finial, generate_answer function turn to be

def generate_answers(wronganswer, correctanswerText):
    Images = []
    for i in range(3):
        image = ImagePath + random.choice(wronganswer)+ ".png"
        Images.append(image)
    correctanswerImage = ImagePath + correctanswerText + ".png"
    answers = []
    for i in range(3):
        answer = PhotoImage(file=Images[i])
        answers.append(answer)
    answer1, answer2, answer3 = answers
    correctanswer = PhotoImage(file=correctanswerImage)
    return [answer1, answer2, answer3, correctanswer]

Keep thinking, answer = PhotoImage(file=Images[i]) and correctanswer = PhotoImage(file=correctanswerImage) are not so different, maybe we can just add correctanswerImage into Images first, and change the loop to for i in range(4) ^^, this will leave to you

ImagePath + random.choice(wronganswer)+ ".png" we can just add path together, but in case that we forgot / or the / and '\' difference for different system, it is better to use os.path.join

and for random.choice(wronganswer)+ ".png", python3.x has format string, more elegant implement f"{random.choice(wronganswer)}.png"

For all, my refractor is as this, just with skills I mentioned

import os
def generate_answers(wronganswer, correctanswerText):
    images = [os.path.join(ImagePath, f"{random.choice(wronganswer)}.png") for _ in range(3)]
    images += [os.path.join(ImagePath, f"{correctanswerText}.png")]
    answers = [PhotoImage(file=images[i]) for i in range(4)]
    random.shuffle(answers)
    return answers

Then about your choices, you can refractor it to be

answers = generate_answers(wronganswer, correctanswerText)
choices = []
for i in range(4):
    choice = Radiobutton(current_frame, image=answers[i], variable = var, value=i+1, command= Check_Answer)
    choice.image = answers[i]
    choices.append(choice)

Also I noticed you used question = questionDirectory.get("question") get is a good practice in dictionary but also, if the "question" not exist in your questionDirectory, the question will return None, I am sure you not wish question to be None to user so setdefault might be better choice

\$\endgroup\$
  • \$\begingroup\$ I'm not sure how to go about implementing this. When I do, I get an error that "correctanswer" isn't defined. Does the function "generate_answers" go inside the "create_widgets_in_first_frame" function or does it stay outside? Could you provide an example of implementing your changes to clarify what you mean? \$\endgroup\$ – Jack Duane Sep 23 '18 at 8:07
  • \$\begingroup\$ I think python is confused by the use of "wronganswer". Did you mean to reference the "wronganswer" that I defined in my code already or were you making something new? \$\endgroup\$ – Jack Duane Sep 23 '18 at 8:23
  • \$\begingroup\$ yeah the "wronganswer" I mean is that one you defined in your code, well where should you put this function, it should go outside, as these parts are duplicated in your create_widgets_in_xxxx_frame function, I will give you better explain \$\endgroup\$ – Aries_is_there Sep 23 '18 at 10:00
  • \$\begingroup\$ the error you got from "correctanswer", that "correctanswer" I meant is the "correctanswerText" you defined, sorry this is my fault, I will fix my answer about it \$\endgroup\$ – Aries_is_there Sep 23 '18 at 10:04
  • \$\begingroup\$ I have update my answer, wish can help you to have better understanding \$\endgroup\$ – Aries_is_there Sep 23 '18 at 11:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.