Ghilas BELHADJ
Ghilas BELHADJ

Reputation: 14096

Interacting with Tkinter window during a long process

I have a basic python class that creates a window using the standard Tkinter library:

import Tkinter

class GUI(Tkinter.Tk):

    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.initialize()

    def lock_func(self):
        while 1==1:
            print "blah"

    def initialize(self):
        self.processBtn = Tkinter.Button(self, text="Process", command=self.lock_func)
        self.processBtn.pack()        

app = GUI(None)
app.mainloop()

when I hit the Process button, the window doesn't respond. I want to be able to close the program (using the x button) whene the lock_func is runing.

Upvotes: 6

Views: 6591

Answers (3)

Rushy Panchal
Rushy Panchal

Reputation: 17532

One way is to use threading:

import Tkinter
import thread

class GUI(Tkinter.Tk):

    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.initialize()

    def lock_func(self):
        while 1==1:
            print "blah"

    def initialize(self):
        self.processBtn = Tkinter.Button(self, text="Process", command=lambda: thread.start_new_thread(self.lock_func, ()))
        self.processBtn.pack()        

app = GUI(None)
app.mainloop()

However, it will keep printing until you close the Python console.

To stop it, you can use another button that changes a variable:

import Tkinter
import thread

class GUI(Tkinter.Tk):

    def __init__(self,parent):
        Tkinter.Tk.__init__(self,parent)
        self.parent = parent
        self.shouldPrint = True
        self.initialize()

    def lock_func(self):
        while self.shouldPrint:
            print "blah"

    def setShouldPrint(self, value):
        self.shouldPrint = value

    def initialize(self):
        self.processBtn = Tkinter.Button(self, text="Process", command=lambda: thread.start_new_thread(self.lock_func, ()))
        self.stopBtn = Tkinter.Button(self, text = "Stop", command = lambda: self.setShouldPrint(False))
        self.processBtn.grid(row = 1)
        self.stopBtn.grid(row = 2)


app = GUI(None)
app.mainloop()

Upvotes: 0

unutbu
unutbu

Reputation: 879471

You could use a generator to hold the state within the loop, and use yield to relinquish control back to the main loop. Then use self.after to repeatedly call the generator's next method to simulate the effect of while True -- but doing it in a way which is friendly to Tkinter's main loop.

import Tkinter as tk

class App(object):
    def __init__(self, master):
        self.master = master
        self.initialize()

    def lock_func(self):
        def step():
            while True:
                print("blah")
                self.nextstep_id = self.master.after(1, nextstep)
                yield
        nextstep = step().next
        self.nextstep_id = self.master.after(1, nextstep)

    def stop(self):
        self.master.after_cancel(self.nextstep_id)
        print("stopped")

    def initialize(self):
        self.nextstep_id = 0
        self.process_button = tk.Button(self.master, text="Process",
                                        command=self.lock_func)
        self.stop_button = tk.Button(self.master, text="Stop",
                                     command=self.stop)        
        self.process_button.pack()
        self.stop_button.pack(expand='yes', fill='x')

root = tk.Tk()
app = App(root)
root.mainloop()

Upvotes: 5

The-IT
The-IT

Reputation: 728

You can use the window.update() method too keep your GUI active and functional after every time you change something on it. During the roots mainloop, this happens automatically but if you're prolonging the mainloop it's probably a good idea to do it manually your self. Put the window.update() in the loop that is taking a while. Note: window is a Tk() object

Upvotes: 4

Related Questions