单选按钮导航和值存储

2024-04-20 13:33:49 发布

您现在位置:Python中文网/ 问答频道 /正文

我想用pythontkinter写一个选择题测验。我有一个由两部分组成的问题。 我有单选按钮,显示选择和收集选定的选项。我还创建了一个按钮来导航到下一个问题或返回到上一个问题,以及另一个按钮来查看分数。你知道吗

  • 第1部分-当在测验中向后/向前导航时,如何使每个问题的单选按钮的选定选项保持存在?

  • 第2部分-我认为查看分数按钮应该如何工作的方法是:

    1. 比较每个收集的选项(保存在list?)找到正确答案
    2. 计算分数
    3. 显示器


    第2点和第3点对我来说是最简单的部分。你能告诉我去第一站的正确方向吗?

from tkinter import messagebox
import tkinter as tk

from tkinter import *
# question list
q = [
    "question 1", "question 2", "question 3", "question 4"
]

# options list
options = [
    ["a","b","c","d"],
    ["b","c","d","a"],
    ["c","d","a","b"],
    ["d","a","b","c"],
]

# correct answers list
a = [3,4,1,2]


class Quiz:
    def __init__(self, master):
        self.opt_selected = IntVar()
        self.qn = 0
        self.correct = 0
        self.ques = self.create_q(master, self.qn)
        self.opts = self.create_options(master, 4)
        self.display_q(self.qn)

        self.button = Button(master, text="Previous Question", command=self.back_btn,
                             width=16, borderwidth=3, relief=RAISED)
        self.button.pack(side=LEFT)

        self.button = Button(master, text="Next Question", command=self.next_btn,
                             width=16, borderwidth=3, relief=RAISED)
        self.button.pack(side=LEFT)

        self.button = Button(master, text="View Score", command=self.score_viewer,
                             width=16, borderwidth=3, relief=RAISED)
        self.button.pack(side=LEFT)

    # define questions
    def create_q(self, master, qn):

        w = Label(master, text=q[qn],
                  anchor='w',
                  wraplength=400, justify=LEFT)
        w.pack(anchor='w')

        return w

    # define multiple options
    def create_options(self, master, n):
        b_val = 0
        b = []
        while b_val < n:
            btn = Radiobutton(master, text="foo", variable=self.opt_selected, value=b_val+1)
            b.append(btn)
            btn.pack(side=TOP, anchor="w")
            b_val = b_val + 1
        return b

    # define questions for display when clicking on the NEXT Question Button
    def display_q(self, qn):
        b_val = 0
        self.opt_selected.set(0)
        self.ques['text'] = q[qn]
        for op in options[qn]:
            self.opts[b_val]['text'] = op
            b_val = b_val + 1

    # define questions for display when clicking on the PREVIOUS Question Button
    def display_prev_q(self, qn):
        b_val = 0
        self.opt_selected.set(0)
        self.ques['text'] = q[qn]
        for op in options[qn]:
            self.opts[b_val]['text'] = op
            b_val = b_val + 1

    # check option selected against correct answer list
    def check_q(self, qn):
        if self.opt_selected.get() == a[qn]:
            self.correct += 1
        else:
            self.correct += 0

    # print results
    def print_results(self):
        print("Score: ", self.correct, "/", len(q))

    # define PREVIOUS button
    def back_btn(self):
        self.qn = self.qn - 1
        self.display_prev_q(self.qn)

   # define NEXT button
    def next_btn(self):
        # if self.check_q(self.qn):
        #     print("Correct")
        #     self.correct += 1
        self.qn = self.qn + 1
        self.display_prev_q(self.qn)
        # if self.qn >= len(q):
        #     self.print_results()
        # else:
        #     self.display_q(self.qn)

    # define SCORE view button and score results
    def score_viewer(self):
        score_viewer = messagebox.askquestion("Warning", 'Would you like to view your current score?', icon='warning')
        if score_viewer == 'yes':
            self.check_q(self.qn)
            corr_ans = self.correct
            total_quest = len(q)
            output = '{:.1%}'.format(self.correct / len(q))
            score_text = "\nScore: %s " % output
            output_text = "Correctly answered %a out of  %d questions. %s" % (corr_ans, total_quest, score_text)
            messagebox.showinfo("Score", output_text)
        else:
            tk.messagebox.showinfo('Return', 'Returning to quiz')

Tags: textselfmasterdefdisplaybuttonval按钮
1条回答
网友
1楼 · 发布于 2024-04-20 13:33:49

不幸的是,我认为您需要更改程序的基本架构,使其更加面向对象。具体来说,而不是像您这样拥有一堆独立的list

# question list
q = [
    "question 1", "question 2", "question 3", "question 4"
]

# options list
options = [
    ["a","b","c","d"],
    ["b","c","d","a"],
    ["c","d","a","b"],
    ["d","a","b","c"],
]

# correct answers list
a = [3,4,1,2]

我认为应该定义一个定制的class来封装问题及其当前状态,然后在应用程序初始化期间创建一个list问题。这种方法不仅可以相对容易地从一个显示切换到另一个显示(更不用说跟踪每个显示的当前状态),而且还可以相当直接地执行所有您希望执行的相关操作。你知道吗

这里有一个完整的实现来说明我的意思。注意,它使用@Bryan Oakley的帧切换技术,类似于问题his answer到问题Switch between two frames in tkinter中的内容来显示每个问题。主要区别在于“页面”(问题)存储在通过索引引用的list中,而不是存储在由类名访问的dict中。你知道吗

这种设计的另一个好的方面是,问题数据与Quiz代码完全分离,这意味着如果需要,它可以存储在文件或数据库的外部。你知道吗

我还尝试使代码符合PEP 8 - Style Guide for Python Code(您也应该尽可能多地这样做)。你知道吗

import tkinter as tk
from tkinter.constants import *
from tkinter import messagebox


class Question(tk.Frame):
    """ Frame subclass encapsulating a multiple-option question. """
    def __init__(self, master, text, options, correct_ans):
        super(Question, self).__init__(master)

        self.text = text
        self.options = options
        self.correct_ans = correct_ans
        self.opt_selected = tk.IntVar()

        tk.Label(self, text=self.text, anchor=W, wraplength=400,
                 justify=LEFT).pack(anchor=W)

        for b_val, option in enumerate(self.options, start=1):
            tk.Radiobutton(self, text=option, variable=self.opt_selected,
                           value=b_val).pack(side=TOP, anchor=W)

    def check_q(self):
        """ Check if currently selected option is correct answer. """
        return self.opt_selected.get() == self.correct_ans


class Quiz:
    def __init__(self, master, quiz_questions):
        self.master = master

        # The container is a stack of question Frames on top of one another.
        # The one we want visible will be raised above the others.
        container = tk.Frame(master)
        container.pack(side="top", fill="both", expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        # Create internal list of question Frames.
        self.questions = []
        for args in quiz_questions:
            q_frame = Question(container, *args)
            q_frame.grid(row=0, column=0, sticky=NSEW)
            self.questions.append(q_frame)

        self.qn = 0  # Current question number.
        self.display_q()  # Show it.

        # Create naviagtion Buttons.
        btn = tk.Button(master, width=16, borderwidth=3, relief=RAISED,
                        text="Previous Question", command=self.display_prev_q)
        btn.pack(side=LEFT)
        btn = tk.Button(master, width=16, borderwidth=3, relief=RAISED,
                        text="Next Question", command=self.display_next_q)
        btn.pack(side=LEFT)
        btn = tk.Button(master, width=16, borderwidth=3, relief=RAISED,
                        text="View Score", command=self.score_viewer)
        btn.pack(side=LEFT)

    def display_q(self):
        """ Show the current question by lifting it to top. """
        frame = self.questions[self.qn]
        frame.tkraise()

    def display_next_q(self):
        """ Increment question number, wrapping to first one at end,
            and display it.
        """
        self.qn = (self.qn+1) % len(self.questions)
        self.display_q()

    def display_prev_q(self):
        """ Decrement question number, wrapping to last one at beginning,
            and display it.
        """
        self.qn = (self.qn-1) % len(self.questions)
        self.display_q()

    def score_viewer(self):
        """ Score results with user consent. """

        view_score = messagebox.askquestion(
            "Warning", 'Would you like to view your current score?',
            icon='warning')

        if view_score != 'yes':
            tk.messagebox.showinfo('Return', 'Returning to quiz')
        else:
            # Calculate number of correct answers and percentage correct.
            correct = sum(question.check_q() for question in self.questions)
            accuracy = correct / len(self.questions) * 100
            messagebox.showinfo("Score",
                "You have correctly answered %d out of %d questions.\n"
                "Score: %.1f%%" % (correct, len(self.questions), accuracy))


if __name__ == '__main__':

    # Note this data could also be stored separately, such as in a file.
    question_data = [('Question 1', ("a1", "b1", "c1", "d1"), 3),
                     ('Question 2', ("b2", "c2", "d2", "a2"), 4),
                     ('Question 3', ("c3", "d3", "a3"), 1),
                     ('Question 4', ("d4", "a4", "b4", "c4"), 2)]

    root = tk.Tk()
    root.title('Quiz')
    quiz = Quiz(root, question_data)
    root.mainloop()

相关问题 更多 >