public
Last active

start/stop tkinter progressbar on start/end of a subprocess without using threads

  • Download Gist
progressbar-nothreads.py
Python
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
import sys
from subprocess import Popen
from Tkinter import Tk, StringVar
import ttk
 
START, STOP = "start", "stop"
 
# just some arbitrary command for demonstration
cmd = [sys.executable, '-c', """import sys, time
print("!")
sys.stdout.flush()
for i in range(30):
sys.stdout.write("%d " % i)
sys.stdout.flush()
time.sleep(.05)
"""]
 
 
class App(object):
def __init__(self, parent):
self.process = None
self.after = parent.after
self.command = START
self.button_text = None
self.progressbar = None
self.make_widgets(parent)
 
def make_widgets(self, parent):
parent = ttk.Frame(parent, padding="3 3 3 3")
parent.pack()
self.progressbar = ttk.Progressbar(parent, length=200,
mode='indeterminate')
self.progressbar.pack()
self.button_text = StringVar()
self.button_text.set(self.command)
button = ttk.Button(parent, textvariable=self.button_text,
command=self.toggle)
button.pack()
button.focus()
 
def toggle(self, event_unused=None):
if self.command is START:
self.progressbar.start()
try:
self.start_process()
except:
self.progressbar.stop()
raise
self.command = STOP
self.button_text.set(self.command)
else:
assert self.command is STOP
self.stop_process()
 
def stop(self):
self.progressbar.stop()
self.command = START
self.button_text.set(self.command)
 
def start_process(self):
self.stop_process()
self.process = Popen(cmd)
 
def poller():
if self.process is not None and self.process.poll() is None:
# process is still running
self.after(delay, poller) # continue polling
else:
self.stop()
delay = 100 # milliseconds
self.after(delay, poller)
 
def stop_process(self):
if self.process is not None and self.process.poll() is None:
self.process.terminate()
# kill process in a couple of seconds if it is not terminated
self.after(2000, kill_process, self.process)
self.process = None
 
 
def kill_process(process):
if process is not None and process.poll() is None:
process.kill()
process.wait()
 
 
if __name__ == "__main__":
root = Tk()
app = App(root)
 
def shutdown():
app.stop_process()
root.destroy()
 
root.protocol("WM_DELETE_WINDOW", shutdown)
root.bind('<Return>', app.toggle)
root.mainloop()

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.