0

Using the method here Switch between two frames in tkinter I want to stack the same frame 10 times to create a 10 question quiz, so far i have made this work by copying and pasting the frame 10 times and changing the name, however i am hoping there is an easier more efficient way to do this using some sort of loop. I have included a extract of the code below, thanks for any help.

from tkinter import *




class secondaryActivity(Tk):
    def __init__(self, *args, **kwargs):
        Tk.__init__(self,*args,**kwargs)


        container=Frame(self)
        container.pack(side="top", fill="both", expand=True)

        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames={}

        for F in (quiz, quiz1):
            frame = F(container, self)
            pageName = F.__name__
            self.frames[pageName]=frame
            frame.grid(row=0, column=0, sticky="snew")
        self.showFrame("quiz")

    def showFrame(self, pageName):
        frame=self.frames[pageName]
        frame.tkraise()


class quiz(Frame):
    def __init__(self, parent, controller):
        Frame.__init__(self, parent)
        self.controller=controller



        self.label1= Label(self, text="this is quiz")
        self.label1.pack()

        self.submitBtn = Button(self, text="submit", command=self.submitBtnClicked)
        self.submitBtn.pack()

    def submitBtnClicked(self):
        self.controller.showFrame("quiz1")




class quiz1(Frame):
    def __init__(self, parent, controller):
        Frame.__init__(self, parent)
        self.controller=controller

        self.label1= Label(self, text="this is quiz1")
        self.label1.pack()

        self.submitBtn = Button(self, text="submit", command=self.submitBtnClicked)
        self.submitBtn.pack()

    def submitBtnClicked(self):
       self.controller.showFrame("quiz")

app = secondaryActivity()
app.mainloop()
  • 1
    There are a couple methods you can use to do this that I can think of off the top of my head. You could create a class that creates a frame based off of your Q's and A's or you can simple just update the Questions and answer options within the same frame. – Mike - SMT Apr 20 '18 at 12:57

2 Answers2

1

There's no reason to create a bunch of classes or frames in a loop. Just store your questions and answers in a simple array, and refresh the display based on the data in that array. You just need a single frame to display one question at a time.

Start by creating a class to hold your questions and other data. I'm assuming this is a multiple choice quiz, so you need a list of choices and a pointer to which one is correct.

class Question():
    def __init__(self, question, choices, answer_index):
        self.question = question
        self.choices=choices
        self.answer_index = answer_index

Next, define your questions:

questions = (
    Question("Question 1?", choices=("A. ...", "B. ...", "C. ...", answer_index=0)),
    Question("Question 2?", choices=("A. ...", "B. ...", "C. ...", answer_index=3)),
    Question("Question 3?", choices=("A. ...", "B. ...", "C. ...", answer_index=2)),
    Question("Question 4?", choices=("A. ...", "B. ...", "C. ...", answer_index=2)),
}

Finally, create a function that given an index will render that question:

def show_question(index):
    """Show the question at the given index in 'questionFrame'"""
    # destroy old widgets
    # (you can skip if all questions have the same number of choices)
    for child in questionFrame.children():
        child.destroy()

    question - questions[index]
    # create widgets for current question
    # (or configure existing widgets if all questions have the same
    #  number of choices)
    question_label = tk.Label(questionFrame, text=question.question)
    for choice in question.choices:
        radiobutton = tk.Radiobutton(...)
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • I think you mean `question = questions[index]` and later you want another name for the label, not `question` otherwise there will be no `question.choices`... – progmatico Apr 20 '18 at 14:02
0

I personally would use a dictionary to hold all my questions and answers. I would work with this dictionary to produce each question and record the results.

I have moved the frame creation to the main class and then created a class method that would update the frame with the desired content. This is just one method of doing things and it can be converted to its on class if you wanted.

I then created a method that will check and save the results of each answer and on the last answer submitted it will check the total score and display it as a pop up message.

Take a look at the below example:

import tkinter as tk
from tkinter import messagebox

questions_and_answers = {"Question 1":{"question":"What is 1+1","key":"2", "answer":""},
                         "Question 2":{"question":"What is 1+4","key":"5", "answer":""},
                         "Question 3":{"question":"What is 9 \\ 3","key":"3", "answer":""},
                         "Question 4":{"question":"What is 4-2","key":"2", "answer":""}}

list_questions = ["Question 1", "Question 2", "Question 3", "Question 4"]


class secondaryActivity(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self,*args,**kwargs)
        self.container=tk.Frame(self)
        self.container.pack(side="top", fill="both", expand=True)
        self.container.grid_rowconfigure(0, weight=1)
        self.container.grid_columnconfigure(0, weight=1)

        self.label1= tk.Label(self.container, text="Press the start button to begin quiz!")
        self.label1.grid(row=0, column=0)
        self.label2 = tk.Label(self.container, text="")
        self.entry1 = tk.Entry(self.container)

        self.submitBtn = tk.Button(self.container, text="start", command=lambda: self.quiz(self.list_questions[0]))
        self.submitBtn.grid(row=3, column=0)

    def quiz(self, question_name):
        self.label1.config(text=question_name)
        self.label1.grid(row=0, column=0)
        self.label2.grid(row=1, column=0)
        self.label2.config(text=questions_and_answers[question_name]["question"])
        self.entry1.delete(0, "end")
        self.entry1.grid(row=2, column=0)

        self.submitBtn.config(text="Submit", command=lambda x=question_name: self.check_and_continue(x))

    def check_and_continue(self, question_name):
        answer = self.entry1.get().strip()
        correct_answer = questions_and_answers[question_name]["key"]

        if answer == correct_answer:
            messagebox.showinfo("Result", "Correct the answer is {}".format(answer))
            questions_and_answers[question_name]["answer"] = "correct"
        else:
            messagebox.showinfo("Result", "Incorrect the answer is {}".format(correct_answer))
            questions_and_answers[question_name]["answer"] = "incorrect"

        for ndex, name in enumerate(list_questions):
            if name == question_name:
                try:
                    self.quiz(list_questions[(ndex+1)])
                except:
                    x = len(list_questions)
                    y = 0
                    for question in questions_and_answers:
                        if questions_and_answers[question]["answer"] == "correct":
                            y += 1
                    messagebox.showinfo("Quiz Info", "You have completed all of the questions in this quiz.\nFinal score is: {} out of {}".format(y, x))
                    self.label1.config(text="Press the start button to begin quiz!")
                    self.submitBtn.config(text="start", command=lambda: self.quiz(self.list_questions[0]))
                    self.entry1.grid_forget()
                    self.label2.grid_forget()


app = secondaryActivity()
app.mainloop()
Mike - SMT
  • 14,784
  • 4
  • 35
  • 79