1

The code is essentially a math quiz that asks the user to input an answer. The code will then display if the answer is right or wrong. However, currently the self.feedback.configure(text = "Correct") is not being run in the method "Next" if the self.feedback.configure(text = "") is inserted into the "Check" method (there is a 0.2 second delay so the code should configure to "Correct" for 0.2 seconds before configuring back to "" an empty string)

This is the code:

from tkinter import *
from tkinter import ttk
import random
import time

class TimesTable:

    def __init__(self, parent):
        """ Sets up the GUI. 
        """
        self.points = 0

        self.problem_label = ttk.Label(parent, text = "Question:") #empty for now
        self.problem_label.grid(row = 0, column=0, sticky = W, padx = 10, pady = 10)

        self.answer_entry = ttk.Entry(parent, width = 7)

        self.check_btn = ttk.Button(parent, text = "Check Answer", command = self.Check)

        self.next_btn = ttk.Button(parent, text = "Start", command = self.Next)
        self.next_btn.grid(row = 0, column = 1, sticky = W, padx = 10, pady = 10)

        self.feedback = ttk.Label(parent, text = "Click 'Start' to begin!")
        self.feedback.grid(row = 1, column = 0, sticky = W, padx = 10, pady = 10)


    def Next(self):
        time.sleep(0.2)
        ***self.feedback.configure(text = "")*** #Fix this (taking it out makes the .configure in Check work)
        number1 = random.randrange(2,10)
        number2 = random.randrange(2,10)
        operation = ["*", "+", "-"]
        operation_ran = operation[(random.randrange((len(operation))))]
        display_question = "Question: {} {} {} = ".format(number1, operation_ran, number2)
        self.ans = eval("{} {} {} ".format(number1, operation_ran, number2))
        self.problem_label.configure(text = display_question)
        self.check_btn.grid(row = 1, column = 1, sticky = W, padx = 10, pady = 10)
        self.answer_entry.grid(row = 0, column = 1, sticky = W, padx = 10, pady = 10)
        self.next_btn.grid_remove()

    def Check(self):
        try:
            if int(self.answer_entry.get()) == int(self.ans):
                ***self.feedback.configure(text = "Correct!")*** #Why does this not work? in def Next, it is configured to nothing but shouln't it still sleep for 0.2s before configuring to nothing via the calling of method def Next???
                self.points += 1
                self.answer_entry.delete(0,END)
                self.answer_entry.focus()
                print(self.points) #Just to check point system works
                self.Next()
            else:
                ***self.feedback.configure(text = "Wrong. The answer is {}".format(self.ans))***
                if self.points > 0:
                    self.points -= 1
                self.answer_entry.delete(0,END)
                self.answer_entry.focus()
                print(self.points)
                self.Next()

        except ValueError:
            self.feedback.configure(text = "Please enter a valid number")
            self.answer_entry.delete(0,END)
            self.answer_entry.focus()

#main routine
if __name__ == "__main__":
    root = Tk()
    root.title("Math Quiz")
    tester = TimesTable(root)
    root.mainloop()
Fish Omelette
  • 21
  • 1
  • 3

1 Answers1

1

Both configures are running correctly, but you can't see the "Correct" text because it is waiting and you need to update your screen before making it wait to see changes.

You can use update_idletasks method for that.
Also, instead of sleep you can use after method. For this particular code, it doesn't matter that much but at least makes you avoid another import (time module).

def Next(self):
    root.update_idletasks()
    root.after(200)
    self.feedback.configure(text = "")
Community
  • 1
  • 1
Lafexlos
  • 7,618
  • 5
  • 38
  • 53
  • My code is working as it should now, however, I am unclear as to what **update_idletasks** does. I searched it up and it said: "Calls all pending idle tasks, without processing any other events. This can be used to carry out geometry management and redraw widgets if necessary, without calling any callbacks." Does the **pending idle task** refer to the root.after(200)? So the method **update_idletasks** stops the code ("without processing any other events") and then runs the root.after(200) ("calls all pending idle tasks") and then proeeds with the code once the root.after(200) is run? – Fish Omelette Mar 24 '17 at 19:41
  • `root.after(200)` without a second argument won't do anything at all. – Junuxx Mar 26 '17 at 01:40
  • To delay the execution of `Next()` as intended in the Q, you probably want to call `root.after(200, self.Next)` within `Check()`. – Junuxx Mar 26 '17 at 01:43
  • @Junuxx I am on mobile at weekends so I can not give proper explanation but check after's doc. Without an argument, it does the same thing as time.sleep and since after method comes with tkinter, using after, you are not importing an extra module. – Lafexlos Mar 26 '17 at 08:41
  • @Fish since there is an infinite loop going on, tasks are executed in queue. When you use update_idletask you force program to execute. So using update there, executes configure(text='complete') first, then makes it wait. – Lafexlos Mar 26 '17 at 08:44
  • @Junuxx What if i was delaying something else instead of a method? Like for example, i have edited my code and now i have a new line that my self.feedback_label configures to, which is "Please enter a valid number!", and i wish for it to configure back to an empty string after? so instead of: 'root.after(200, self.Next)' could i do: 'root.after(200, self.feedback_label.configure(text = ""))' ? Thanks – Fish Omelette Apr 04 '17 at 05:15
  • @Fish Omelette: That won't work like that, but the easiest way to fix that would be to put that line that calls configure(text="") in its own method (say, self.ClearLabel), and then call that with root.after. – Junuxx Apr 04 '17 at 21:58