我正在尝试使用Python Tkinter编写多项选择测验。我有一个两部分的问题。 我有单选按钮,用于显示选择并收集选择的选项。我还创建了一个用于导航至下一个问题或返回至上一个问题的按钮,以及另一个用于查看分数的按钮。
第1部分-当在测验中向后/向前导航时,如何使每个问题的单选按钮的选定选项保持显示状态?
第2部分-我考虑过观看得分按钮应该如何工作的方式是:
list
中)与正确答案进行比较
对我来说,要点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')
答案 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 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()
答案 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)
...