单选按钮导航和值存储

时间:2019-04-25 21:44:41

标签: python python-3.x list tkinter radio-button

我正在尝试使用Python Tkinter编写多项选择测验。我有一个两部分的问题。 我有单选按钮,用于显示选择并收集选择的选项。我还创建了一个用于导航至下一个问题或返回至上一个问题的按钮,以及另一个用于查看分数的按钮。

  • 第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')

2 个答案:

答案 0 :(得分:1)

不幸的是,我认为您需要更改程序的基本体系结构并使之更加面向对象。具体来说,代替是像您一样拥有一堆独立的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 answerSwitch 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()

答案 1 :(得分:0)

您可以将结果存储在dict中,并在查看分数或什至前进/后退按钮时调出结果。

class Quiz:
    def __init__(self, master):
        self.opt_selected = IntVar()
        self.answer = {}
        ...

    def create_q(self, master, qn): # I'm using the label text as key

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

        return self.w

    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,command=lambda : self.set_dict(self.opt_selected.get()))
            b.append(btn)
            btn.pack(side=TOP, anchor="w")
            b_val = b_val + 1
        return b

    def set_dict(self,ans):
        self.answer[self.w["text"]] = ans
        print (self.answer)

    ...