Question: Tkinter Progressbar
update from multiple Thread
'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 from ttk.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