From e03eb97ea9088a8d90e61820ad3572c2e4245ffa Mon Sep 17 00:00:00 2001 From: Joab Date: Thu, 27 Feb 2020 19:27:30 -0300 Subject: [PATCH 1/2] Progress: Refactored Progress --- cereja/display.py | 146 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 128 insertions(+), 18 deletions(-) diff --git a/cereja/display.py b/cereja/display.py index 1dbba79..b55fd5b 100644 --- a/cereja/display.py +++ b/cereja/display.py @@ -2,10 +2,25 @@ import sys import time +from cereja.cj_types import Number + __all__ = 'Progress' class Progress(object): + """ + Percentage calculation is based on max_value (default is 100) and current_value: + percent = (current_value / max_value) * 100 + + :param style: defines the type of progress that will be used. "loading" is Default + + :param task_name: Defines the name to be displayed in progress + + :param max_value: This number represents the maximum amount you want to achieve. + It is not a percentage, although it is purposely set to 100 by default. + :param kwargs: + loading_with_clock: You will see a clock if the style is loading + """ NON_BMP_MAP = dict.fromkeys(range(0x10000, sys.maxunicode + 1), 0xfffd) DONE_UNICODE = "\U00002705" CLOCKS_UNICODE = ("\U0001F55C", "\U0001F55D", "\U0001F55E", "\U0001F55F", "\U0001F560", "\U0001F561", "\U0001F562", @@ -13,30 +28,35 @@ class Progress(object): CHERRY_UNICODE = "\U0001F352" - def __init__(self, job_name="Progress Tool", style="loading", **kwargs): + def __init__(self, task_name="Progress Tool", style="loading", max_value=100, **kwargs): self._style = style self._default_loading_symb = "." self._default_bar_symb = "=" self._current_percent = 0 self._finish = False + self._started = False self._last_percent = None self._n_times = 0 self._max_loading_symb = 3 - self._max_percent_value = kwargs.get("max_percent", 100) self._bar_size = kwargs.get('bar_size', 30) self._bar = ' ' * self._bar_size - self._use_loading_with_clock = False + self._use_loading_with_clock = bool(kwargs.get("loading_with_clock", False)) self._sleep_time = 0.5 - self.job_name = job_name + self.task_name = task_name self.first_time = time.time() + self.set_max_value(max_value) + # This is important, as there may be an exception if the terminal does not support unicode bmp try: - sys.stdout.write(f"{self.CHERRY_UNICODE} {self.job_name}: Created!") + sys.stdout.write(f"{self.CHERRY_UNICODE} {self.task_name}: Created!") self.non_bmp_supported = True except UnicodeEncodeError: self.non_bmp_supported = False def __parse(self, msg: str): + """ + This is important, as there may be an exception if the terminal does not support unicode bmp + """ if self.non_bmp_supported: return msg return msg.translate(self.NON_BMP_MAP) @@ -70,7 +90,7 @@ def _loading_progress(self): def _bar_progress(self): if self._current_percent != self._last_percent: self._last_percent = self._current_percent - current_bar_value = int((self._bar_size / self._max_percent_value) * self._current_percent) + current_bar_value = int((self._bar_size / 100) * self._current_percent) return self._bar.replace(' ', self._default_bar_symb, current_bar_value).replace(' ', '>', 1) def _display(self): @@ -88,38 +108,128 @@ def _current_value_info(self): def _write(self): if self._current_percent == 0: self.__send_msg( - f"{self.CHERRY_UNICODE} {self.job_name}: Awaiting {self._loading_progress()} {self._current_value_info()}") + f"{self.CHERRY_UNICODE} {self.task_name}: Awaiting {self._loading_progress()} {self._current_value_info()}") else: - self.__send_msg(f"{self.CHERRY_UNICODE} {self.job_name}: [{self._display()}] {self._current_value_info()}") + self.__send_msg(f"{self.CHERRY_UNICODE} {self.task_name}: [{self._display()}] {self._current_value_info()}") self._last_percent = self._current_percent def _looping(self): + """ + Used by the thread. Checks and shows current progress + """ while not self._finish: self._write() time.sleep(self._sleep_time) self._write() - def set_percent(self, percent: float): - self._current_percent = percent - - def display_only_once(self, percent: float = None): - if percent is not None: - self.set_percent(percent) + def start(self, task_name: str = None): + """ + Start progress task, you will need to use this method if it does not run with the "with statement" + :param task_name: Defines the name to be displayed in progress + """ + if task_name is not None: + self.task_name = task_name + if not self._started: + self._current_percent = 0 + self.__enter__() + + def stop(self): + """ + Stop the current task. + This is necessary because the task is running on a separate thread. + + If you are not using with statement it is extremely important to call this method at the end, + otherwise the thread will never die. Unless it ends the runtime. + """ + self._finish = True + self._started = False + self.th.join() + self.__send_msg('\n') + + def _reload(self): + """ + Intermediate that calls stop and start in sequence + """ + self.stop() + self.start() + + def set_max_value(self, value: Number): + """ + :param value: This number represents the maximum amount you want to achieve. + It is not a percentage, although it is purposely set to 100 by default. + :param value: + :return: + """ + if value is not None: + if isinstance(value, (int, float, complex)) and value > 0: + self.max_value = value + else: + raise Exception("Send Number.") + + def update(self, current_value: float, max_value=None): + """ + + :param current_value: Fraction of the "max_value" you want to achieve. + Remember that this value is not necessarily the percentage. + It is totally dependent on the "max_value" + :param max_value: This number represents the maximum amount you want to achieve. + It is not a percentage, although it is purposely set to 100 by default. + """ + self.set_max_value(max_value) + current_value = (current_value / self.max_value) * 100 + + # TODO: improve the identification of another task + if current_value < self._current_percent: + self._reload() + + self._current_percent = current_value + + def display_only_once(self, current_value: float = None): + """ + In case you need to show the bar only once. This method will cause the code to be executed on the main thread + :param current_value: + :return: + """ + if current_value is not None: + self.update(current_value) self._write() def __enter__(self): - self.th = threading.Thread(target=self._looping) + """ + intern method used by "with st + :return: + """ + self._finish = False + self._started = True + self.th = threading.Thread(name=self.task_name, target=self._looping) self.th.start() return self def __exit__(self, *args, **kwargs): self._finish = True + self._started = False self.th.join() + self.__send_msg('\n') if __name__ == '__main__': - with Progress(job_name="Progress Bar Test", style='bar') as bar: - for i in range(1, 100): + with Progress(task_name="Progress Bar Test", max_value=500) as bar: + for i in range(1, 500): + time.sleep(1 / i) + bar.update(i) + + for i in range(1, 400): time.sleep(1 / i) - bar.set_percent(i) \ No newline at end of file + bar.update(i, max_value=400) + + bar = Progress(task_name="Progress Bar Test", style='bar', max_value=500) + bar.start() + for i in range(1, 500): + time.sleep(1 / i) + bar.update(i) + + for i in range(1, 400): + time.sleep(1 / i) + bar.update(i, max_value=400) + bar.stop() From f38fb73631a14b008a446aeba0cea137d229d454 Mon Sep 17 00:00:00 2001 From: Joab Date: Thu, 27 Feb 2020 19:27:46 -0300 Subject: [PATCH 2/2] Progress: Refactored Progress --- cereja/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cereja/__init__.py b/cereja/__init__.py index 7086de1..329f5fc 100644 --- a/cereja/__init__.py +++ b/cereja/__init__.py @@ -2,7 +2,7 @@ from . import utils from importlib import import_module -VERSION = "1.0.5.final.0" +VERSION = "1.0.6.final.0" __version__ = get_version_pep440_compliant(VERSION)