Source code for niceprint

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from typing import Callable, Iterable, Union
import sys
from threading import Timer
from time import sleep
from enum import Enum
from json import dumps

k = 0
val = 0

_COLORIZE_FORMAT = "\033[{:d};{:d};{:d}m{!s}\033[0m"

class _FgColor(Enum):
    Black   = 30
    Red     = 31
    Green   = 32
    Yellow  = 33
    Blue    = 34
    Magenta = 35
    Cyan    = 36
    White   = 37
    Null    = 10

class _BgColor(Enum):
    Black   = 40
    Red     = 41
    Green   = 42
    Yellow  = 43
    Blue    = 44
    Magenta = 45
    Cyan    = 46
    White   = 47
    Null    = 10


def _to_int(member):
    try:
        return member.value
    except:
        return member
        
    
def _colorize(bg, base, fg, *text):
    """ _colorize(bg, base, fg, *text)
    """
    # All argument types must be str. 
    rtext = [str(f) for f in text]
    
    return _COLORIZE_FORMAT.format(
        _to_int(bg), _to_int(base), _to_int(fg), ''.join(rtext)
    )

[docs]def black(*text): return _colorize(_BgColor.Null, 10, _FgColor.Black, *text)
[docs]def red(*text): return _colorize(_BgColor.Null, 10, _FgColor.Red, *text)
[docs]def green(*text): return _colorize(_BgColor.Null, 10, _FgColor.Green, *text)
[docs]def yellow(*text): return _colorize(_BgColor.Null, 10, _FgColor.Yellow, *text)
[docs]def blue(*text): return _colorize(_BgColor.Null, 10, _FgColor.Blue, *text)
[docs]def magenta(*text): return _colorize(_BgColor.Null, 10, _FgColor.Magenta, *text)
[docs]def cyan(*text): return _colorize(_BgColor.Null, 10, _FgColor.Cyan, *text)
[docs]def bg_black(*text): return _colorize(_BgColor.Black, 10, _FgColor.Null, *text)
[docs]def bg_red(*text): return _colorize(_BgColor.Red, 10, _FgColor.Null, *text)
[docs]def bg_green(*text): return _colorize(_BgColor.Green, 10, _FgColor.Null, *text)
[docs]def bg_yellow(*text): return _colorize(_BgColor.Yellow, 10, _FgColor.Null, *text)
[docs]def bg_blue(*text): return _colorize(_BgColor.Blue, 10, _FgColor.Null, *text)
[docs]def bg_magenta(*text): return _colorize(_BgColor.Magenta, 10, _FgColor.Null, *text)
[docs]def bg_cyan(*text): return _colorize(_BgColor.Cyan, 10, _FgColor.Null, *text)
[docs]def create_full_list(amount, lst, cmp = None): if cmp is None: out = [] length = amount // len(lst) remaining = amount % len(lst)+1 for item in lst: for _ in range(length): out.append(item) for _ in range(remaining): out = [lst[0]] + out out.reverse() return out
[docs]class SetInterval: """ Sets an interval for functions. Creates an instance of SetInterval. :param interval: time between intervals :type interval: int or float :param Callable function: the function to call :param int end: number of times to run :param Callable callback: function to call after code run :param bool show_progress: If to show a progress bar to indicate running #buggy :param bool req: if to add self to the called function. usefull if function should cancel the interval :param Iterable args: List of positional arguments :param dict kwargs: List of named arguments :rtype: None :warning: Asynchronous Thread. Does not wait. """ def __init__(self, interval:Union[int, float], function, end:int=1000, callback:Callable=None, \ show_progress:bool=False, req:bool=False, args:list=None, kwargs:dict=None): global k, val self.k = k self.callback = callback k = self.k self.interval = interval self.function = function self.end = int(end) self.show_progress = show_progress if show_progress: self.progress = ProgressBar(len=end, color="green") if self.end > 50: self.progress.set_len(self.end//50) self.req = req self.args = args self.val = val self.kwargs = kwargs
[docs] def begin(self, *args): "Starts the interval " self._interval()
def _interval(self): global val self.k2 = Timer(self.interval, self._interval) self.k = self.k+1 if self.show_progress: if self.end > 50: if self.end // 50 == 0: self.progress.pulse() else: self.progress.pulse() kwargs = self.kwargs args = self.args if self.req and self.args is None and self.kwargs is None: val = self.function(self) elif self.args is not None and self.req: val = self.function(self, *self.args) elif self.args is not None and self.kwargs and self.req: val = self.function(self, *self.args, **kwargs) elif self.kwargs is not None and self.req: val = self.function(self, **kwargs) elif self.kwargs is not None: val = self.function(**kwargs) elif self.args is not None: val = self.function(*args) else: val = self.function() self.val = val if self.k == self.end: self.cancel() return self.k2.start()
[docs] def cancel(self): "Cancel running Interval" self.k = self.end self.k2.cancel() if self.callback is not None: self.callback() val = self.val del self return val
[docs]class InvalidColor(Exception): """Shows that the color is not accepted""" def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.msg = "No Such Color" pass
[docs]def dummy(text, *args): return text
[docs]def bg_dummy(text, *args): return dummy(text, *args)
[docs]class Print: """ Print Letters individualy according to given time >>> from niceprint import Print >>> Print(\"Hello\",color=\"red\",time=0.1) Throws InvalidColor exception is the color is not in color list To get the accepted colors >>> from niceprint import Print >>> Print.get_colors() Prints any amount of text with the given color and background. :param Iterable text: Items to print out. Can be anything iterable :param str fg: Foreground color :param str bg: Background color :param bool lock: If lock is true, there will be no access to the console till the printing is over. :param str end: Character to end with :param bool format: If format is true, any item in @param 'text' that is a dict, will be formated with json.dumps with indent of 2 :raises InvalidColor: If color is not in the color list """ colors = ['red', 'green', 'blue', 'magenta', 'black', 'yellow', "cyan"]
[docs] @staticmethod def get_colors() -> list: "Returns the list of colors availiable" return Print.colors[:]
@staticmethod def _process_color(c): if c is None or c == "": return "dummy" if c in Print.colors: return Print.colors[Print.colors.index(c)] Print.colors[4] = "kblack" if c in [cl[0] for cl in Print.colors]: oc = [cl[0] for cl in Print.colors] Print.colors[4] = "black" return Print.colors[oc.index(c)] return "dummy" def _get_text(self): if self.color is not None: print((eval(f"{self.color}")((self.text[self.i]))), end="", flush=True) elif self.bg_color is not None: print(eval(f"bg_{self.bg_color}")((self.text[self.i])), end="", flush=True) elif self.color is not None: print(eval(f"bg_{self.bg_color}")(eval(f"{self.color}")((self.text[self.i]))), end="", flush=True) else: print(self.text[self.i], end="", flush=True) self.i += 1 if self.i == len(self.text): print() def _print(self, *args): self.inter = SetInterval(self.time, self._get_text, len(self.text)) self.inter.begin() def _locking_print(self): for i, t in enumerate(self.text, 0): if self.color is not None: print(eval(f"bg_{self.bg_color}")(eval(f"{self.color}")( (t))), end="", flush=True) else: print(t, end="", flush=True) try: sleep(self.time) except KeyboardInterrupt: raise TimeoutError def __init__(self, *text, fg=None, bg=None, time=0.03, lock=True, end="\n", format=False): for ind, content in enumerate(text, 0): self.i = 0 if type(content) is dict or type(content) is list and format: content = dumps(content, indent=2) else: self.text = [x for x in str(content)] self.inter = None self.color = Print._process_color(fg) self.bg_color = Print._process_color(bg) if self.color is None or self.bg_color is None: raise InvalidColor() self.time = time if lock: self._locking_print() else: self._print() print(end=end) #print() return
[docs]class MultiColoredPrint: """ Prints words or text individually according to the given time and color >>> from niceprint import MultiColoredPrint as MP >>> text = \"Multiple Text\" >>> MP(text, color=[\'c\', 'g']) Prints multiple text with multiple colors, different backgrounds and a special delimiter after each color. :param Iterable args: Items to print :param str color: Foreground color :param str bg: Background color :param str delimiter: Character to print out after every item :param time: Time for each item :type time: int or float :param bool lock: If lock is true, there will be no access to the console till the printing is over. :raises InvalidColor: If color is invalid """
[docs] @staticmethod def get_colors(): """Returns all the accepted colors.""" return Print.colors
def __init__(self, *args, color: Iterable = "", bg: Iterable = "", delimiter=" ", time = 0, lock: bool = True): args = [str(x) for x in args] color = list(color) color.reverse() bg = list(bg) bg.reverse() if len(bg) == 0: bg = [""] if len(color) == 0: color = [""] if len(args) == len(color) and len(args) == len(bg): for ind, text in enumerate(args, 0): Print(text, fg=color[ind], bg=bg[ind], end=delimiter, lock=lock, time=time) print() elif len(args) == len(color) and len(bg) == 0: for ind, text in enumerate(args, 0): Print(text, fg=color[ind], end=delimiter, lock=lock, time=time) print() elif len(args) == len(bg) and len(color) == 0: for ind, text in enumerate(args, 0): Print(text, bg=color[ind], end=delimiter, lock=lock, time=time) print() else: la = len(args) lc = len(color) ll = lc//la for ind, text in enumerate(args): tt = [str(x)+" " for x in text.split(" ")] if len(tt) < 2: tt = list(text) tt_ = [] n = int(round(len(tt)/ll, 0)) s = 0 constn = n for _ in range(ll): tt_.append(" ".join(tt[s:n])) s = n n += constn if len(tt_) < len(tt): tt_ = tt color = create_full_list(len(tt_), color) bg = create_full_list(len(tt_), bg) for ind_, wt in enumerate(tt_): Print(wt, fg=color[ind_], bg=bg[ind_], end=delimiter, lock=lock, time=time) print()
[docs]class ProgressBar: """Creates a progress bar with the given length, color, background and character. >>> from niceprint import ProgressBar as pb >>> progress = pb(color='c', bg='k') >>> progress.fill(ms=10) [# ] :param int len: Length of progress bar :param str color: Foreground color :param str bg: Background Color :param str char: Character in bar :raises InvalidColor: If color is invalid """ def _process_len(self, *args): self.diff = 1 if self.len > 20: self.diff = self.len//20 self.len = 20 def __init__(self, len=10, color="", bg="", char="#", **kwargs): self.len = len self._old_len = len self._process_len() len = self.len self.color = Print._process_color(color) self.bg_color = Print._process_color(bg) self.tick = 0 self.charcter = char if char==" " or char == "": color = "" if bg == "": self.bg_color = Print._process_color("b") bar = f"[" + " "*len + "]" self.right_bar = "[" self.left_bar = "]" try: if self.color is not None and self.bg_color is not None: char = eval(f"bg_{self.bg_color}")(eval(f"{self.color}")(char)) bar = eval(f"bg_{self.bg_color}")(eval(f"{self.color}")(bar)) self.right_bar = eval(f"bg_{self.bg_color}")(eval(f"{self.color}")(self.right_bar)) self.left_bar = eval(f"bg_{self.bg_color}")(eval(f"{self.color}")(self.left_bar)) elif self.bg_color is not None: char = eval(f"bg_{self.bg_color}")(char) bar = eval(f"bg_{self.bg_color}")(bar) self.left_bar = eval(f"bg_{self.bg_color}")(self.left_bar) self.right_bar = eval(f"bg_{self.bg_color}")(self.right_bar) elif self.color is not None: char = eval(f"{self.color}")(char) bar = eval(f"{self.color}")(bar) except Exception as e: color = "" print(e) self.char = char self.pg = 0 self.chw = 0 self.clean_up() sys.stdout.write(u"\u001b[1000D"+bar) sys.stdout.flush()
[docs] def set_len(self, len): """Sets the length of the progress bar""" self.len = len self.pulse()
[docs] def clean_up(self, *args): """Cleans the line it's on""" sys.stdout.write(u"\u001b[10000000000D") sys.stdout.flush()
[docs] def empty(self, *args): """Empties the progress bar""" self.pg = 0 self.chw = 0
[docs] def fill(self, ms=10, sec=None, text = ""): """Fill up the progress bar according to time. >>> from niceprint import ProgressBar as pb >>> progress = pb(2, 'c', 'r') >>> progress.fill(text = ['One text']) [# ] One text [##] One text >>> progress.empty() >>> progress.fill(text = ['Text 1', 'Text 2']) [# ] Text 1 [##] Text 2 :param int ms: Time between each pulse in milliseconds :param sec:Time between each pulse in seconds :type sec: int or float :param text: Items to print at the end of the bar :type text: str or list or tuple """ char=self.char self.len = self.len-self.pg if sec is not None: t = sec elif ms is not None: t = ms/100 for _ in range(self.len+1): sleep(t) bar = self.right_bar + (char*(self.pg+_)) +" "*(self.len-_) + self.left_bar +" "+ str(text) self.tick += 1 sys.stdout.write(u"\u001b[1000D"+bar) sys.stdout.flush() print()
[docs] def set_color(self, color) -> bool: """Sets the backgroun color of the char in the bar returns True if color has been set else False""" if color != "": try: color = Print._process_color(color) self.char = eval(f"{color}")(self.charcter) self.color = color except Exception as e: color = "" print(e) return False return True
[docs] def set_bgcolor(self, color) -> bool: """Sets the backgroun color of the char in the bar returns True if color has been set else False""" if color != "": try: color = Print._process_color(color) self.char = eval(f"bg_{color}")(self.charcter) self.bg_color = color except Exception as e: color = "" print(e) return False return True
[docs] def set_char(self, char, *args) -> bool: """Sets the character of the progress bar returns True if char has been set else False""" try: self.char = eval(f"{self.color}")(char) self.charcter = char return True except Exception as e: print(e) return False
[docs] def pulse(self, step=1, ms=1, sec=None, text: str = ""): """Increase the progress bar by amount of step and with a text :param int ms: Time between each pulse in milliseconds :param sec:Time between each pulse in seconds :type sec: int or float :param str text: Items to print at the end of the bar """ if step > 1: self.len = self._old_len*2 self._process_len() if self.len <= self.pg: return try: t = ms/100 if sec is None else sec sleep(t) except Exception as e: Print("[ERROR] : ", e, color="r") char = self.char self.tick += 1 self.chw = step if self.tick % self.diff == 0: self.pg += step bar = self.right_bar + (char*(self.pg)) +" "*(self.len-self.pg) + self.left_bar +" "+ str(text) sys.stdout.write(u"\u001b[1000D"+bar) sys.stdout.flush() if self.pg == self.len: print()
[docs]class Spinner: """Creates a spinner >>> from niceprint import Spinner >>> sp = Spinner() >>> sp.rotate() :param str fg: Foreground color :param str bg: Background color :raises InvalidColor: If color is not accepted """ def __del__(self, *args): print() def __init__(self, fg="c", bg = "", *args): self.color = Print._process_color(fg) self.bg = Print._process_color(bg) self._chars = [" | ", " / ", " -- ", " \\ "] self._index = 0 sys.stdout.flush() def _get_char(self, *args): if self._index > len(self._chars)-1: self._index = 0 ch = eval(f"bg_{self.bg}")(eval(f"{self.color}")(self._chars[self._index])) self._index += 1 return ch
[docs] def spin(self, text="", *args): """Turns the spinner 1 step""" sys.stdout.write(u"\u001b[10000D"+" "*100) sys.stdout.write(u"\u001b[10000D" + self._get_char() +" "+ str(text)) sys.stdout.flush()
[docs] def rotate(self, times: int = 5, time: int = 0.1, text: Union[str, list] = ""): """Rotates the spiner N(times) with a time interval of T(time) :param int times: Times to rotate :param time: Time between each turn in seconds :type time: int or float :param text: Items to print at the end of the bar :type text: str or list or tuple """ for _ in range(times): if type(text) is list: text = create_full_list(self._chars, text) for x in range(len(self._chars)): sys.stdout.write(u"\u001b[10000D"+" "*100) sys.stdout.write(u"\u001b[10000D" + eval(f"bg_{self.bg}")(eval(f"{self.color}")(self._chars[x])) + text[x]) sys.stdout.flush() sleep(time) if type(text) is str: for x in range(len(self._chars)): sys.stdout.write(u"\u001b[10000D"+" "*100) sys.stdout.write(u"\u001b[10000D" + eval(f"bg_{self.bg}")(eval(f"{self.color}")(self._chars[x])) + text) sys.stdout.flush() sleep(time) sys.stdout.write(u"\u001b[1000D") sys.stdout.flush()
[docs]class Percentage: """Creates a percentage class >>> from niceprint import Percentage >>> pg = Percentage(10) >>> pg.tick() 0% >>> pg.fill(text = ['Starting...', 'Almost Done', 'Just a sec..']) :param int max: Maximum number :param str fg: Foreground color :param str bg: Background color :raises InvalidColor: If color is not accepted """ def __init__(self,max = 100, fg="", bg=""): self.fg = Print._process_color(fg) self.bg = Print._process_color(bg) self.max = max self.start = 0 def _get_point(self, *args): return eval(f"bg_{self.bg}")(eval(f"{self.fg}")(str(self.start)+"%")) + " "
[docs] def tick(self, text = ""): """Increases the percentage value by 1 with an optional text at the end""" sys.stdout.write(u"\u001b[10000D"+" "*100) sys.stdout.write(u"\u001b[10000D"+self._get_point()+str(text)) sys.stdout.flush() self.start += 1
[docs] def fill(self, time: Union[int, float] = 0, text: Union[str, list] = ""): """Increases the percentage value till the end with multiple or single text :param time: Time between each increment in seconds :type time: int or float :param text: Items to print at the end of the bar :type text: str or list or Tuple :rtype: None """ if type(text) is list: text = create_full_list(self.max - self.start, text) while self.start <= self.max: #print(len(text), self.max - self.start, [self.max - self.start]) sys.stdout.write(u"\u001b[10000D"+" "*100) sys.stdout.write(u"\u001b[10000D"+self._get_point()+str(text[self.max - self.start])) sys.stdout.flush() self.start += 1 sleep(time) elif type(text) is str: while self.start <= self.max: sys.stdout.write(u"\u001b[10000D"+" "*100) sys.stdout.write(u"\u001b[10000D"+self._get_point()+text) sys.stdout.flush() self.start += 1 sleep(time)
def __del__(self, *args): print()
[docs]def get_availiable_colors(): "Returns availiable colors" return Print.colors
[docs]def color_is_valid(color): """Checks if color is valid and returns color :param str color: Color to check :rtype: str or False """ res = Print._process_color(color) if res == "dummy" or res == "" or res is None: return False else: return res
if __name__ == "__main__": MultiColoredPrint("Multi Colored Print", color='cmb', delimiter="")