【问题标题】:Radiobutton navigation and value storing单选按钮导航和值存储
【发布时间】:2019-09-15 09:42:24
【问题描述】:

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

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

  • 第 2 部分 - 我想出 view score 按钮应该如何工作的方式是:

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

【问题讨论】:

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


    【解决方案1】:

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

    # 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()
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-05-02
      • 2020-07-12
      • 2016-05-12
      • 1970-01-01
      • 2018-06-08
      • 1970-01-01
      • 1970-01-01
      • 2013-04-09
      相关资源
      最近更新 更多