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
from contextlib import contextmanager
from dataclasses import dataclass
from typing import Optional
from typing import Optional, Callable
class ClearableQueue(queue.Queue):
@ -25,29 +25,41 @@ def acquire_timeout(lock, timeout):
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):
@dataclass(repr=False)
@dataclass(init=False, repr=False)
class Stats:
start_time: float
total: int
current: int = 0
start_time: float = time.time()
last_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):
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} "
f"| {ips:.2f}it/s "
f"| elapsed: {time.time() - self.start_time:.2f} "
f"| remaining: {(self.total - self.current) * self.last_elapsed_time:.2f} ")
f"| {self.last_elapsed_time}s/it" if ips < 1 else f"{ips:.2f}it/s "
f"| elapsed: {format_time(time.time() - self.start_time)} "
f"| remaining: {format_time((self.total - self.current) * self.last_elapsed_time):} ")
class Range:
def __init__(self, parent, iterable, update_interval):
def __init__(self, parent, iterable, iter_len, update_interval, stop_event):
self.iterable = iterable
self.iter_len = iter_len
self.parent = parent
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):
last_time = time.time()
@ -57,6 +69,8 @@ class AsyncProgress(ttk.Frame):
try:
for obj in self.iterable:
if self.stop_event.is_set():
return
yield obj
stats.current += 1
@ -67,17 +81,17 @@ class AsyncProgress(ttk.Frame):
self.parent.step(1, stats)
last_update_time = time.time()
finally:
stats.total_elapsed_time = time.time() - stats.start_time
self.parent._finish(stats)
finally:
self.close()
def close(self):
self.iterable = range(0)
def __init__(self, parent, *,
width=450,
width=800,
height=30,
update_interval=20,
range_update_interval=10,
@ -87,6 +101,10 @@ class AsyncProgress(ttk.Frame):
self.__event_step_queue = ClearableQueue()
self.__lock = threading.Lock()
self.__cancel_event = threading.Event()
self.__stop_condition = None
self.__tk_pbar_value = tk.IntVar()
self.__tk_stats_str = tk.StringVar()
self.__tk_stats_str.set("Not running")
@ -117,16 +135,33 @@ class AsyncProgress(ttk.Frame):
self.__tk_pbar_value.set(0)
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):
_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:
if self.running:
raise RuntimeError('Progressbar is already running')
if stop is None:
stop = start_stop
start_stop = 0
return AsyncProgress.Range(self, range(start_stop, stop, step), self.range_update_interval)
return AsyncProgress.Range(self, iterable, length, self.range_update_interval, self.__cancel_event)
def _start(self):
self.__cancel_event.clear()
with self.__lock:
self.running = True
@ -141,6 +176,9 @@ class AsyncProgress(ttk.Frame):
def __update_self(self):
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():
(amount, stat) = self.__event_step_queue.get()
@ -163,7 +201,7 @@ if __name__ == '__main__':
def worker(pbar, t):
print('Starting worker')
while True:
for i in pbar.range(10):
for i in pbar.range(1000):
print(i)
time.sleep(t)
@ -174,6 +212,7 @@ if __name__ == '__main__':
root = tk.Tk()
root.minsize(width=800, height=600)
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)
bar3 = AsyncProgress(root, label=None, update_interval=20, range_update_interval=10)