Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
4.2k views
in Technique[技术] by (71.8m points)

python - How can I make this code for a multiple choice quiz work in Tkinter?

I plan to show the text in a label and then have 4 buttons at the bottom which the user will select

The questions are stored in a notepad file that's quite primitive, and I split each line up by colons to separate the subject, question, correct answer, then finally the 3 incorrect answers

English:What is a proper noun?:A person or a place:An object:A describing word:A doing word

Maths:What is 4 x 9:36:37:28:29

Below is the Python code I need to print the variable question[1] which is the Question Then I need to have 4 buttons which show the following variables, question[2], question[3], question[4], question[5]

I have no idea how to 'tkintify' this, so any help would be really appreciated. My particular struggle is turning variables into 'tkinter variables', especially when creating a button where the "text" parameter is a variable

Thanks in advance

Python code:

f1 = f.readlines()
f.close()

#Asks the user to pick a subject, in the project this
#will use your subject variable from where the user has
#chosen a subject by clicking a button
Subject = input("Pick a subject")

# Creates a blank list
questions = []

# Splits the list up by colon so we can find the subject, question,
# and correct answers. Subject = question[0], question = question[1],
# correct answer = question[2], incorrect answers = question[3] to question[5]
for question in f1:
    sQuestion = question.split(":")
    questions.append(sQuestion)

# Goes through the list of questions and only prints the correct subject
for question in questions:
    if question[0] == Subject:
        answer = input(question[1]+": "+question[2]+", "+question[3]+", "+question[4]+", "+question[5]+"?")
        # Checks to see if the user guess is correct
        if answer==question[2]:
            print("Correct")
        else:
            print("Incorrect")


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

Your question is really, really broad and not really suited to Stackoverflow. However, I'm going to try to give you some general guidelines because I understand how difficult it can be when trying to learn GUI programming.

Overview

Event-based programming is very different than procedural code. Instead of writing top-down code, you need to initialize the UI and then set up functions to respond to events. Those functions can then be used to alter the display.

One of the most talented programmers I ever met gave me the advice that you should always start by thinking about your data structures. If you have designed your data well, the implementation almost becomes self-evident. If you have bad data structures (eg: just a bunch of random variables), it will make the program really hard to write and understand.

Using objects for questions

So, let's start by looking at your questions. They are a line of text that has to be parsed. It would be easier to write the GUI if all of the questions were objects that you could then store in a dictionary by subject.

It doesn't have to be fancy, the definition might look something like this if we can assume that all of your data is properly formatted:

class Question():
    def __init__(self, question_string):
        subject, question,answer1,answer2,answer3,answer4 = question_string.split(":")
        self.subject = subject
        self.question = question
        self.correct_answer = answer1
        self.answers = (answer1, answer2, answer3, answer4)

To create a single question you could pass it one line from your file. Once you have the object, it becomes easy to get the subject, the question text, the correct answer, and the possible answers.

>>> q = Question("English:What is a proper noun?:A person or a place:An object:A describing word:A doing word")
>>> print(q.question)
What is a proper noun?
>>> print(q.correct_answer)
A person or a place
>>> print(q.answers)
('A person or a place', 'An object', 'A describing word', 'A doing word')

With this, you can loop over the lines of your file, creating one object for each line. You can store the questions in a list or in a dictionary organized by subject. Since you said you want the user to pick a subject, creating a dictionary with a key for each subject makes the most sense.

That might look something like this:

questions = {"English": [], "Maths": []}
for row in f.readlines():
    question = Question(row)
    questions[question.subject].append(question)

Create the GUI

The general idea is to create the widgets beforehand, and then update the widgets when a question is chosen. For this, we'll create a frame to hold a question, and in that frame will be one label for the question and four radio buttons for the answers. Radio buttons are the right UI choice since they represent an exclusive choice. There will also be a label to display whether the answer was correct or not.

First, create the widgets to display a single question and its answers.

This code assumes that you've previously created a Frame named question_frame. Putting the widgets in a separate frame will make it easier to add additional widgets to your GUI in the future.

Radiobuttons need to share one of the tkinter variables (StringVar, IntVar, etc). This is how tkinter knows that only one can be selected at a time, and it provides a mechanism for getting the selected value. We'll use an IntVar and name it answer_var.

answer_var = tk.StringVar()
question_label = tk.Label(question_frame, width=80, anchor="w")
answer_radiobuttons = (
    tk.Radiobutton(question_frame, anchor="w", variable=answer_var, value=0),
    tk.Radiobutton(question_frame, anchor="w", variable=answer_var, value=1),
    tk.Radiobutton(question_frame, anchor="w", variable=answer_var, value=2),
    tk.Radiobutton(question_frame, anchor="w", variable=answer_var, value=3),
)

This creates a label to show "correct" or "incorrect"

result_label = tk.Label(root)

Finally, we need to arrange the widgets in the frame

pack is used here because of its simplicity. You could use grid if you prefer.

question_label.pack(side="top", fill="x", anchor="w")
result_label.pack(side="bottom", fill="x")
for button in answer_radiobuttons:
    button.pack(side="top", fill="x", expand=True, anchor="w")

Create a function to show a question

Now that there are widgets to display a question and its answers, and now that we have some question objects, we need to write a function that can display a single question and its answers. We can do that by using the configure method of a widget which allows a widget to be modified after it has been created.

In the following example the answers are randomized so that the user can't get a correct answer just by selecting the first radiobutton on every question. Also, we'll use a global variable to keep track of the current question.

def show_question(question):
    global current_question
    current_question = question

    # configure the question label to display the question
    question_label.configure(text=question.question)

    # configure the radiobuttons to hold the answers
    answers = list(question.answers)
    random.shuffle(answers)
    for i in range(4):
        answer_radiobuttons[i].configure(text=answers[i])

    answer_var.set(-1)  # causes buttons to show as unselected

With that, you can show any question by passing it to show_question. You could, for example, have a "next" button that picks a question from a list of question and then shows it on the screen.

Create a function to check the answer

Now we have a data structure to hold questions, and a way to display a question. Now we need a way to validate the results. Radiobutton widgets accept a command attribute which could be used for this purpose, or you could have a "check answer" button, or perhaps you could call the function when the user tries to click a "next question" button. Regardless, the point is to create a function that does the checking. How it is triggered is up to you.

We know that the radiobuttons are tied to a single variable named answer_var, and that variable has a get method which returns the value. We also have the radiobuttons in a list so we can get the text of the selected radiobutton with the cget method. With that, we can compare it to the correct answer of the current question object.

It would look something like this:

def check_answer():
    choice = answer_var.get()
    answer = answer_radiobuttons[choice].cget("text")
    if answer == current_question.correct_answer:
        result_label.configure(text="Correct")
    else:
        result_label.configure(text="Incorrect")

As an example of how to use this, we could ask that a radiobutton call this function whenever it is configured. We can do that by changing how the radiobuttons are created, by adding a command attribute:

answer_radiobuttons = (
    tk.Radiobutton(main_frame, anchor="w", variable=answer_var, value=0, command=check_answer),
    tk.Radiobutton(main_frame, anchor="w", variable=answer_var, value=1, command=check_answer),
    tk.Radiobutton(main_frame, anchor="w", variable=answer_var, value=2, command=check_answer),
    tk.Radiobutton(main_frame, anchor="w", variable=answer_var, value=3, command=check_answer),
)

Summary

I've left a lot of details out, but that's because there's no one best way to do this and your question was very broad. However, the general pattern applies to most GUI programs:

  • initialize your data
  • initialize the display
  • update the display by reacting to what the user does

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to OStack Knowledge Sharing Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...