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
|
||||
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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue