While Loop Locks Application

I have been banging my head for a while now on a application I am working on. After many hours trying to debug an issue where the interface locks up and nothing else can take place I figured out it was the dreaded While loop. See this example below and run it. When you start the while loop by clicking on the button you cannot do anything else on the screen. In this is case it is just a simple alert button that needs pressing.

from Tkinter import *
import tkMessageBox

root = Tk()
root.geometry("450x250+300+300")
root.title("Raspberry PI Test")

def myloop():
    count = 0
    while (count < 500):
       print 'The count is:', count
       count = count + 1

    print "Good bye!"

def mymessage():
    tkMessageBox.showinfo(title="Alert", message="Hello World!")

buttonLoop = Button(root, text="Start Loop", command=myloop)
buttonLoop.place(x=5, y=15)

buttonMessage = Button(root, text="Start Loop", command=mymessage)
buttonMessage.place(x=85, y=15)


root.mainloop()

How can I have a loop that needs to run until a count is completed and still be able to do other tasks in my application? I should also note that I have tried this same thing using a Thread and it doesn’t matter. The UI is still waiting for the While loop to end before you can do anything.

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

now that I understand what you want better (a stopwatch) I would recommend the root.after command

from Tkinter import *
import tkMessageBox
import threading
import time
root = Tk()
root.geometry("450x250+300+300")
root.title("Raspberry PI Test")
print dir(root)
count = 0
def start_counter():
    global count
    count = 500
    root.after(1,update_counter)
def update_counter():
    global count
    count -= 1
    if count < 0:
        count_complete()
    else:
        root.after(1,update_counter)

def count_complete():
    print "DONE COUNTING!! ... I am now back in the main thread"
def mymessage():
    tkMessageBox.showinfo(title="Alert", message="Hello World!")

buttonLoop = Button(root, text="Start Loop", command=myloop)
buttonLoop.place(x=5, y=15)

buttonMessage = Button(root, text="Start Loop", command=mymessage)
buttonMessage.place(x=85, y=15)


root.mainloop()

(original answer below)

use a thread

from Tkinter import *
import tkMessageBox
import threading
import time
root = Tk()
root.geometry("450x250+300+300")
root.title("Raspberry PI Test")
print dir(root)
def myloop():
    def run():
        count = 0
        while (count < 500) and root.wm_state():
           print 'The count is:', count
           count = count + 1
           time.sleep(1)

        root.after(1,count_complete)
    thread = threading.Thread(target=run)
    thread.start()
def count_complete():
    print "DONE COUNTING!! ... I am now back in the main thread"
def mymessage():
    tkMessageBox.showinfo(title="Alert", message="Hello World!")

buttonLoop = Button(root, text="Start Loop", command=myloop)
buttonLoop.place(x=5, y=15)

buttonMessage = Button(root, text="Start Loop", command=mymessage)
buttonMessage.place(x=85, y=15)


root.mainloop()

note that when you show the info box that will block at the windows api level so the thread counting will wait till that closes … to get around that you can just replace threading with multiprocessing I think

Method 2

I don’t really know much about TKinter, but from my reading it’s clear that you need to use a some TKinter method in your while loop in order to update your text box. TKinter runs on an event loop so you have to send a signal from your code to re-enter TKinter’s execution.

You’ve done a great job discovering that your while loop is blocking the execution of your UI’s updates. So instead of threading you need to just pause your counting's execution and let TKinter update the UI.

This tutorial provides an excellent example. The key is on line 24 where he calls root.update which I believe breaks from your program to let TKinter do it’s thing.

Method 3

Here is the final code just to prove that the thread works. The count is displaying on the screen at the same time as it is happening. Thanks again Joran!

from Tkinter import *
import tkMessageBox
import threading
import time
root = Tk()
root.geometry("450x250+300+300")
root.title("Raspberry PI Test")
showResults = StringVar()
showResults.set('0')
print dir(root)
def myloop():
    def run():
        count = 0
        while (count < 1000) and root.wm_state():
           print 'The count is:', count
           showResults.set(count)
           count = count + 1
           #time.sleep(1)

        root.after(1,count_complete)
    thread = threading.Thread(target=run)
    thread.start()
def count_complete():
    print "DONE COUNTING!! ... I am now back in the main thread"
def mymessage():
    tkMessageBox.showinfo(title="Alert", message="Hello World!")

buttonLoop = Button(root, text="Start Loop", command=myloop)
buttonLoop.place(x=5, y=15)

buttonMessage = Button(root, text="Message", command=mymessage)
buttonMessage.place(x=85, y=15)

l2 = Label(root, width=15, height=4, font=("Helvetica", 16), textvariable=showResults, background="black", fg="green")
l2.place(x=15, y=65)

root.mainloop()


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x