Display the progress of execution of a function using a progress bar - python

import sys
import ttk
from Tkinter import *
from timeit import default_timer as timer
def sum(a, b):
for i in range(10):
c = a + b
print "Sum", c
time.sleep(5)
return c
mGui = Tk()
mGui.title('Progress')
mpb = ttk.Progressbar(mGui,orient ="horizontal", length = 200, mode ="determinate")
mpb.pack()
mpb.start()
mpb["maximum"] = 100
Start_Timer=timer()
sum(3,4)
Stop_Timer=timer()
Execution_Time=Stop_Timer-Start_Timer
mpb["value"] = Execution_Time
mGui.mainloop()
I have a function which calculates the sum of two integers. I want to display the status of the execution of this sum function using tkinter progress bar.
This is my approach, but it displays progress bar after executing the sum function, and I want to display the progress bar while the sum function is executing, and the progress should be indicated based on the execution time of the function.
I didn't find answers which satisfy my requirement. It would be great if someone could help me with this.

You question is interesting, but you approach is totally wrong.
It executes your sum first, because the GUI has not reached mainloop.
So after GUI reaches mainloop it start to wait for events (because of event-driven nature of GUI programming) like button presses. In other words: you can't call a function until mainloop if you ask for callbacks to tkinter.
Another problem - tkinter is single threaded so the gui can only update itself when the function finishes running. So if you start a loop in function there's no callbacks to gui either, but you can call function from a loop, and update gui on each iteration! (e.g. "Loop" of self generated events, consisting of the methods generate_event or after.)
And for god's sake how progressbar can know execution time of a function if function isn't completely executed? If anybody knows that, please comment..
But if you dare, you can start playing with multithreading and queues!
In my example, the progress bar is updated in parallel with the execution of the function, thanks to the separate thread in which the function is executed, and the queue to which the "responses" of the function fall, on the basis of which the progress bar is updating!
Tested it with python 3.5:
try:
import Tkinter as tk # Python 2
import ttk
import Queue as queue
except ImportError:
import tkinter as tk # Python 3
import tkinter.ttk as ttk
import queue
import threading
import time
class ThreadFunc(threading.Thread):
def __init__(self, loop_time=1.0 / 60):
super(ThreadFunc, self).__init__()
self.queue = queue.Queue()
self.timeout = loop_time
self.parent = None
self.stop_on_complete = None
self.running = False
self._stop = threading.Event()
def start_thread(self, parent, stop_on_complete=False):
# thread can wait for functions if not stop_on_complete
self.parent = parent
self.stop_on_complete = stop_on_complete
self.running = True
self.start()
def put_function(self, function, *args, **kwargs):
# put another function in queue
self.queue.put((function, args, kwargs))
def run(self):
print('### STARTED ###')
while self.running:
try:
function, args, kwargs = self.queue.get(timeout=self.timeout)
print('### RUNNING ###')
function(*args, **kwargs)
except queue.Empty:
if self.stop_on_complete:
self.stop()
else:
self.idle()
def stop(self):
print('### STOPPED ###')
self.running = False
self._stop.set()
#staticmethod
def idle():
print('### IDLE ###')
class App(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.resizable(width=False, height=False)
self.minsize(width=400, height=25)
self.wm_title('Another SO Example with progressbar')
self.queue = queue.Queue()
self.thread = None
self.in_work = False
self.mpb_frame = tk.Frame(self)
self.mpb = ttk.Progressbar(self.mpb_frame, orient='horizontal', length=400, mode='determinate')
self.mpb.pack()
self.mpb_frame.pack()
self.mpb.bind('<Map>', self.start_example)
def start_example(self, event=None):
if self.in_work:
return
self.in_work = True
self.spawn_thread(sum_func, 4, 3, self.queue)
def spawn_thread(self, command, *args):
# spawn a thread
self.thread = ThreadFunc()
self.thread.start_thread(self, True)
self.thread.put_function(command, *args)
self.periodic_call()
def periodic_call(self):
# check if our thread is running and if so - update progressbar
self.check_queue()
try:
self.thread.is_alive()
self.after(100, self.periodic_call)
except TypeError:
self.in_work = False
self.quit()
def check_queue(self):
# "transfer" messages to mpb-progressbar steps (10 iteration over you sum)
while self.queue.qsize():
try:
self.queue.get(0)
self.mpb.step(10)
except queue.Empty:
pass
def sum_func(a, b, queue_local):
# your sum function
for i in range(10):
c = a + b
time.sleep(1)
queue_local.put(c)
print('Sum', c)
app = App()
app.mainloop()
Links:
Running Functions as Threads in Python
How to connect a progress bar to a function?
linking tkinter progress bar to function

Related

How to create a tkinter quit button that works for all the functions in the class?

Suppose I have a class and it has a lot of functions in it.
I wanted to create a quit button using tkinter that will work for all the functions in the class.
Is there any easy way to do it? Here is what I have tried.
import threading
import tkinter as tk
import time
def threaded(fn):
def wrapper(*args, **kwargs):
thread = threading.Thread(target=fn, args=args, kwargs=kwargs)
thread.start()
e = threading.Event()
return thread,e
return wrapper
class MyClass:
#threaded
def func_to_be_threaded(self):
i = 10
while i > 1:
print("xyz")
time.sleep(2)
i -= 1
def stopbutton(thread,e):
def _quit():
print("Exiting...")
e.set()#set the flag that will kill the thread when it has finished
thread.join()
root.quit()
root.destroy()
root = tk.Tk()
QuitButton = tk.Button(master=root, text="Quit", command=_quit) # the quit button
QuitButton.pack(side=tk.BOTTOM)
root.mainloop()
my_obj = MyClass()
handle,e = my_obj.func_to_be_threaded()
stopbutton(handle,e)
It doesnt exit the function though
Your threaded function does not check e (the instance of Event()), so why do you expect that the threaded function will be terminated when e.set() is called?
You need to pass e to the threaded function and check whether it is set. If it is set, break out the while loop. I would suggest to pass e as one of the "kwargs" as below:
import threading
import tkinter as tk
import time
def threaded(fn):
def wrapper(*args, **kwargs):
# create the Event() object and save it in the kwargs
e = threading.Event()
kwargs["EventObject"] = e # choose any keyword you want
thread = threading.Thread(target=fn, args=args, kwargs=kwargs)
thread.start()
return thread, e
return wrapper
class MyClass:
#threaded
def func_to_be_threaded(self, *args, **kwargs): # added *args and **kwargs
i = 10
while i > 1 and not kwargs["EventObject"].is_set():
print("xyz")
time.sleep(2)
i -= 1
def stopbutton(thread, e):
def _quit():
print("Exiting...")
e.set()#set the flag that will kill the thread when it has finished
thread.join()
#root.quit() # calling root.destroy() is enough
root.destroy()
root = tk.Tk()
QuitButton = tk.Button(master=root, text="Quit", command=_quit) # the quit button
QuitButton.pack(side=tk.BOTTOM)
root.mainloop()
my_obj = MyClass()
handle, e = my_obj.func_to_be_threaded()
stopbutton(handle, e)

python script runs after x seconds but when tkinter code inserted it runs only once

What is the best way to repeatedly execute a function every x seconds in Python?
i have tried the solutions posted on above links but none helped me to achieve the desired result.
the code above prints "Doing stuff..." on console many times as per seconds mentioned i.e. 5 but when i add the line of window() which is a tkinter code for displaying a message the code runs just once and not anytime again .
please help . i want to run the tkinter code again and again on specific time as per system clock but now i am just trying to execute it after x amounts of seconds .
any help would really mean a lot to me.Thanks
search for tkinter .after method.
This will alow you to run a command every x seconds.
The problem your tkinter code runs only once, is since its set up first and then goes into a loop, (root.mainloop()) , hence never returning to your code to display anything again.
Example : tkinter: how to use after method
I think you need thread and queue...let me show a little demo.
I've set time.sleep(1) per seconds in the thead class.
In this matter you get two advantages, first repet your funcion any times
you desire and second your program never freeze it self.
import tkinter as tk
import threading
import queue
import datetime
import time
class MyThread(threading.Thread):
def __init__(self, queue,):
threading.Thread.__init__(self)
self.queue = queue
self.check = True
def stop(self):
self.check = False
def run(self):
while self.check:
x = "Doing stuff.. "
y = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
msg = x+y
time.sleep(1)
self.queue.put(msg)
class App(tk.Frame):
def __init__(self,):
super().__init__()
self.master.title("Hello World")
self.master.protocol("WM_DELETE_WINDOW",self.on_close)
self.queue = queue.Queue()
self.my_thread = None
self.init_ui()
def init_ui(self):
self.f = tk.Frame()
w = tk.Frame()
tk.Button(w, text="Start", command=self.launch_thread).pack()
tk.Button(w, text="Stop", command=self.stop_thread).pack()
tk.Button(w, text="Close", command=self.on_close).pack()
w.pack(side=tk.RIGHT, fill=tk.BOTH, expand=0)
self.f.pack(side=tk.LEFT, fill=tk.BOTH, expand=0)
def launch_thread(self):
if (threading.active_count()!=0):
self.my_thread = MyThread(self.queue)
self.my_thread.start()
self.periodiccall()
def stop_thread(self):
if(threading.active_count()!=1):
self.my_thread.stop()
def periodiccall(self):
self.checkqueue()
if self.my_thread.is_alive():
self.after(1, self.periodiccall)
else:
pass
def checkqueue(self):
while self.queue.qsize():
try:
ret = self.queue.get(0)
msg = "%s"%(ret)
print(msg)
except queue.Empty:
pass
def on_close(self):
if(threading.active_count()!=1):
self.my_thread.stop()
self.master.destroy()
if __name__ == '__main__':
app = App()
app.mainloop()

Tkinter progress bar [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 years ago.
Improve this question
I looked through the internet and couldn't really get an answer, if I have a process that is heavy on CPU usage, and I would like to present a progress bar while it's running (so the program will not get in the state of "not responding") on another thread probably, how do I do that?
The solution should be something like:
You have at least 2 threads, threadA and threadB.
ThreadA is the thread that calls mainloop() and where the progress bar lives.
ThreadB is the thread where your heavy process do his stuff.
When every thing starts:
ThreadB put a message in a shared queue.
(this way ThreadB tells ThreadA it is not in "not responding" state)
ThreadA get the message from the shared queue and updates the progress bar
(this way ThreadA tells the user that ThreadB is not in "not responding" state)
This is a very general solution pattern.
If you try to write down some code that implements this pattern, post it and I will try to help.
Here some reference:
for the threads: https://docs.python.org/3/tutorial/stdlib2.html#multi-threading
for the shared queue: https://docs.python.org/3/library/queue.html#module-queue
EDIT
Here a complete example of the general solution.
I hope this helps.
import tkinter as tk
from tkinter import ttk
import threading
import queue
import time
shared_queue = queue.Queue()
def thread1_main():
tot_time = 5 # seconds
elapsed_time = 0
while(True):
time.sleep(1)
shared_queue.put("I'm working")
elapsed_time += 1
if(elapsed_time > tot_time):
break;
shared_queue.put("task done")
def updates_progress_bar():
try:
msg = shared_queue.get(block=False)
except queue.Empty:
msg = None
else:
# do update the progress bar here
delta = 1
val = bar_value.get()+delta
val = val if val <= bar["maximum"] else 0
bar_value.set(val)
if(msg != "task done"):
root.after(500, updates_progress_bar)
else:
print(msg)
if(__name__ == "__main__"):
root = tk.Tk()
root.wm_geometry("200x200")
root.rowconfigure(0, weight=1)
root.columnconfigure(0, weight=1)
bar_value = tk.IntVar()
bar = ttk.Progressbar( root,
maximum=3, length=100, variable=bar_value)
bar.grid(row=0, column=0, sticky="we", padx=5)
shared_queue = queue.Queue()
thread1 = threading.Thread(target=thread1_main)
thread1.deamon = True
thread1.start()
root.after(500, updates_progress_bar)
root.mainloop()
print("that's all folks")
This is a primitive progress bar implementation with an indefinite mode of operation. It does not run in a thread as it really should. If you integrate this in a thread that is NOT doing the heavy lifting, and send a stop message to this thread, then the process will stop. This example uses a timer to stop the progress bar, but you can use any event just as well.
import tkinter as tk
from tkinter import ttk
import sys
class MyProgress(tk.Frame):
def __init__(self, parent, **kwargs):
tk.Frame.__init__(self, parent)
self.pack()
label = tk.Label(self, text="Progress:", anchor='w')
label.pack(side='left')
self.progress = ttk.Progressbar(self)
for attribute,value in kwargs.items():
try:
self.progress[attribute] = value
except:
print("Attribute error:", attribute)
print("Try one of:")
print(self.progress.configure())
sys.exit()
self.progress.pack(side='left')
self.running = False
self.parent = parent
def start(self):
self.running = True
self.increaseProgress()
def stop(self):
self.running = False
print("End of Job")
def increaseProgress(self):
if not self.running:
return
self.progress['value'] += 1
self.parent.after(100, self.increaseProgress)
if __name__ == '__main__':
root = tk.Tk()
app = MyProgress(root, mode='indeterminate', maximum=10, length=100, value=0)
app.start()
# Trigger a stop after 5 seconds
root.after(5000, app.stop)
root.mainloop()

Calling Tkinter class from another class (Python on Raspberry Pi)

I am stumped as to why the Display class isn't running in the following code. I call it using Display(root) in main(). The main class runs just fine. I don't get any errors in terminal.
#!/usr/bin/python
import alsaaudio as aa
import audioop
import Tkinter as tk
import tkFont
class Display():
def __init__(self, parent):
self.parent = parent
self._geom = '200x200+0+0'
parent.geometry("{0}x{1}+0+0".format(parent.winfo_screenwidth(), parent.winfo_screenheight()))
parent.overrideredirect(1)
parent.title('Listen')
parent.configure(background='#000000')
parent.displayFont = tkFont.Font(family="Unit-Bold", size=150)
def printMessage(self, parent, messageString):
self.message = tk.Message(self.parent, text=messageString, bg="#000000", font=parent.displayFont, fg="#777777", justify="c")
self.message.place(relx=.5, rely=.5, anchor="c")
def main():
root = tk.Tk()
window = Display(root)
# Set up audio
data_in = aa.PCM(aa.PCM_CAPTURE, aa.PCM_NONBLOCK, 'hw:1')
data_in.setchannels(2)
data_in.setrate(44100)
data_in.setformat(aa.PCM_FORMAT_S16_LE)
data_in.setperiodsize(256)
while True:
# Read data from device
l,data = data_in.read()
if l:
# catch frame error
try:
max_vol=audioop.rms(data,2)
scaled_vol = max_vol//4680
print scaled_vol
window.printMessage(root, scaled_vol)
except audioop.error, e:
if e.message !="not a whole number of frames":
raise e
root.mainloop()
if __name__ == '__main__':
main()
Also, both classes run perfectly separately. I'm just having problems using them together.
I think it's because there is an infinite loop (while True) that does not allow the execution to reach to Tk event loop root.mainloop().
Using threads might help to solve this issue. You can run the data input loop in a separate thread, and run Tk event loop in the main thread as it is.
Update: I ran the code (Python 2.7.6 on Linux 3.13.0-24-generic #47-Ubuntu SMP, pyalsaaudio==0.8.2) and it was showing the data. However it could be possible that using threads on different platforms behave differently. I'm having doubts about threads in this case now, because in the comments you mentioned that even the print statement is not being called. Another approach is not to use threads and the while True loop, but to use the mainloop from Tkinter itself.
Also please not that Display.printMessage is creating a new message widget on each call. This will end up with many message widgets showing (and occupying memory). To avoid this, I'd suggest using Tkinter variables, as used on the second code example using Tkinter mainloop.
Solution 1 Using threads (minimum change to code in the question):
#!/usr/bin/python
import alsaaudio as aa
import audioop
import Tkinter as tk
import tkFont
from threading import Thread, Event
class Display():
def __init__(self, parent):
self.parent = parent
self._geom = '200x200+0+0'
parent.geometry("{0}x{1}+0+0".format(parent.winfo_screenwidth(), parent.winfo_screenheight()))
parent.overrideredirect(1)
parent.title('Listen')
parent.configure(background='#000000')
parent.displayFont = tkFont.Font(family="Unit-Bold", size=150)
def printMessage(self, messageString):
self.message = tk.Message(self.parent, text=messageString, bg="#000000", font=self.parent.displayFont, fg="#777777", justify="c")
self.message.place(relx=.5, rely=.5, anchor="c")
def setup_audio(window, stop_event):
data_in = aa.PCM(aa.PCM_CAPTURE, aa.PCM_NONBLOCK, 'hw:1')
data_in.setchannels(2)
data_in.setrate(44100)
data_in.setformat(aa.PCM_FORMAT_S16_LE)
data_in.setperiodsize(256)
while not stop_event.is_set():
# Read data from device
l,data = data_in.read()
if l:
# catch frame error
try:
max_vol=audioop.rms(data,2)
scaled_vol = max_vol//4680
print scaled_vol
window.printMessage(scaled_vol)
except audioop.error, e:
if e.message !="not a whole number of frames":
raise e
def main():
root = tk.Tk()
window = Display(root)
stop_event = Event()
audio_thread = Thread(target=setup_audio, args=[window, stop_event])
audio_thread.start()
try:
root.mainloop()
finally:
stop_event.set()
audio_thread.join()
if __name__ == '__main__':
main()
Changes:
Moved reading sound loop into a separate function that accepts a threading Event to signal stop the loop
Removed the parent arg from Display.printMesssage() definition and use self.parent instead. So in the setup_autdio function when calling the printMessage(), there is no need to pass in root as an argument anymore.
Solution 2 Using Tkinter mainloop, and run all in the main thread (note that I also changed the Display.printMessage() class, mainly for performance improvements):
#!/usr/bin/python
import alsaaudio as aa
import audioop
import Tkinter as tk
import tkFont
class Display():
def __init__(self, parent):
self.parent = parent
self._geom = '200x200+0+0'
parent.geometry("{0}x{1}+0+0".format(parent.winfo_screenwidth(), parent.winfo_screenheight()))
parent.overrideredirect(1)
parent.title('Listen')
parent.configure(background='#000000')
parent.displayFont = tkFont.Font(family="Unit-Bold", size=150)
# using a Tkinter variable to display different messages, on the same widget
self.messageVar = tk.StringVar()
self.message = tk.Message(self.parent, textvar=self.messageVar, bg="#000000", font=self.parent.displayFont, fg="#777777", justify="c")
self.message.place(relx=.5, rely=.5, anchor="c")
def printMessage(self, messageString):
self.messageVar.set(messageString)
def update_ui_from_audio(data_in, window):
l, data = data_in.read()
if l:
# catch frame error
try:
max_vol=audioop.rms(data,2)
scaled_vol = max_vol//4680
print scaled_vol
window.printMessage(scaled_vol)
except audioop.error, e:
if e.message !="not a whole number of frames":
raise e
def main():
root = tk.Tk()
window = Display(root)
# Set up audio
data_in = aa.PCM(aa.PCM_CAPTURE, aa.PCM_NONBLOCK, 'hw:1')
data_in.setchannels(2)
data_in.setrate(44100)
data_in.setformat(aa.PCM_FORMAT_S16_LE)
data_in.setperiodsize(256)
def audio_read_loop():
update_ui_from_audio(data_in, window)
root.after(1, audio_read_loop)
root.after(1, audio_read_loop)
root.mainloop()
if __name__ == '__main__':
main()
Changes:
Move the code that reads data from PCM device, and updates the UI into a separate function named update_ui_from_audio
Display.printMessage uses a StringVar to update the message, so only 1 message widget is used.
Replace the while True loop, with another function that calls the update_ui_from_audio function, and asks Tkinter to call it again in a milisecond (hence, acting like a loop).

How to connect a progress bar to a function?

I'm trying to connect a progress bar to a function for my project.
This is what I have so far but im pretty sure it does nothing:
def main():
pgBar.start()
function1()
function2()
function3()
function4()
pgBar.stop()
Here is the code where I make my progress bar if that helps at all:
pgBar = ttk.Progressbar(window, orient = HORIZONTAL, length=300, mode = "determinate")
pgBar.place(x=45, y=130)
I have been doing some research and understand that the tkinter window freezes when running a function or something like that. Is there a way I could "unfreeze" the window at the end of each function that is called inside the main one?
Since tkinter is single threaded, you need another thread to execute your main function without freezing the GUI. One common approach is that the working thread puts the messages into a synchronized object (like a Queue), and the GUI part consumes this messages, updating the progress bar.
The following code is based on a full detailed example on ActiveState:
import tkinter as tk
from tkinter import ttk
import threading
import queue
import time
class App(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.queue = queue.Queue()
self.listbox = tk.Listbox(self, width=20, height=5)
self.progressbar = ttk.Progressbar(self, orient='horizontal',
length=300, mode='determinate')
self.button = tk.Button(self, text="Start", command=self.spawnthread)
self.listbox.pack(padx=10, pady=10)
self.progressbar.pack(padx=10, pady=10)
self.button.pack(padx=10, pady=10)
def spawnthread(self):
self.button.config(state="disabled")
self.thread = ThreadedClient(self.queue)
self.thread.start()
self.periodiccall()
def periodiccall(self):
self.checkqueue()
if self.thread.is_alive():
self.after(100, self.periodiccall)
else:
self.button.config(state="active")
def checkqueue(self):
while self.queue.qsize():
try:
msg = self.queue.get(0)
self.listbox.insert('end', msg)
self.progressbar.step(25)
except Queue.Empty:
pass
class ThreadedClient(threading.Thread):
def __init__(self, queue):
threading.Thread.__init__(self)
self.queue = queue
def run(self):
for x in range(1, 5):
time.sleep(2)
msg = "Function %s finished..." % x
self.queue.put(msg)
if __name__ == "__main__":
app = App()
app.mainloop()
Since the original example on ActiveState is a bit messy IMO (the ThreadedClient is quite coupled with the GuiPart, and things like controlling the moment to spawn the thread from the GUI are not as straightforward as they could be), I have refactored it and also added a Button to start the new thread.
To understand the 'freezing' you need to understand mainloop(). Calling this method starts the tkinter event loop. The main thread is responsible for this loop. Therefore, when your work intensive function runs in the main thread, it is also interfering with the mainloop. To prevent this you can use a secondary Thread to run your function. It's recommended that secondary threads are not given access to tkinter objects. Allen B.Taylor, author of mtTkinter, states:
The problems stem from the fact that the _tkinter module attempts to
gain control of the main thread via a polling technique when
processing calls from other threads.
If it succeeds, all is well. If it fails (i.e., after a timeout), the
application receives an exception with the message: "RuntimeError:
main thread is not in main loop".
You can have the secondary thread put information into a Queue. Then have a function that checks the Queue every x milliseconds, within the mainloop, via the after() method.
First, decide what you want the value of the Progressbar's maximum option to be.
This is the Progressbar's maximum indicator value (how many units are required to fill the Progressbar).
For example, you could set maximum=4 and then put the appropriate indicator value into the Queue after each of your four functions. The main thread can then retrieve these values (from the Queue) to set the progress via a tkinter.IntVar().
(Note that if you use progbar.step(), the Progressbar resets to 0 (empty) at the end, instead of reaching 4 (completely filled).)
Here's a quick look at how you can use a tkinter.IntVar() with a Progressbar:
int_var = tkinter.IntVar()
pb_instance = ttk.Progressbar(root, maximum=4)
pb_instance['variable'] = int_var
pb_instance.pack()
# completely fill the Progressbar
int_var.set(4)
# get the progress value
x = int_var.get()
Here's an example based on your own (renamed the "main" function "arbitrary"):
import time
import threading
try: import tkinter
except ImportError:
import Tkinter as tkinter
import ttk
import Queue as queue
else:
from tkinter import ttk
import queue
class GUI_Core(object):
def __init__(self):
self.root = tkinter.Tk()
self.int_var = tkinter.IntVar()
progbar = ttk.Progressbar(self.root, maximum=4)
# associate self.int_var with the progress value
progbar['variable'] = self.int_var
progbar.pack()
self.label = ttk.Label(self.root, text='0/4')
self.label.pack()
self.b_start = ttk.Button(self.root, text='Start')
self.b_start['command'] = self.start_thread
self.b_start.pack()
def start_thread(self):
self.b_start['state'] = 'disable'
self.int_var.set(0) # empty the Progressbar
self.label['text'] = '0/4'
# create then start a secondary thread to run arbitrary()
self.secondary_thread = threading.Thread(target=arbitrary)
self.secondary_thread.start()
# check the Queue in 50ms
self.root.after(50, self.check_que)
def check_que(self):
while True:
try: x = que.get_nowait()
except queue.Empty:
self.root.after(25, self.check_que)
break
else: # continue from the try suite
self.label['text'] = '{}/4'.format(x)
self.int_var.set(x)
if x == 4:
self.b_start['state'] = 'normal'
break
def func_a():
time.sleep(1) # simulate some work
def func_b():
time.sleep(0.3)
def func_c():
time.sleep(0.9)
def func_d():
time.sleep(0.6)
def arbitrary():
func_a()
que.put(1)
func_b()
que.put(2)
func_c()
que.put(3)
func_d()
que.put(4)
que = queue.Queue()
gui = GUI_Core() # see GUI_Core's __init__ method
gui.root.mainloop()
If all you want is something that indicates to the user that there is activity
you can set the Progressbar's mode option to 'indeterminate'.
The indicator bounces back and forth in this mode (the speed relates to the maximum option).
Then you can call the Progressbar's start() method directly before starting the secondary thread;
and then call stop() after secondary_thread.is_alive() returns False.
Here's an example:
import time
import threading
try: import tkinter
except ImportError:
import Tkinter as tkinter
import ttk
else: from tkinter import ttk
class GUI_Core(object):
def __init__(self):
self.root = tkinter.Tk()
self.progbar = ttk.Progressbar(self.root)
self.progbar.config(maximum=4, mode='indeterminate')
self.progbar.pack()
self.b_start = ttk.Button(self.root, text='Start')
self.b_start['command'] = self.start_thread
self.b_start.pack()
def start_thread(self):
self.b_start['state'] = 'disable'
self.progbar.start()
self.secondary_thread = threading.Thread(target=arbitrary)
self.secondary_thread.start()
self.root.after(50, self.check_thread)
def check_thread(self):
if self.secondary_thread.is_alive():
self.root.after(50, self.check_thread)
else:
self.progbar.stop()
self.b_start['state'] = 'normal'
def func_a():
time.sleep(1) # simulate some work
def func_b():
time.sleep(0.3)
def func_c():
time.sleep(0.9)
def func_d():
time.sleep(0.6)
def arbitrary():
func_a()
func_b()
func_c()
func_d()
gui = GUI_Core()
gui.root.mainloop()
→ Progressbar reference
You must be using:
self.pgBar.step(x)
where 'x' is the amount to be increased in progressbar.
for this to get updated in your UI you have to put
self.window.update_idletasks() after every self.pgBar.step(x) statement

Categories

Resources