ttk Progressbar display only in the end of the process - python

As per object, the progress bar is displayed only at the end of the completion of the for loop. Instead I would like it to show the progress of the cycle step by step.
from tkinter import ttk
from tkinter import *
import time
def inner_loop_func():
k = 0
for i in range(10**5):
k=k+1
print("k: ",k)
def loop_fun():
p = ttk.Progressbar(root, orient="horizontal", length=300, mode="determinate", takefocus=True, maximum=100)
p['value'] = 0
p.pack()
end = 100
for i in range(end):
start(end,p)
inner_loop_func()
print(i," of ", end)
def start(end,p):
if p['value'] < 300:
p['value'] += (300/end)
else:
print("finish")
if __name__ == "__main__":
root = Tk()
loop_fun()
root.mainloop()

Use p.update() inside the loop_fun function for loop:
for i in range(end):
start(end, p)
inner_loop_func()
print(i, " of ", end)
p.update()

Related

How to update Tkinter's progressbar with urlretrieve?

What i want to do is a program that downloads a file from a URL with a given filename and see its progress with a progressbar but when i launch the dl function , the progressbar doesn't move.
Can you please help me with updating the progressbar in relation to urlretrieve's reporthook function?
Code below :
import tkinter as tk
import tkinter.ttk as ttk
from urllib.request import urlretrieve
window = tk.Tk()
window.geometry('300x300')
window.minsize(300, 300)
window.maxsize(300, 300)
window.title('Downloader')
lbl_url = tk.Label(window)
lbl_url.configure(text='URL', font='{Arial} {9}')
lbl_url.pack()
ent_url = tk.Entry(window)
ent_url.configure(font='{Arial} {9}')
ent_url.pack()
lbl_file = tk.Label(window)
lbl_file.configure(text='Filename', font='{Arial} {9}')
lbl_file.pack()
ent_file = tk.Entry(window)
ent_file.configure(font='{Arial} {9}')
ent_file.pack()
progressbar = ttk.Progressbar(window)
progressbar.configure(length=225, orient='horizontal', mode='determinate', maximum=100)
progressbar.pack(side='bottom', pady=10, padx=10)
def report(count, block_size, total_size):
value = 0
while value <= 100.0:
progress_size = float(count * block_size)
percent = float(progress_size * 100 / total_size)
value = percent
progressbar['value'] = value
time.sleep(0.1)
window.update()
def dl():
urlretrieve(ent_url.get(), ent_file.get(), report)
btn_dl = tk.Button(window)
btn_dl.configure(text='Download', font='{Arial} {9}', command=dl)
btn_dl.pack(side='bottom')
window.mainloop()```
What is the purpose of this while value <= 100.0: loop?
After a chunk is downloaded, the report() function will be called. We do not need to run a while loop.
Here is the working report() -
def report(count, block_size, total_size):
progress_size = count * block_size
percent = progress_size * 100 / total_size
progressbar['value'] = percent
window.update()

Clock and counter in the same window

I can't remove a label from this code at the end of countdown
the idea is to have a clock and a countdown timer for an event and at the end of the counter I want it to disappear.
from tkinter import *
import time
win = Tk()
win.geometry('400x400')
frame=Frame(win)
frame.grid()
labelTD=Label(frame)
labelTD.grid(row=2,column=0)
def clock():
t=time.strftime('%A''\n''%D''\n''%I:%M:%S',time.localtime())
if t!='':
labelTD.config(text=t,font='infra 50 bold',foreground='black',background='white')
labelTD.update_idletasks()
labelTD.after(1000,clock)
def countdown():
for t in range(12,-1,-1):
Mn = t % 60
Hr = t // 60
if int(Hr)<10 and int(Mn)<10:
xmn = str(Mn).zfill(2)
xhr = str(Hr).zfill(2)
label1 = Label(frame, text=(xhr+':'+xmn))
label1.grid(row=1)
else:
label1 = Label(frame, text=(str(Hr).zfill(2)+':'+str(Mn)))
label1.grid(row=1)
frame.update()
time.sleep(1)
labelTD.grid(row=2,column=0)
clock()
countdown()
win.mainloop()
You should use after() for the countdown as well and create the label for countdown outside the function:
labelCD = Label(frame)
labelCD.grid()
def countdown(n):
hr, mn = divmod(n, 60)
labelCD.config(text=f"{hr:02}:{mn:02}")
if n > 0:
labelCD.after(1000, countdown, n-1)
else:
labelCD.destroy()
countdown(12)
Update: Show hh:mm:ss in countdown():
def countdown(n):
mn, secs = divmod(n, 60)
hr, mn = divmod(mn, 60)
labelCD.config(text=f"{hr:02}:{mn:02}:{secs:02}")
if n > 0:
labelCD.after(1000, countdown, n-1)
else:
labelCD.destroy()

Progress bar update in python

I search in stackoverflow how to implement a ProgressBar in tkinter. This is the code:
def worker(x):
return x+x
#time.sleep(1)
def compute():
pool = multiprocessing.Pool(2)
objects=range(1,10)
for i, _ in enumerate(pool.imap_unordered(worker, objects), 1):
#print("completed =" +str(i/len(listF)))
scanned.set((i/len(objects)*100))
if __name__ == '__main__':
window = Tk()
frame = Frame(master = window)
frame.grid(row=0,column=0)
button2 = Button(master=frame,text="Start", command=compute)
button2.pack()
frame = Frame(master = window)
scanned = IntVar()
frame.grid(row=3,column=0,padx=5,pady=10)
progress = Progressbar(master=frame, orient='horizontal',maximum=100, variable=scanned,mode='determinate')
progress.pack()
window.mainloop()
I found that this should work, but not in my case because the bar is update to 100% all in a time and I can not see the intermediate step.
Hi guys I solve the problem adding a Thread. This is the code:
def worker(x):
return x+x
#time.sleep(1)
def precompute():
t=threading.Thread(target=compute)
t.start()
def compute():
pool = multiprocessing.Pool(2)
objects=range(1,1000)
for i, _ in enumerate(pool.imap(worker, objects), 1):
#print("completed =" +str(i/len(listF)))
scanned.set((i/len(objects)*100))
if __name__ == '__main__':
window = Tk()
frame = Frame(master = window)
frame.grid(row=0,column=0)
button2 = Button(master=frame,text="Start", command=precompute)
button2.pack()
frame = Frame(master = window)
scanned = IntVar()
frame.grid(row=3,column=0,padx=5,pady=10)
progress = Progressbar(master=frame, orient='horizontal',maximum=100, variable=scanned,mode='determinate')
progress.pack()
window.mainloop()

Print letter by letter using Tkinter

Is there a way to print letter by letter (redraw between frame update) in Python 3.4.1 using Tkinter?
This is doable in the normal IDLE window using this piece of code:
import time
string = "hello world"
for char in string:
print(char, end='')
time.sleep(.25)
But I can't do this in GUI Tkinter window. In my opinion, I could display a label on to the tkinter window, add a letter from a string into it, and wait for a period of time before adding another letter (about 0.25 secs), with the same idea as the code above.
Any solutions?
For example:
String To Display = 'abc'
First frame : 'a'
Second frame : 'ab'
Third Frame : 'abc'
Thanks in advance!
You didn't specify much so I just guessed you'd like something like this:
import time
import tkinter as tk
def printString(string):
for char in string:
Label.configure(text=Label.cget('text') + char)
Label.update()
time.sleep(.25)
root = tk.Tk()
text = "Hello world!"
Label = tk.Label(root)
Label.pack()
printString(text)
root.mainloop()
UPDATE:
Same principle but for a canvas:
import time
import tkinter as tk
def printString(item, string):
if item:
for char in string:
idx = c.index(item, tk.END)
c.insert(item, idx, char)
c.update()
time.sleep(.25)
root = tk.Tk()
text = "Hello world!"
c = tk.Canvas(root)
c_txt = c.create_text(50,50)
c.pack()
c.update()
printString(c_txt, text)
root.mainloop()
You can do this using after to call a method periodically to update the GUI. time.sleep is not as easy to work with as it blocks main-thread which the GUI runs on.
You could periodically replace the label's single character text with next letter in the string:
try:
import tkinter as tk
except:
import Tkinter as tk
def update_label_letter():
global label
label['text'] = label.string[label.current_char_index]
label.current_char_index += 1
label.current_char_index %= len(label.string)
label.after(250, update_label_letter)
if __name__ == '__main__':
root = tk.Tk()
label = tk.Label(root)
label.string = "Hello World!"
label.current_char_index = 0
root.after(250, update_label_letter)
label.pack()
root.mainloop()
One way would be to periodically append the letters of a single label.
Vertical:
try:
import tkinter as tk
except:
import Tkinter as tk
def update_label_letters():
global label
_no_of_space_replacements = label['text'].count('\n')
_last_char_index = len(label['text']) - _no_of_space_replacements
if _last_char_index < len(label.string):
label['text'] += label.string[_last_char_index].replace(" ", " \n")
else:
label['text'] = ""
label.after(250, update_label_letters)
if __name__ == '__main__':
root = tk.Tk()
label = tk.Label(root, wraplength=1)
label.string = "Hello World!"
root.after(250, update_label_letters)
label.pack()
root.mainloop()
Horizontal:
try:
import tkinter as tk
except:
import Tkinter as tk
def update_label_letters():
global label
_last_char_index = len(label['text'])
if _last_char_index < len(label.string):
label['text'] += label.string[_last_char_index]
else:
label['text'] = ""
label.after(250, update_label_letters)
if __name__ == '__main__':
root = tk.Tk()
label = tk.Label(root)
label.string = "Hello World!"
root.after(250, update_label_letters)
label.pack()
root.mainloop()
Another way would be to write the letters periodically to empty but shown letter_labels.
Vertical:
try:
import tkinter as tk
except:
import Tkinter as tk
def update_label_letters():
global current_char_index, my_string, letter_labels
if current_char_index < len(my_string):
_current_char = my_string[current_char_index]
letter_labels[current_char_index]['text'] = _current_char
letter_labels[current_char_index].after(250, update_label_letters)
current_char_index += 1
if __name__ == '__main__':
root = tk.Tk()
my_string = "Hello World!"
letter_labels = list()
for index, value in enumerate(my_string):
letter_labels.append(tk.Label(root))
letter_labels[index].pack()
current_char_index = 0
root.after(250, update_label_letters)
root.mainloop()
Horizontal:
try:
import tkinter as tk
except:
import Tkinter as tk
def update_label_letters():
global current_char_index, my_string, letter_labels
if current_char_index < len(my_string):
_current_char = my_string[current_char_index]
letter_labels[current_char_index]['text'] = _current_char
letter_labels[current_char_index].after(250, update_label_letters)
current_char_index += 1
if __name__ == '__main__':
root = tk.Tk()
my_string = "Hello World!"
letter_labels = list()
for index, value in enumerate(my_string):
letter_labels.append(tk.Label(root))
letter_labels[index].pack(side='left')
current_char_index = 0
root.after(250, update_label_letters)
root.mainloop()
Another way would be to display the letter_labels periodically.
Vertical:
try:
import tkinter as tk
except:
import Tkinter as tk
def show_label():
global current_char_index, my_string, letter_labels
if current_char_index < len(my_string):
letter_labels[current_char_index].pack()
letter_labels[current_char_index].after(250, show_label)
current_char_index += 1
if __name__ == '__main__':
root = tk.Tk()
my_string = "Hello World!"
letter_labels = list()
for _char in my_string:
letter_labels.append(tk.Label(root, text=_char))
current_char_index = 0
root.after(250, show_label)
root.mainloop()
Horizontal:
try:
import tkinter as tk
except:
import Tkinter as tk
def show_label():
global current_char_index, my_string, letter_labels
if current_char_index < len(my_string):
letter_labels[current_char_index].pack(side='left')
letter_labels[current_char_index].after(250, show_label)
current_char_index += 1
if __name__ == '__main__':
root = tk.Tk()
my_string = "Hello World!"
letter_labels = list()
for _char in my_string:
letter_labels.append(tk.Label(root, text=_char))
current_char_index = 0
root.after(250, show_label)
root.mainloop()

Making a countdown timer with Python and Tkinter?

I want to set a label in Tkinter using my countdown timer function. Right now all it does is set the lable to "10" once 10 is reached and I don't really understand why. Also, even if I have the timer print to a terminal instead the "Time's up!" bit never prints.
import time
import tkinter as tk
class App():
def __init__(self):
self.root = tk.Tk()
self.label = tk.Label(text="null")
self.label.pack()
self.countdown()
self.root.mainloop()
# Define a timer.
def countdown(self):
p = 10.00
t = time.time()
n = 0
# Loop while the number of seconds is less than the integer defined in "p"
while n - t < p:
n = time.time()
if n == t + p:
self.label.configure(text="Time's up!")
else:
self.label.configure(text=round(n - t))
app=App()
Tkinter already has an infinite loop running (the event loop), and a way to schedule things to run after a period of time has elapsed (using after). You can take advantage of this by writing a function that calls itself once a second to update the display. You can use a class variable to keep track of the remaining time.
import Tkinter as tk
class ExampleApp(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
self.label = tk.Label(self, text="", width=10)
self.label.pack()
self.remaining = 0
self.countdown(10)
def countdown(self, remaining = None):
if remaining is not None:
self.remaining = remaining
if self.remaining <= 0:
self.label.configure(text="time's up!")
else:
self.label.configure(text="%d" % self.remaining)
self.remaining = self.remaining - 1
self.after(1000, self.countdown)
if __name__ == "__main__":
app = ExampleApp()
app.mainloop()

Categories

Resources