Как создать следующую кнопку с помощью tkinter для переключения между несколькими кадрами - PullRequest
0 голосов
/ 21 октября 2018

Я пишу программу для викторин в tkinter, в которой есть рамка для каждого задаваемого вопроса.После того, как пользователь выберет правильный ответ из списка радиокнопок, появится окно, позволяющее пользователю выбрать любой из вопросов в викторине.Вот пример: enter image description here

Вместо того, чтобы показывать все возможные ответы, я хотел бы иметь только кнопки «следующий» и «предыдущий», но у меня естьсложность создания кнопки, которая будет автоматически обновляться каждый раз при выборе следующей или предыдущей кнопки.

Вот мой полный код ниже для справки.Позже я выделил важные части, чтобы сделать его немного легче.

Root_File_Name = "C:\\LearningArabic\\LiblibArriby\\"
def Part1():
    JSON_File = Root_File_Name + "Lessons\\Lesson_1\\"
    with open(JSON_File+"Arabic_Lesson_1.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=frames[0]               #Make the frame number generic so as to make copy/paste easier. ##UPDATE PER QUESTION##
        question_number = "question0"
        question_frame_populator(current_frame, question_number)

    def create_widgets_in_second_frame():
        current_frame=frames[1]               #Make the frame number generic so as to make copy/paste easier. ##UPDATE PER QUESTION##
        question_number = "question1"
        question_frame_populator(current_frame, question_number)

    def create_widgets_in_third_frame():
        current_frame=frames[2]               #Make the frame number generic so as to make copy/paste easier
        question_number = "question2"      
        question_frame_populator(current_frame, question_number)

    def create_widgets_in_forth_frame():
        current_frame=frames[3]               #Make the frame number generic so as to make copy/paste easier
        question_number = "question3"
        question_frame_populator(current_frame, question_number)

    def create_widgets_in_fifth_frame():
        current_frame=frames[4]               #Make the frame number generic so as to make copy/paste easier
        question_number = "question4"
        question_frame_populator(current_frame, question_number)

    def create_widgets_in_sixth_frame():
        current_frame=frames[5]               #Make the frame number generic so as to make copy/paste easier
        question_number = "question5"
        question_frame_populator(current_frame, question_number)

    def question_frame_populator(current_frame, question_number): #This is what displayes all of the information on the frame
        questionDirectory = data["lesson 1"]["part one"][question_number]       ##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            
        correctanswer = 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 var
        var = IntVar()
        var.set(0)  #Sets the initial radiobutton selection to nothing

        answers = generate_answers(wronganswer, correctanswer)  #Pulls answers generated from the "generate_answers" function
        choices = []
        for i in range(3):
            choice = Radiobutton(current_frame, image=answers[i], variable = var, value=i+1, command= Check_Answer)
            choice.image = answers[i]
            choices.append(choice)

        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)

        L1 = Label(current_frame, text=question, font=("Helvetica", 35))    #This displays the question at the top of the screen
        L1.grid(columnspan=4, row=0)

        transliteration_button = Button(current_frame, text="Show Transliteration", command= lambda: Transliteration(current_frame, arabic, transliteration))    # Makes the phonetic pronunciation button. #####
        transliteration_button.grid(column=0, row=2)

        Previous_Button() # Creates the "previous" button and displays it.
        Next_Button()  # Creates the "next" button and displays it.
        Quit_Button(current_frame)  # Creates the "quit" button and displays it.

    def Transliteration(current_frame, arabic, transliteration):
        Transliteration = Label(current_frame, text="'"+arabic+"'" + " is pronounced " + "'"+transliteration+"'", font=("Helvetica", 35))
        Transliteration.grid(row=3, columnspan=4)

    def generate_answers(wronganswer, correctanswer):
        Wans=random.sample(wronganswer, 2)
        images = [os.path.join(ImagePath, f"{Wans[i]}.png") for i in range(2)]
        images += [os.path.join(ImagePath, f"{correctanswer}.png")]
        answers = [PhotoImage(file=images[i]) for i in range(3)]
        return answers

    def Check_Answer():
        global lives
        global score

        if str(var.get()) !="3":
            special_frames[1].grid_forget() #This is the frame for right answers
            special_frames[0].grid(column=1, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.E)) #This is the frame for wrong answers
            lives -=1

            Incorrect = Label(special_frames[0], text ="That's incorrect!\n Lives: " +str(lives) + "\n Score: " + str(score), font=("Helvetica", 35))
            Incorrect.grid(row=0, rowspan=2, column=0, columnspan=3)

        if str(var.get()) == "3":
            score +=1

            special_frames[0].grid_forget() #This is the frame for wrong answers
            special_frames[1].grid(column=1, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.E)) #This is the frame for right answers

            Correct = Label(special_frames[1], text = "    That's right!    \n Lives: " +str(lives)+ "\n Score: " + str(score), font=("Helvetica", 35))
            Correct.grid(row=0, rowspan=2, column=0, columnspan=5)
            first_frame_button = Button(special_frames[1], text = "Question 1", command = call_frame_1)
            first_frame_button.grid(column=0, row=3)
            second_frame_button = Button(special_frames[1], text = "Question 2", command = call_frame_2)
            second_frame_button.grid(column=1, row=3)
            third_frame_button = Button(special_frames[1], text = "Question 3", command = call_frame_3)
            third_frame_button.grid(column=2, row=3)
            forth_frame_button = Button(special_frames[1], text = "Question 4", command = call_frame_4)
            forth_frame_button.grid(column=4, row=3)
            fifth_frame_button = Button(special_frames[1], text = "Question 5", command = call_frame_5)
            fifth_frame_button.grid(column=0, row=4)                
            sixth_frame_button = Button(special_frames[1], text = "Question 6", command = call_frame_6)
            sixth_frame_button.grid(column=1, row=4)

    def all_frames_forget():
        for i in range(6):  #This is for question frames
            frames[i].grid_forget()
        for i in range(3):  #This is for special frames, like the correct and incorrect answer frames
            special_frames[i].grid_forget()

    def check_remaining_lives(create_widgets_in_current_frame, current_frame):
        if lives<= 0:
            Zero_Lives()
        else:
            create_widgets_in_current_frame
            current_frame.grid(column=0, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.N, tkinter.E))

    def Zero_Lives():
        special_frames[2].grid(column=0, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.E))

        L5 = Label(special_frames[2], text="You have no remaining lives. \nPlease quit the lesson and try again.", font=("Helvetica", 35))
        L5.grid(columnspan=4, row=0)

        quit_button = Button(special_frames[2], text = "Quit", command = root_window.destroy)
        quit_button.grid(column=1, columnspan = 2, row=2)

    def Previous_Button():
        previous_button = Button(special_frames[1], text = "Previous", command = previous_question)
        previous_button.grid(column=1, row=5)

    def previous_question():
        global frameNumber
        frameNumber -=1
        call_frame_frameNumber()

    def Next_Button():
        next_button = Button(special_frames[1], text = "Next", command = next_question)
        next_button.grid(column=2, row=5)

    def next_question():
        global frameNumber
        frameNumber +=1
        call_frame_frameNumber()

    def Quit_Button(current_frame):
        quit_button = Button(current_frame, text = "Quit", command = quit_program)
        quit_button.grid(column=4, row=4)

    def quit_program():
        root_window.destroy()

    def call_frame_1():
        all_frames_forget()
        create_widgets_in_current_frame = create_widgets_in_first_frame() #This line is unique
        current_frame = frames[0]     #This line is unique
        check_remaining_lives(create_widgets_in_current_frame, current_frame)

    def call_frame_2():
        all_frames_forget()
        create_widgets_in_current_frame = create_widgets_in_second_frame() #This line is unique
        current_frame = frames[1] #This line is unique         
        check_remaining_lives(create_widgets_in_current_frame, current_frame)

    def call_frame_3():
        all_frames_forget()
        create_widgets_in_current_frame = create_widgets_in_third_frame() #This line is unique
        current_frame = frames[2] #This line is unique         
        check_remaining_lives(create_widgets_in_current_frame, current_frame)

    def call_frame_4():
        all_frames_forget()
        create_widgets_in_current_frame = create_widgets_in_forth_frame() #This line is unique
        current_frame = frames[3] #This line is unique         
        check_remaining_lives(create_widgets_in_current_frame, current_frame)

    def call_frame_5():
        all_frames_forget()
        create_widgets_in_current_frame = create_widgets_in_fifth_frame() #This line is unique
        current_frame = frames[4] #This line is unique         
        check_remaining_lives(create_widgets_in_current_frame, current_frame)

    def call_frame_6():
        all_frames_forget()
        create_widgets_in_current_frame = create_widgets_in_sixth_frame() #This line is unique
        current_frame = frames[5] #This line is unique         
        check_remaining_lives(create_widgets_in_current_frame, current_frame)       

    ##### Program starts here  #####
    Lesson1_FilePath = Root_File_Name + "Lessons\\Lesson_1\\"
    ImagePath = Lesson1_FilePath + "Images\\"

    root_window = Tk() # Create the root GUI window.
    root_window.title("Lesson 1: Part 1") # Label the root GUI window.

    global score
    score = 0               #Setting the initial score to zero.
    global lives
    lives = 3               #Setting the initial number of lives.
    global frameNumber
    frameNumber = 1

    window_width = 200      # Define window size
    window_heigth = 100

    frames = []  # This includes frames for all questions
    for i in range(6):
        frame=tkinter.Frame(root_window, width=window_width, height=window_heigth)
        frame['borderwidth'] = 2
        frame['relief'] = 'sunken'
        frame.grid(column=0, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.N, tkinter.E))
        frames.append(frame)

    special_frames=[] #This includes the frames for: wrong answers, right answers, and zero lives
    for i in range(3):
        special=tkinter.Frame(root_window, width=window_width, height=window_heigth)
        special['borderwidth'] = 2
        special['relief'] = 'sunken'
        special.grid(column=1, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.E))
        special.grid_forget()
        special_frames.append(special)

    call_frame_1() #Calls the first function which creates the firist frame

    root_window.mainloop()

Я знаю, что это много кода, но я хотел убедиться, что все было там на случай, если что-то не имеет смысла для васвсе.Вот части, которые я считаю наиболее важными:

    def Previous_Button():
        previous_button = Button(special_frames[1], text = "Previous", command = previous_question)
        previous_button.grid(column=1, row=5)

    def previous_question():
        global frameNumber
        frameNumber -=1
        call_frame_frameNumber()

    def Next_Button():
        next_button = Button(special_frames[1], text = "Next", command = next_question)
        next_button.grid(column=2, row=5)

    def next_question():
        global frameNumber
        frameNumber +=1
        call_frame_frameNumber()

    global frameNumber
    frameNumber = 1

Проблема состоит в том, что python не распознает "call_frame_frameNumber" как "call_frame_X", где X изменяется при каждом нажатии кнопки.Я получаю сообщение об ошибке: NameError: имя 'call_frame_frameNumber' не определено

Я знаю, что определение глобальных переменных не лучшая практика, но это все, что я действительно знаю на данный момент.Если вы можете показать лучший способ, я более чем готов учиться!

Ответы [ 2 ]

0 голосов
/ 21 октября 2018

После просмотра некоторых комментариев @PurpleIce и попытки дальнейшей очистки моего кода я нашел решение своей проблемы.

Я изменил код, чтобы не было уникального call_frame_X для каждого вопроса, а был только один общий call_frame с переменной frameNumber, как показано ниже.

    def Next_Button():
        next_button = Button(special_frames[1], text = "Next Question", command = next_question)
        next_button.grid(column=0, columnspan=5, row=3)

    def next_question():
        global frameNumber
        frameNumber +=1
        call_frame(frameNumber)

Тогда вместо того, чтобы иметьнесколько create_widgets_in_X_frame, я создал одну общую функцию, которая будет повторяться каждый раз при нажатии кнопки «Далее».

    def create_widgets_function(frameNumber):
        current_frame=frames[frameNumber]               #Make the frame number generic so as to make copy/paste easier. ##UPDATE PER QUESTION##

        question_number = "question"+ str(frameNumber)
        question_frame_populator(current_frame, question_number)

    def call_frame(frameNumber):
        all_frames_forget()
        create_widgets_in_current_frame =create_widgets_function(frameNumber)
        current_frame = frames[frameNumber]
        check_remaining_lives(create_widgets_in_current_frame, current_frame)

Это помогло уменьшить / консолидировать большую часть моего кода, одновременно давая мне кнопку «Далее».

Вот обновленный код: def Part1 ():

    JSON_File = Root_File_Name + "Lessons\\Lesson_1\\"
    with open(JSON_File+"Arabic_Lesson_1.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=frames[0]               #Make the frame number generic so as to make copy/paste easier. ##UPDATE PER QUESTION##
        question_number = "question0"

        question_frame_populator(current_frame, question_number)

    def question_frame_populator(current_frame, question_number): #This is what displayes all of the information on the frame
        questionDirectory = data["lesson 1"]["part one"][question_number]       ##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            
        correctanswer = 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 var
        var = IntVar()
        var.set(0)  #Sets the initial radiobutton selection to nothing

        answers = generate_answers(wronganswer, correctanswer)  #Pulls answers generated from the "generate_answers" function
        choices = []
        for i in range(3):
            choice = Radiobutton(current_frame, image=answers[i], variable = var, value=i+1, command= Check_Answer)
            choice.image = answers[i]
            choices.append(choice)

        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)

        L1 = Label(current_frame, text=question, font=("Helvetica", 35))    #This displays the question at the top of the screen
        L1.grid(columnspan=4, row=0)

        transliteration_button = Button(current_frame, text="Show Transliteration", command= lambda: Transliteration(current_frame, arabic, transliteration))    # Makes the phonetic pronunciation button. #####
        transliteration_button.grid(column=0, row=2)

        #Previous_Button() # Creates the "previous" button and displays it.
        Next_Button()  # Creates the "next" button and displays it.
        Quit_Button(current_frame)  # Creates the "quit" button and displays it.

    def Transliteration(current_frame, arabic, transliteration):
        Transliteration = Label(current_frame, text="'"+arabic+"'" + " is pronounced " + "'"+transliteration+"'", font=("Helvetica", 35))
        Transliteration.grid(row=3, columnspan=4)

    def generate_answers(wronganswer, correctanswer):
        Wans=random.sample(wronganswer, 2)
        images = [os.path.join(ImagePath, f"{Wans[i]}.png") for i in range(2)]
        images += [os.path.join(ImagePath, f"{correctanswer}.png")]
        answers = [PhotoImage(file=images[i]) for i in range(3)]
        return answers

    def Check_Answer():
        global lives
        global score

        if str(var.get()) !="3":
            special_frames[1].grid_forget() #This is the frame for right answers
            special_frames[0].grid(column=1, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.E)) #This is the frame for wrong answers
            lives -=1

            Incorrect = Label(special_frames[0], text ="That's incorrect!\n Lives: " +str(lives) + "\n Score: " + str(score), font=("Helvetica", 35))
            Incorrect.grid(row=0, rowspan=2, column=0, columnspan=3)

        if str(var.get()) == "3":
            score +=1

            special_frames[0].grid_forget() #This is the frame for wrong answers
            special_frames[1].grid(column=1, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.E)) #This is the frame for right answers

            Correct = Label(special_frames[1], text = "    That's right!    \n Lives: " +str(lives)+ "\n Score: " + str(score), font=("Helvetica", 35))
            Correct.grid(row=0, rowspan=2, column=0, columnspan=5)

    def all_frames_forget():
        for i in range(6):  #This is for question frames
            frames[i].grid_forget()
        for i in range(3):  #This is for special frames, like the correct and incorrect answer frames
            special_frames[i].grid_forget()

    def check_remaining_lives(create_widgets_in_current_frame, current_frame):
        if lives<= 0:
            Zero_Lives()
        else:
            create_widgets_in_current_frame
            current_frame.grid(column=0, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.N, tkinter.E))

    def Zero_Lives():
        special_frames[2].grid(column=0, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.E))

        L5 = Label(special_frames[2], text="You have no remaining lives. \nPlease quit the lesson and try again.", font=("Helvetica", 35))
        L5.grid(columnspan=4, row=0)

        quit_button = Button(special_frames[2], text = "Quit", command = root_window.destroy)
        quit_button.grid(column=1, columnspan = 2, row=2)

    def Quit_Button(current_frame):
        quit_button = Button(current_frame, text = "Quit", command = quit_program)
        quit_button.grid(column=4, row=2)

    def quit_program():
        root_window.destroy()

    def Next_Button():
        next_button = Button(special_frames[1], text = "Next Question", command = next_question)
        next_button.grid(column=0, columnspan=5, row=3)

    def next_question():
        global frameNumber
        frameNumber +=1

        call_frame(frameNumber)

    def create_widgets_function(frameNumber):
        current_frame=frames[frameNumber]               #Make the frame number generic so as to make copy/paste easier. ##UPDATE PER QUESTION##

        question_number = "question"+ str(frameNumber)
        question_frame_populator(current_frame, question_number)

    def call_frame(frameNumber):
        all_frames_forget()
        create_widgets_in_current_frame =create_widgets_function(frameNumber)
        current_frame = frames[frameNumber]
        check_remaining_lives(create_widgets_in_current_frame, current_frame)

    def call_frame_1():
        all_frames_forget()
        create_widgets_in_current_frame = create_widgets_in_first_frame() #This line is unique
        current_frame = frames[0]     #This line is unique
        check_remaining_lives(create_widgets_in_current_frame, current_frame)        

    ##### Program starts here  #####
    Lesson1_FilePath = Root_File_Name + "Lessons\\Lesson_1\\"
    ImagePath = Lesson1_FilePath + "Images\\"

    root_window = Tk() # Create the root GUI window.
    root_window.title("Lesson 1: Part 1") # Label the root GUI window.

    global score
    score = 0               #Setting the initial score to zero.
    global lives
    lives = 3               #Setting the initial number of lives.
    global frameNumber
    frameNumber = 1

    window_width = 200      # Define window size
    window_heigth = 100

    frames = []  # This includes frames for all questions
    for i in range(6):
        frame=tkinter.Frame(root_window, width=window_width, height=window_heigth)
        frame['borderwidth'] = 2
        frame['relief'] = 'sunken'
        frame.grid(column=0, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.N, tkinter.E))
        frames.append(frame)

    special_frames=[] #This includes the frames for: wrong answers, right answers, and zero lives
    for i in range(3):
        special=tkinter.Frame(root_window, width=window_width, height=window_heigth)
        special['borderwidth'] = 2
        special['relief'] = 'sunken'
        special.grid(column=1, row=0, padx=20, pady=5, sticky=(tkinter.W, tkinter.E))
        special.grid_forget()
        special_frames.append(special)

    call_frame_1() #Calls the first function which creates the firist frame

    root_window.mainloop() 
0 голосов
/ 21 октября 2018

Это не очень хорошая практика кодирования, так как количество кадров будет увеличиваться, но вы можете просто использовать несколько операторов 'if' для вызова каждой функции.

Например:

if frameNumber == 1:
    call_frame_1()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...