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