Python Forum
Trouble with .after() Tkinter
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Trouble with .after() Tkinter
#1
Im doing my first big project which is a quiz. I am stuck on trying to limit the time the user has to answer a question. I've searched for so many hours and the only option that seems to work is using a timer thread or .after(). I'm not faliliar with threading or any slightly advanced tkInter at all so I'm all ears.

def revisionMode(question): inputAnswer = StringVar()

#-----Creation & placement of buttons and labels
qLabel = Label(screen1, text = question.prompt[0]
qLabel.grid(row = 6, column = 2)

answerBox = Entry(screen1, textvariable = inputAnswer)
answerBox.grid(column = 2, row = 10)

t = Timer(7.0, nextQuestion, args=(False, question.difficulty), kwargs=None)
t.start()

#-----The button that will trigger the validation of the answer
Button(screen1, text = "Submit", command = lambda: checkAnswer(question)).grid(column = 3, row = 9)
The error I get from that is that is: RuntimeError: main thread is not in main loop. From my understanding and desparate googling, tkinter and threading don't work toghether very well and I've seen solutions using Queues.
The version below is using .after()

def revisionMode(question): inputAnswer = StringVar()

    #-----Creation & placement of buttons and labels
    qLabel = Label(screen1, text = question.prompt[0]
    qLabel.grid(row = 6, column = 2)

    answerBox = Entry(screen1, textvariable = inputAnswer)
    answerBox.grid(column = 2, row = 10)

    after_id = screen1.after(7000, nextQuestion, False, question.difficulty)

    #-----The button that will trigger the validation of the answer
    Button(screen1, text = "Submit", command = lambda: checkAnswer(question)).grid(column = 3, row = 9)
    screen1.after_cancel(after_id)
With this I don't get any errors but the question displayed does not change after 7 sec. I've been stuck for so long and any help, advice and tips are greatly appreciated! :D
Reply
#2
you can read up on after here: http://infohost.nmt.edu/tcc/help/pubs/tk...ersal.html
I have used it before, but it was too long ago to remember all the details, so just pointing out a good writeup.
general format is w.after(delay_ms, callback=None, *args)
Another alternative is to create your own timed event. I write one of those some time back, thread here: https://python-forum.io/Thread-Multi-thr...imer-Class
It's pretty simple to use.
Reply
#3
This looks fine to me (and so you can delete the Timer code)
after_id = screen1.after(7000, nextQuestion, False, question.difficulty)
so the problem is most likely in the nextQuestion function, and we don't know anything about what it does.
Reply
#4
(Mar-02-2019, 06:06 PM)woooee Wrote: This looks fine to me (and so you can delete the Timer code)
after_id = screen1.after(7000, nextQuestion, False, question.difficulty)
so the problem is most likely in the nextQuestion function, and we don't know anything about what it does.

nextQuestion takes the difficultyof the question, does some stuff to it and at the end calls revisonMode with a new question. This is how the function looks like. It seems like the function is not even called in the screen1.after()
def nextQuestion(validity, currentDiffculty):
    
    newDifficulty = 0  
    newQuestion = None
    if validity:
        newDifficulty = currentDiffculty + 1
        
        if newDifficulty > 3:
            newDifficulty = 3
        
        for question in questions:
            if question.difficulty == newDifficulty:
                newQuestion = question  
    else:
        newDifficulty = currentDiffculty - 1
        
        if newDifficulty < 1 :
            newDifficulty = 1
        
        for question in questions:
            if question.difficulty == newDifficulty:
                newQuestion = question           
    qLabel.config(text = "")
    ans1.config(text = "")
    ans2.config(text = "")
    ans3.config(text = "")
    revisionMode(newQuestion)
Reply
#5
This code will always ask the same question, i.e. the last question found, so it appears that it is not working because it puts up the same question. Add a print statement to the function to prove this to yourself.
        for question in questions:
            if question.difficulty == newDifficulty:
                newQuestion = question           
Reply
#6
(Mar-02-2019, 09:46 PM)woooee Wrote: This code will always ask the same question, i.e. the last question found, so it appears that it is not working because it puts up the same question. Add a print statement to the function to prove this to yourself.
 for question in questions: if question.difficulty == newDifficulty: newQuestion = question 

I got it to work omg!! I put a print statement after
screen1.after_cancel(after_id)

The timer was being cancelled as soon as the button was created. Now the .after_cancel() is at the start of nextQuestion. Thanks for the help :D
Reply


Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020