added stop and updated __repr__ of Stats
This commit is contained in:
parent
4dc1ab37d0
commit
9302f359bf
|
|
@ -5,7 +5,7 @@ import tkinter as tk
|
||||||
import tkinter.ttk as ttk
|
import tkinter.ttk as ttk
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from typing import Optional
|
from typing import Optional, Callable
|
||||||
|
|
||||||
|
|
||||||
class ClearableQueue(queue.Queue):
|
class ClearableQueue(queue.Queue):
|
||||||
|
|
@ -25,29 +25,41 @@ def acquire_timeout(lock, timeout):
|
||||||
lock.release()
|
lock.release()
|
||||||
|
|
||||||
|
|
||||||
|
def format_time(seconds):
|
||||||
|
m, s = divmod(seconds, 60)
|
||||||
|
h, m = divmod(m, 60)
|
||||||
|
return f'{str(int(h)) + "h " if h > 0 else ""}{"{:2d}min ".format(int(m)) if m > 0 else ""}{int(s):2d}s'
|
||||||
|
|
||||||
|
|
||||||
class AsyncProgress(ttk.Frame):
|
class AsyncProgress(ttk.Frame):
|
||||||
@dataclass(repr=False)
|
@dataclass(init=False, repr=False)
|
||||||
class Stats:
|
class Stats:
|
||||||
|
start_time: float
|
||||||
total: int
|
total: int
|
||||||
current: int = 0
|
current: int = 0
|
||||||
start_time: float = time.time()
|
|
||||||
last_elapsed_time: float = 0
|
last_elapsed_time: float = 0
|
||||||
total_elapsed_time: float = 0
|
total_elapsed_time: float = 0
|
||||||
|
|
||||||
|
def __init__(self, total: int):
|
||||||
|
self.total = total
|
||||||
|
self.start_time = time.time()
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
ips = 1 / self.last_elapsed_time if self.last_elapsed_time != 0 else 0
|
ips = 1 / self.last_elapsed_time if self.last_elapsed_time != 0 else 0
|
||||||
return (f"{'{v:{p}d}'.format(v=self.current, p=len(str(self.total)))}/{self.total} "
|
return (f"{'{v:{p}d}'.format(v=self.current, p=len(str(self.total)))}/{self.total} "
|
||||||
f"| {ips:.2f}it/s "
|
f"| {self.last_elapsed_time}s/it" if ips < 1 else f"{ips:.2f}it/s "
|
||||||
f"| elapsed: {time.time() - self.start_time:.2f} "
|
f"| elapsed: {format_time(time.time() - self.start_time)} "
|
||||||
f"| remaining: {(self.total - self.current) * self.last_elapsed_time:.2f} ")
|
f"| remaining: {format_time((self.total - self.current) * self.last_elapsed_time):} ")
|
||||||
|
|
||||||
class Range:
|
class Range:
|
||||||
def __init__(self, parent, iterable, update_interval):
|
def __init__(self, parent, iterable, iter_len, update_interval, stop_event):
|
||||||
self.iterable = iterable
|
self.iterable = iterable
|
||||||
|
self.iter_len = iter_len
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.update_interval = update_interval
|
self.update_interval = update_interval
|
||||||
|
self.stop_event = stop_event
|
||||||
|
|
||||||
self.parent.pbar['maximum'] = len(iterable)
|
self.parent.pbar['maximum'] = iter_len
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
last_time = time.time()
|
last_time = time.time()
|
||||||
|
|
@ -57,6 +69,8 @@ class AsyncProgress(ttk.Frame):
|
||||||
|
|
||||||
try:
|
try:
|
||||||
for obj in self.iterable:
|
for obj in self.iterable:
|
||||||
|
if self.stop_event.is_set():
|
||||||
|
return
|
||||||
yield obj
|
yield obj
|
||||||
|
|
||||||
stats.current += 1
|
stats.current += 1
|
||||||
|
|
@ -67,17 +81,17 @@ class AsyncProgress(ttk.Frame):
|
||||||
self.parent.step(1, stats)
|
self.parent.step(1, stats)
|
||||||
last_update_time = time.time()
|
last_update_time = time.time()
|
||||||
|
|
||||||
|
finally:
|
||||||
stats.total_elapsed_time = time.time() - stats.start_time
|
stats.total_elapsed_time = time.time() - stats.start_time
|
||||||
self.parent._finish(stats)
|
self.parent._finish(stats)
|
||||||
|
|
||||||
finally:
|
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def close(self):
|
def close(self):
|
||||||
self.iterable = range(0)
|
self.iterable = range(0)
|
||||||
|
|
||||||
def __init__(self, parent, *,
|
def __init__(self, parent, *,
|
||||||
width=450,
|
width=800,
|
||||||
height=30,
|
height=30,
|
||||||
update_interval=20,
|
update_interval=20,
|
||||||
range_update_interval=10,
|
range_update_interval=10,
|
||||||
|
|
@ -87,6 +101,10 @@ class AsyncProgress(ttk.Frame):
|
||||||
|
|
||||||
self.__event_step_queue = ClearableQueue()
|
self.__event_step_queue = ClearableQueue()
|
||||||
self.__lock = threading.Lock()
|
self.__lock = threading.Lock()
|
||||||
|
self.__cancel_event = threading.Event()
|
||||||
|
|
||||||
|
self.__stop_condition = None
|
||||||
|
|
||||||
self.__tk_pbar_value = tk.IntVar()
|
self.__tk_pbar_value = tk.IntVar()
|
||||||
self.__tk_stats_str = tk.StringVar()
|
self.__tk_stats_str = tk.StringVar()
|
||||||
self.__tk_stats_str.set("Not running")
|
self.__tk_stats_str.set("Not running")
|
||||||
|
|
@ -117,16 +135,33 @@ class AsyncProgress(ttk.Frame):
|
||||||
self.__tk_pbar_value.set(0)
|
self.__tk_pbar_value.set(0)
|
||||||
self.__tk_stats_str.set("Not running")
|
self.__tk_stats_str.set("Not running")
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.__cancel_event.set()
|
||||||
|
|
||||||
|
def stop_if(self, condition: Callable):
|
||||||
|
if (condition()):
|
||||||
|
self.stop()
|
||||||
|
else:
|
||||||
|
self.__stop_condition = condition
|
||||||
|
|
||||||
def range(self, start_stop, stop=None, step=1):
|
def range(self, start_stop, stop=None, step=1):
|
||||||
|
_start = start_stop
|
||||||
|
_stop = stop
|
||||||
|
if _stop is None:
|
||||||
|
_stop = start_stop
|
||||||
|
_start = 0
|
||||||
|
return self.iter(range(_start, _stop, step))
|
||||||
|
|
||||||
|
def iter(self, iterable, length=None):
|
||||||
|
if length is None:
|
||||||
|
length = len(iterable)
|
||||||
with self.__lock:
|
with self.__lock:
|
||||||
if self.running:
|
if self.running:
|
||||||
raise RuntimeError('Progressbar is already running')
|
raise RuntimeError('Progressbar is already running')
|
||||||
if stop is None:
|
return AsyncProgress.Range(self, iterable, length, self.range_update_interval, self.__cancel_event)
|
||||||
stop = start_stop
|
|
||||||
start_stop = 0
|
|
||||||
return AsyncProgress.Range(self, range(start_stop, stop, step), self.range_update_interval)
|
|
||||||
|
|
||||||
def _start(self):
|
def _start(self):
|
||||||
|
self.__cancel_event.clear()
|
||||||
with self.__lock:
|
with self.__lock:
|
||||||
self.running = True
|
self.running = True
|
||||||
|
|
||||||
|
|
@ -141,6 +176,9 @@ class AsyncProgress(ttk.Frame):
|
||||||
|
|
||||||
def __update_self(self):
|
def __update_self(self):
|
||||||
with acquire_timeout(self.__lock, 0.1):
|
with acquire_timeout(self.__lock, 0.1):
|
||||||
|
if self.running and self.__stop_condition is not None and self.__stop_condition():
|
||||||
|
self.stop()
|
||||||
|
|
||||||
while not self.__event_step_queue.empty():
|
while not self.__event_step_queue.empty():
|
||||||
(amount, stat) = self.__event_step_queue.get()
|
(amount, stat) = self.__event_step_queue.get()
|
||||||
|
|
||||||
|
|
@ -163,7 +201,7 @@ if __name__ == '__main__':
|
||||||
def worker(pbar, t):
|
def worker(pbar, t):
|
||||||
print('Starting worker')
|
print('Starting worker')
|
||||||
while True:
|
while True:
|
||||||
for i in pbar.range(10):
|
for i in pbar.range(1000):
|
||||||
print(i)
|
print(i)
|
||||||
time.sleep(t)
|
time.sleep(t)
|
||||||
|
|
||||||
|
|
@ -174,6 +212,7 @@ if __name__ == '__main__':
|
||||||
|
|
||||||
|
|
||||||
root = tk.Tk()
|
root = tk.Tk()
|
||||||
|
root.minsize(width=800, height=600)
|
||||||
bar1 = AsyncProgress(root, label="Progressbar 1", update_interval=20, range_update_interval=10)
|
bar1 = AsyncProgress(root, label="Progressbar 1", update_interval=20, range_update_interval=10)
|
||||||
bar2 = AsyncProgress(root, label="Progressbar 2", update_interval=20, range_update_interval=10)
|
bar2 = AsyncProgress(root, label="Progressbar 2", update_interval=20, range_update_interval=10)
|
||||||
bar3 = AsyncProgress(root, label=None, update_interval=20, range_update_interval=10)
|
bar3 = AsyncProgress(root, label=None, update_interval=20, range_update_interval=10)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue