← Apps with Gradio

A quiz app

Keep a quiz's questions as data, make a Radio input for each one with a loop, and write the function that marks the answers and gives feedback.

Quiz apps, flashcards and online tests are all the same idea: questions stored as data, and code that checks the answers.

A quiz is data: each question has its text, its choices and its answer. Here each question is a dictionary, which looks a value up by its name (its key): q["answer"]. The questions are in a list, so a loop can make one Radio input for each (inputs.append(...) adds it to the end of the inputs list), and the marking function can check every answer the same way.

PredictRead the code and say what it will do before you run anything. A wrong guess is fine: it shows you what to look for.

Predict

quiz.py
questions = [
    {"q": "How many legs does a spider have?", "answer": "8"},
    {"q": "What is 7 x 8?", "answer": "56"},
]
answers = ["8", "54"]
score = 0
for i in range(len(questions)):
    if answers[i] == questions[i]["answer"]:
        score = score + 1
print(score)

The loop compares answers[0] with question 0's answer, then answers[1] with question 1's. How many match?

What does it print?

RunRun it and compare the result with your prediction.

Run

Run it, answer the questions and press Submit. Get one wrong on purpose and read the feedback. Then change a question's text in the list and run again: the app changes with the data.

quiz.py
import gradio as gr

questions = [
    {"q": "Which planet is closest to the Sun?", "choices": ["Venus", "Mercury", "Mars"], "answer": "Mercury"},
    {"q": "How many legs does a spider have?", "choices": ["6", "8", "10"], "answer": "8"},
    {"q": "What is 7 x 8?", "choices": ["54", "56", "64"], "answer": "56"},
]


def mark(a1, a2, a3):
    answers = [a1, a2, a3]
    score = 0
    feedback = ""
    for i in range(len(questions)):
        q = questions[i]
        if answers[i] == q["answer"]:
            score = score + 1
            feedback = feedback + "Right: " + q["q"] + "\n"
        else:
            feedback = feedback + "Not quite: " + q["q"] + " The answer is " + q["answer"] + ".\n"
    return str(score) + " out of " + str(len(questions)), feedback


inputs = []
for q in questions:
    inputs.append(gr.Radio(q["choices"], label=q["q"]))

gr.Interface(
    fn=mark,
    inputs=inputs,
    outputs=[gr.Textbox(label="Score"), gr.Textbox(label="Feedback")],
    title="Quick quiz",
).launch()
Try it yourselfYour edits are saved in this browser.

InvestigateTrace how each line of code connects to what you see.

Investigate

quiz.py
import gradio as gr

questions = [
    {"q": "Which planet is closest to the Sun?", "choices": ["Venus", "Mercury", "Mars"], "answer": "Mercury"},
    {"q": "How many legs does a spider have?", "choices": ["6", "8", "10"], "answer": "8"},
    {"q": "What is 7 x 8?", "choices": ["54", "56", "64"], "answer": "56"},
]


def mark(a1, a2, a3):
    answers = [a1, a2, a3]
    score = 0
    feedback = ""
    for i in range(len(questions)):
        q = questions[i]
        if answers[i] == q["answer"]:
            score = score + 1
            feedback = feedback + "Right: " + q["q"] + "\n"
        else:
            feedback = feedback + "Not quite: " + q["q"] + " The answer is " + q["answer"] + ".\n"
    return str(score) + " out of " + str(len(questions)), feedback


inputs = []
for q in questions:
    inputs.append(gr.Radio(q["choices"], label=q["q"]))

gr.Interface(
    fn=mark,
    inputs=inputs,
    outputs=[gr.Textbox(label="Score"), gr.Textbox(label="Feedback")],
    title="Quick quiz",
).launch()

One Radio per question.

The chosen text.

A score that stops at 1.

Where do the three Radio inputs come from?

What does mark get as a1 when the first question is answered Mercury?

The score is never more than 1, even with every answer right. Click the line that is wrong.

ModifyChange the code on purpose and check the result.

Modify

Add a question of your own. What else has to change?

Need a hint?

Add a fourth dictionary to the list, then a fourth parameter: def mark(a1, a2, a3, a4): and answers = [a1, a2, a3, a4].

Add a message to the feedback when every answer is right.

Need a hint?

Before the return: if score == len(questions): add "Perfect score!" to the feedback.

A question left blank gives None (Python's word for "no value"). Say it was not answered, instead of "Not quite".

Need a hint?

A Radio with nothing chosen gives None. Check if answers[i] is None: first, and say the question was not answered.

Try it yourselfYour edits are saved in this browser.

Add a fourth question.

    Run your program. This ticks when your code does all of the above.

    Add a message for a perfect score.

      Run your program. This ticks when your code does all of the above.

      Say which questions were not answered, instead of marking them wrong.

        Run your program. This ticks when your code does all of the above.

        MakeBuild something new using what you have learned. Expect a few tries; that is normal.

        Your own quiz

        Make a quiz on something you know well (a sport, a game, a subject, music) with at least four questions and feedback that helps someone learn.

        Swap with a classmate. Fix one thing they found confusing.

        Try it yourselfYour edits are saved in this browser.

        Make a quiz of at least four questions on your own topic, with a Radio for each made by a loop.

          Run your program. This ticks when your code does all of the above.

          Info

          Where this leads

          On a computer this same file is a real quiz app, and a teacher can host it as a Hugging Face Space for the class. The optional next lesson stores each question as an object with its own check method.

          Your teacher wants a quiz with 30 questions. What is the main problem with mark(a1, a2, ..., a30)?

          For teachers

          Students build a quiz app from a list of question dictionaries, make a Radio input for each question with a loop, mark the answers with feedback, then add a question, a perfect-score message and handling for blanks, and make and user-test a quiz of their own. About 50 minutes.

          Open the teacher guide: answers, solutions, slides and a worksheet.

          Students will be able to

          Before class

          Read the unit primer first. Each question is a dictionary (q["q"], q["choices"], q["answer"]), introduced here only as far as reading one. The inputs list starts empty and a loop appends a gr.Radio per question; mark takes one parameter per question (a1, a2, a3) and puts them in a list to compare by position. A Radio gives the chosen text, or None when nothing is chosen (the third Modify task). The bug task returns inside the loop, so only the first question is ever marked. The info question points at *answers (any number of inputs as a list) as a stretch. The Make ends with user testing: a classmate takes the quiz.

          Running the lesson

          1. Predict: trace i through the loop on the board with the two answers.
          2. Run: change a question in the list and run again; the app follows the data.
          3. Investigate: count the inputs, the questions and the parameters: three of each.
          4. Make: pairs swap quizzes and report one confusing thing.

          The hard part (on purpose)

          Marking by position: answers[i] against questions[i]. Ask students to trace i for two questions on paper.

          When students get stuck

          Stretch and support

          Stretch: def mark(*answers): to accept any number of questions, or shuffle each question's choices (import random; random.shuffle).

          Support: Give students a fourth question dictionary written out, and ask them only to change the parameters to match.

          Where it leads

          The optional next lesson stores each question as an object with a check method. Machine learning with Gradio (coming later) puts a trained model behind the same kind of app.

          What each step is for

          Now you can
          Next lesson4. A class for questionsStore each question as an object of your own class, Question, with a check method, so each question knows how to mark itself.