Reputation: 386
I am trying to display downloaded images in tkinter Progressbar
, It is working but the progressbar finishes way before the all images are downloaded. I asked a very similar question tkinter updating progress bar on thread progress, My idea is to update progressbar depending on how many files were created using len(os.listdir('.'))
to count.
import tkinter
from tkinter.ttk import Progressbar
import os,uuid,requests,threading
import numpy as np
def bar():
temp = 0
for lst in chunks:
threads.append(threading.Thread(target=download_image, args=(lst)))
for x in threads:
x.start()
while temp<len(links):
progress['value'] = temp
root.update_idletasks()
temp =len(os.listdir('.'))
print("closing threads")
for i in threads:
i.join()
temp =len(os.listdir('.'))
progress['value'] = temp
print('done')
root.destroy()
with open('image_urls.txt','r') as f:
links = f.read().split('\n') #links to image urls
threads =[]
chunks = [i.tolist() for i in np.array_split(links, 10) if i.size>0]
root = tkinter.Tk()
root.geometry("400x300")
root.title('Downloader v1')
progress = Progressbar(root, orient = tkinter.HORIZONTAL,
length = 250, mode = 'determinate',maximum=len(links))
progress.pack(pady = 100)
notice = tkinter.Label(root, text=str(len(links)),
fg="grey2",)
notice.place(x=350, y=100)
compose_button = tkinter.Button(root, text = 'Start', command = bar)
compose_button.pack()
root.mainloop()
Upvotes: 0
Views: 906
Reputation: 99
I see that you have accepted the answer above. However, I would post my answer since it is not based on threading, which I think is simpler and may be more suitable. I did not bother adapting my solution to your case since you have accepted the answer:
from tkinter import *
from tkinter.ttk import *
import os
root = Tk()
# I set the length and maximum as shown to demonstrate the process in the
# proceeding function. Pay attention to the increment r
progress = Progressbar(root, orient = HORIZONTAL,
length = 200/5, maximum=200/5, mode = 'determinate')
# Function
def my_func():
t=0
r= 1/5
for i in range(200):
print(i) #whatever function interests you
t=t+r
progress['value'] = t
root.update_idletasks()
progress.pack()
# Button
Button(root, text = 'Start', command = bar).pack(pady = 10)
mainloop()
Upvotes: 1
Reputation: 15513
Question: Tkinter
Progressbar
update from multipleThread
's
Core Point
.event_generate('<<Progressbar>>')
This example uses a virtual event '<<Progressbar>>'
to increment the Progressbar['value']
. This event driven progamming, needs, no callback, no polling .after
, no queue
, to work across Thread
's.
Imports:
import tkinter as tk
import tkinter.ttk as ttk
import threading, time
import random
The worker
Thread
class Task(threading.Thread):
is_alive = 0
def __init__(self, app, args, name):
super().__init__(name=name, daemon=True)
self.args = args[0]
self.app = app
self._lock = threading.Lock()
self.start()
def run(self):
# threaded task
with self._lock:
Task.is_alive += 1
time.sleep(0.01)
for link in self.args:
print('Thread[{}]: link:{}'.format(self.name, link))
time.sleep(random.randint(1, 5))
with self._lock:
self.app.event_generate('<<Progressbar>>', when='tail')
# on end of threaded task
with self._lock:
Task.is_alive -= 1
if Task.is_alive == 0:
# last Task has finished
self.app.event_generate('<<COMPLETED>>', when='tail')
Customized
Progressbar
by inheriting fromttk.Progressbar
class Progressbar(ttk.Progressbar):
def __init__(self, parent):
super().__init__(parent, orient="horizontal",
maximum=0, mode="determinate", length=250)
parent.bind('<<Progressbar>>', self.value)
def value(self, event):
self['value'] += 1
Usage:
class App(tk.Tk):
def __init__(self):
super().__init__()
self.pb = Progressbar(self)
self.pb.pack()
tk.Button(self, text="Start", command=self.start).pack()
self.bind('<<COMPLETED>>', self.on_completed)
def start(self):
links = (1, 2, 3, 4, 5, 6, 7, 8, 9)
self.pb['maximum'] = len(links)
chunks = [l for l in zip(links[0::3], links[1::3], links[2::3])]
for i, args in enumerate(chunks, 1):
# Task start() at once
Task(self, name='Task {}'.format(i), args=(args,))
def on_completed(self, event):
# Do cleanups before exiting
self.destroy()
if __name__ == "__main__":
App().mainloop()
Tested with Python: 3.5 - 'TclVersion': 8.6 'TkVersion': 8.6
Upvotes: 2