Reputation: 23
I'm using Python 2.7, and I'm trying to write a GUI, but am having some issues with my buttons. I currently have everything running properly, but assuming I've made a mistake with my inputs or something, I'd like a way to stop a running function after hitting the "GO" button. My code is way too long to post here, but a simple example is below. How do I make the "Stop" button break the start function, but not quit the window entirely? Maybe something to do with threading?
I'm sort of new with writing GUIs, and I'm not really a programmer, so this isn't really my area of expertise.
The GUI is totally unresponsive while the main function is running. There must be a way to simultaneously run my function while also allowing me to change things in the GUI and hit buttons, but I'm not sure how that works. The updates don't have to be implemented until the next time the "GO" button is hit though.
import time
from Tkinter import *
class Example:
def __init__(self,master):
self.startButton = Button(master,text='Start',command=self.start)
self.startButton.grid(row=0,column=0)
self.stopButton = Button(master,text='Stop',command=self.stop)
self.stopButton.grid(row=0,column=1)
self.textBox = Text(master,bd=2)
self.textBox.grid(row=1,columnspan=2)
def start(self):
self.textBox.delete(0.0,END)
for i in xrange(1000):
text = i+1
self.textBox.insert(END,str(text)+'\n\n')
time.sleep(1)
return
def stop(self):
""" Do something here to stop the running "start" function """
pass
root=Tk()
Example(root)
root.mainloop()
Upvotes: 1
Views: 9279
Reputation: 1
I added some improvement to the answer written on top in way that multiple clicks on start button doesn't corrupt the program:
from tkinter import *
class Example:
def __init__(self, master):
self.cancel_id = None
self.startButton = Button(master,text='Start', command=self.start)
self.startButton.grid(row=0, column=0)
self.stopButton = Button(master, text='Stop', command=self.stop)
self.stopButton.grid(row=0, column=1)
self.textBox = Text(master, bd=2)
self.textBox.grid(row=1, columnspan=2)
def start(self):
if(self.cancel_id==None):
self.count = 0
#self.cancel_id = None
self.counter()
def counter(self):
self.textBox.delete("1.0", END)
if (self.count < 10):
self.count += 1
self.textBox.insert(END, str(self.count)+'\n\n')
self.cancel_id = self.textBox.after(1000, self.counter)
def stop(self):
if self.cancel_id is not None:
self.textBox.after_cancel(self.cancel_id)
self.cancel_id = None
root=Tk()
Example(root)
root.mainloop()
Upvotes: 0
Reputation: 123531
With Tkinter
, such things are commonly done using the universal widget after()
method. You should generally not use time.sleep()
in a Tkinter program because it prevents the mainloop()
from running (which is what is making the GUI unresponsive in your code).
A successful after()
call will return an integer "cancel id" which can used to stop the callback just scheduled. This is what's needed by the Stop()
method of your Example
class to stop the method doing the counting.
from Tkinter import *
class Example:
def __init__(self, master):
self.startButton = Button(master,text='Start', command=self.start)
self.startButton.grid(row=0, column=0)
self.stopButton = Button(master, text='Stop', command=self.stop)
self.stopButton.grid(row=0, column=1)
self.textBox = Text(master, bd=2)
self.textBox.grid(row=1, columnspan=2)
def start(self):
self.count = 0
self.cancel_id = None
self.counter()
def counter(self):
self.textBox.delete("1.0", END)
if self.count < 10:
self.count += 1
self.textBox.insert(END, str(self.count)+'\n\n')
self.cancel_id = self.textBox.after(1000, self.counter)
def stop(self):
if self.cancel_id is not None:
self.textBox.after_cancel(self.cancel_id)
self.cancel_id = None
root=Tk()
Example(root)
root.mainloop()
Upvotes: 4
Reputation: 5698
You could try using a class property of type bool
Above your init
method, you could define a property like this: bool stopFlag = False
Then inside your for
loop, add an if(!self.stopFlag): break
. This will check the stopFlag
every iteration, and negate it. So it will default to passing the if check at first.
Then inside your stop
method, just set your self.stopFlag = True
So when stop
is called, it will flip the flag, and if the start
method is running in it's for
loop, it will catch this change in the flag and break out.
Upvotes: -1