1

My question is similar to this: Python TKinter multiple operations. However, The answer provided does not help me since it points to an article with a list of possible functions to use. I want to see an actual implementation of the solution please.

My question: I have two buttons on a frame. one button calls the 'execute' function as long as the toggle variable is set to true. The 2nd button sets the toggle value to False. I want the 'execute' function to keep going once i press the execute button but stop when i press the 2nd (toggle false) button. However, the frame gets stuck once i press 'execute'. I understand its because of callbacks. How can i fix this? Heres my sample code:

from Tkinter import *
from time import sleep

class App:

    def __init__(self, master):

        self.toggle = False
        frame = Frame(master)
        frame.pack()

        self.exeButton = Button(frame, text="Execute", fg="blue", command=self.execute)
        self.exeButton.pack(side=LEFT)

        self.tOffButton = Button(frame, text="Toggle Off", command=self.toggleOff)
        self.tOffButton.pack(side=LEFT)

    def execute(self):
        self.toggle = True
        while(self.toggle):
            print "hi there, everyone!"
            sleep(2)

    def toggleOff(self):
    self.toggle = False

root = Tk()
app = App(root)
root.mainloop()
Community
  • 1
  • 1
Aasam Tasaddaq
  • 645
  • 4
  • 13
  • 23

1 Answers1

5

Short answer, you can't do exactly what you want. Tkinter is single threaded -- when you call sleep(2) it does exactly what you ask it to: it sleeps.

If your goal is to execute something every 2 seconds as long as a boolean flag is set to True, you can use after to schedule a job to run in the future. If that job also uses after to (re)schedule itself, you've effectively created an infinite loop where the actual looping mechanism is the event loop itself.

I've taken your code and made some slight modifications to show you how to execute something continuously until a flag tells it to stop. I took the liberty of renaming "toggle" to "running" to make it a little easier to understand. I also use just a single method to both turn on and turn off the execution.

from Tkinter import *
from time import sleep

class App:

    def __init__(self, master):

        self.master = master
        self.running = False
        frame = Frame(master)
        frame.pack()

        self.exeButton = Button(frame, text="Execute", fg="blue", 
            command=lambda: self.execute(True))
        self.exeButton.pack(side=LEFT)

        self.tOffButton = Button(frame, text="Toggle Off", 
            command=lambda: self.execute(False))
        self.tOffButton.pack(side=LEFT)

    def execute(self, running=None):
        if running is not None:
            self.running = running
        if self.running:
            print "hi there, everyone!"
            self.master.after(2000, self.execute)

root = Tk()
app = App(root)
root.mainloop()
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • If we had a nickle for every time we had a question where the answer was essentially : "Don't use `sleep`, use `Widget.after` instead", we'd probably have a dollar by now :^). – mgilson Aug 23 '12 at 18:13
  • Here's another situation where you would probably have 10 times more the amount of money: I am new to GUI programming. Or, I am new to python. Also, I have never used Tkinter before. – Aasam Tasaddaq Aug 23 '12 at 18:42
  • Question: What happens if the 'execute' function calls another function form a different imported class and that imported function uses sleep? I am assuming I would end up with the same problem again? – Aasam Tasaddaq Aug 23 '12 at 18:43
  • Yes, you would have the same problem. Sleep causes the whole thread to sleep. A good rule of thumb for just about every GUI toolkit is to never use sleep. That's just not how GUI applications work; they need to remain active so that they can service events such as redraw requests, key presses, etc. – Bryan Oakley Aug 23 '12 at 19:14
  • Of course you can wrap a GUI library to make it thread-safe and then use threads alternatively ... – Noctis Skytower Aug 23 '12 at 20:21
  • @NoctisSkytower: true. You can use other threads or processes for your long running tasks, and communicate with the main thread via a thread-safe queue. Quite often that's not necessary -- it's surprising just how much you can do with a single event loop in a single thread. – Bryan Oakley Aug 23 '12 at 20:27