added stop and updated __repr__ of Stats

This commit is contained in:
Patrick 2024-07-06 18:18:38 +02:00
parent 4dc1ab37d0
commit 9302f359bf
1 changed files with 54 additions and 15 deletions

View File

@ -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)