К сожалению, я думаю, вам нужно изменить основную архитектуру вашей программы и сделать ее намного более объектно-ориентированной.В частности, вместо с кучей отдельных list
s, как у вас:
# 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, аналогичный тому, что есть в его ответе на вопрос Переключение между двумя кадрами в tkinter для отображения каждого вопроса.Основное отличие состоит в том, что «страницы» (вопросы) хранятся в list
, на который ссылается индекс, а не в dict
, доступ к которому осуществляется по имени класса.
Еще один приятный аспект этого дизайна заключается в том, чтоданные вопроса полностью отделены от кода Quiz
, что означает, что при желании они могут храниться в файле или базе данных извне.
Я также пытался привести код в соответствие с PEP 8 - СтильРуководство по 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()