Before discovering http://quizlet.com/, the following program was developed for running custom quizzes to help with studying for college courses. The program is not very advanced, but it works reasonably well for what it was designed to do. If the program were developed further, it would need greater capabilities than it currently has and would require a secondary system for actually creating the quizzes (currently, they are hand-typed). Quiz Me could be a starting point for anyone who wishes to actually write a program such as this and inspire others to write much better programs than what this recipe currently offers.
There are several dialogs used throughout Quiz Me, and this module implements most of them. The standard dialog class in "tkinter.simpledialog" is modified slightly and used as a base class for the rest of the dialogs. ShowStatus displays whether a tier of the quiz is being entered or exited. AskQuestion allows questions to be asked and answered. ReviewProblems can be used to cycle through questions that were answered incorrectly. ShowReport gives a readout of the current cumulative progress score earned in the quiz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 | ####################
# source/gui_logs.py
####################
from tkinter import messagebox, simpledialog, font
from tkinter import Frame, Label, Button, StringVar
from tkinter import NSEW, LEFT, W, DISABLED, NORMAL
import textwrap, functools
################################################################################
TITLE_SIZE = 16
TEXT_WIDTH = 40
################################################################################
class _Dialog(simpledialog.Dialog):
def ok(self, event=None):
self.withdraw()
self.update_idletasks()
try:
self.apply()
finally:
self.cancel(force=True)
def cancel(self, event=None, force=False):
title = 'Warning'
message = 'Are you sure you want\nto stop taking this test?'
if force or messagebox.askyesno(title, message, master=self):
if self.parent is not None:
self.parent.focus_set()
self.destroy()
################################################################################
class ShowStatus(_Dialog):
WAIT = 3
def __init__(self, parent, title, message, callback):
self.message = message
self.callback = callback
super().__init__(parent, title)
def body(self, master):
style = font.Font(self, size=TITLE_SIZE)
self.status = Label(master, text=self.message, font=style)
self.status.grid(sticky=NSEW, padx=TITLE_SIZE, pady=TITLE_SIZE)
return self.status
def buttonbox(self):
self.after(self.WAIT * 1000, self.ok)
def apply(self):
root.after_idle(self.callback)
################################################################################
class AskQuestion(_Dialog):
def __init__(self, parent, event, callback):
self.question = textwrap.wrap(event.question, TEXT_WIDTH)
self.choices = event.choices
self.answer = event.answer
self.callback = callback
super().__init__(parent, event.category)
def body(self, master):
self.labels = []
for line in self.question:
self.labels.append(Label(master, text=line, justify=LEFT))
self.labels[-1].grid(sticky=NSEW)
def buttonbox(self):
self.buttons = []
box = Frame(self)
for choice in self.choices:
options = {'text': textwrap.fill(choice, TEXT_WIDTH),
'width': TEXT_WIDTH,
'command': functools.partial(self.click, choice)}
self.buttons.append(Button(box, **options))
self.buttons[-1].grid(padx=5, pady=5)
box.pack()
def click(self, choice):
self.answer(choice)
self.ok()
def apply(self):
root.after_idle(self.callback)
################################################################################
class ReviewProblems(_Dialog):
def __init__(self, parent, event, flag):
self.problems = list(event.problems())
self.problem = 0
self.flag = flag
super().__init__(parent, 'Problems')
def body(self, master):
title = font.Font(self, size=TITLE_SIZE)
legend = font.Font(self, weight='bold')
# Create display variables.
self.category = StringVar(master)
self.question = StringVar(master)
self.answer = StringVar(master)
self.right = StringVar(master)
# Create form labels.
self.c_label = Label(master, textvariable=self.category, font=title)
self.q_label = Label(master, textvariable=self.question)
self.you_answered = Label(master, text='You answered:', font=legend)
self.a_label = Label(master, textvariable=self.answer)
self.right_answer = Label(master, text='Right answer:', font=legend)
self.r_label = Label(master, textvariable=self.right)
# Create control buttons.
options = {'text': '< < <',
'width': TEXT_WIDTH // 2,
'command': self.go_back}
self.back = Button(master, **options)
options = {'text': '> > >',
'width': TEXT_WIDTH // 2,
'command': self.go_next}
self.next = Button(master, **options)
# Display the body.
options = {'sticky': NSEW, 'padx': 5, 'pady': 5}
self.c_label.grid(row=0, column=0, columnspan=2, **options)
self.q_label.grid(row=1, column=0, columnspan=2, **options)
self.you_answered.grid(row=2, column=0, **options)
self.a_label.grid(row=2, column=1, **options)
self.right_answer.grid(row=3, column=0, **options)
self.r_label.grid(row=3, column=1, **options)
self.back.grid(row=4, column=0, **options)
self.next.grid(row=4, column=1, **options)
# Update the labels.
self.update()
def go_back(self):
self.problem -= 1
self.update()
def go_next(self):
self.problem += 1
self.update()
def update(self):
# Update the labels.
problem = self.problems[self.problem]
self.category.set(problem.category)
self.question.set(textwrap.fill(problem.question, TEXT_WIDTH))
self.answer.set(textwrap.fill(problem.answer, TEXT_WIDTH // 2))
self.right.set(textwrap.fill(problem.right, TEXT_WIDTH // 2))
# Update the buttons.
if self.problem == 0:
self.back['state'] = DISABLED
else:
self.back['state'] = NORMAL
if self.problem + 1 == len(self.problems):
self.next['state'] = DISABLED
else:
self.next['state'] = NORMAL
def apply(self):
self.flag[0] = True
################################################################################
class ShowReport(_Dialog):
RULE = '='
def __init__(self, parent, event, callback):
self.level = event.level
self.right = event.right
self.wrong = event.wrong
self.total = event.total
self.callback = callback
super().__init__(parent, 'Report')
def body(self, master):
title = font.Font(self, size=TITLE_SIZE)
legend = {'anchor': W,
'justify': LEFT,
'font': font.Font(self, weight='bold')}
# Create all labels.
text = 'Cumulative score for\nprevious {}:'.format(self.level)
self.explanation = Label(master, text=text, font=title)
self.ruler_one = Label(master, text=(self.RULE * TEXT_WIDTH))
self.answers_right = Label(master, text='Answers right:', **legend)
self.display_right = Label(master, text=str(self.right))
self.answers_wrong = Label(master, text='Answers wrong:', **legend)
self.display_wrong = Label(master, text=str(self.wrong))
self.percent = Label(master, text='Percentage correct:', **legend)
percentage = str(int(100 * self.right / self.total + 0.5)) + '%'
self.display = Label(master, text=percentage)
self.ruler_two = Label(master, text=(self.RULE * TEXT_WIDTH))
# Display the results.
options = {'sticky': NSEW, 'padx': 5, 'pady': 5}
self.explanation.grid(row=0, column=0, columnspan=2, **options)
self.ruler_one.grid(row=1, column=0, columnspan=2, **options)
self.answers_right.grid(row=2, column=0, **options)
self.display_right.grid(row=2, column=1, **options)
self.answers_wrong.grid(row=3, column=0, **options)
self.display_wrong.grid(row=3, column=1, **options)
self.percent.grid(row=4, column=0, **options)
self.display.grid(row=4, column=1, **options)
self.ruler_two.grid(row=5, column=0, columnspan=2, **options)
def apply(self):
root.after_idle(self.callback)
|