From d5e63f56c16aac89a54892e172698afa51f6c670 Mon Sep 17 00:00:00 2001 From: aristocratos Date: Fri, 10 Jul 2020 02:50:24 +0200 Subject: [PATCH] Added Graph class with functions for graph creation --- bpytop.py | 182 ++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 137 insertions(+), 45 deletions(-) diff --git a/bpytop.py b/bpytop.py index d71d012..9052df8 100755 --- a/bpytop.py +++ b/bpytop.py @@ -760,23 +760,21 @@ class Theme: setattr(self, item, Color(tdict[item], depth=depth, default=default)) else: setattr(self, item, Color(value, depth=depth, default=default)) - #* Create color gradients from one, two or three colors, 101 values + #* Create color gradients from one, two or three colors, 101 values indexed 0-100 rgb: Dict[str, Tuple[int, int, int]] - colors: List[Tuple[int, ...]] - rgb_calc: List[int] + colors: List[List[int]] = [] for name in self.gradient.keys(): rgb = { "start" : getattr(self, f'{name}_start').dec, "mid" : getattr(self, f'{name}_mid').dec, "end" : getattr(self, f'{name}_end').dec } - colors = [ getattr(self, f'{name}_start') ] + colors = [ list(getattr(self, f'{name}_start')) ] if rgb["end"][0] >= 0: r = 50 if rgb["mid"][0] >= 0 else 100 for first, second in ["start", "mid" if r == 50 else "end"], ["mid", "end"]: for i in range(r): - rgb_calc = [rgb[first][n] + i * (rgb[second][n] - rgb[first][n]) // r for n in range(3)] - colors += [tuple(rgb_calc)] + colors += [[rgb[first][n] + i * (rgb[second][n] - rgb[first][n]) // r for n in range(3)]] if r == 100: break - for color in colors: - self.gradient[name] += [Color.fg(*color)] + self.gradient[name] += [ Color.fg(*color) for color in colors ] + else: c = Color.fg(*rgb["start"]) for _ in range(100): @@ -875,36 +873,93 @@ class Symbol: fail: str = f'{Color.fg("#ff3050")}!{Color.fg("#cc")}' class Graph: - out: str = "" - width: int = 0 - height: int = 0 - graphs: Dict[bool, List[str]] = {False : [], True : []} + out: str + width: int + height: int + graphs: Dict[bool, List[str]] colors: List[str] - invert: bool = False - max_value: int = 0 - current: bool = False - last_value: int = 0 + invert: bool + max_value: int + current: bool + last: int symbol: Dict[float, str] - def __init__(self, width: int, height: int, color_gradient: List[str], data: List[int], invert: bool = False, max_value: int = 0): - for i in range(1, height + 1): - self.colors.append(color_gradient[100 // height * i]) #* Calculate colors of graph - if invert: self.colors.reverse() + def __init__(self, width: int, height: int, color: Union[List[str], Color, None], data: List[int], invert: bool = False, max_value: int = 0): + self.graphs: Dict[bool, List[str]] = {False : [], True : []} + self.current: bool = True + self.colors: List[str] = [] + if isinstance(color, list): + for i in range(1, height + 1): self.colors.insert(0, color[i * 100 // height]) #* Calculate colors of graph + if invert: self.colors.reverse() + elif isinstance(color, Color): + self.colors = [ f'{color}' for _ in range(height) ] + self.width = width + self.height = height + self.invert = invert + if not data: data = [0] if max_value: self.max_value = max_value data = [ v * 100 // max_value if v < max_value else 100 for v in data ] #* Convert values to percentage values of max_value with max_value as ceiling + else: + self.max_value = 0 self.symbol = Symbol.graph_down if invert else Symbol.graph_up + value_width: int = ceil(len(data) / 2) + filler: str = "" + if value_width > width: #* If the size of given data set is bigger then width of graph, shrink data set + data = data[-(width*2):] + value_width = ceil(len(data) / 2) + elif value_width < width: #* If the size of given data set is smaller then width of graph, fill graph with whitespace + filler = " " * (width - value_width) + for _ in range(height): + for b in [True, False]: + self.graphs[b].append(filler if filler else "") + self._create(data, new=True) + def _create(self, data: List[int], new: bool = False): + h_high: int + h_low: int + value: Dict[str, int] = { "left" : 0, "right" : 0 } + val: int + side: str + + #* Create the graph + for h in range(self.height): + h_high = round(100 * (self.height - h) / self.height) if self.height > 1 else 100 + h_low = round(100 * (self.height - (h + 1)) / self.height) if self.height > 1 else 0 + for v in range(len(data)): + if new: self.current = bool(v % 2) #* Switch between True and False graphs + if new and v == 0: self.last = 0 + for val, side in [self.last, "left"], [data[v], "right"]: # type: ignore + if val >= h_high: + value[side] = 4 + elif val <= h_low: + value[side] = 0 + else: + if self.height == 1: value[side] = round(val * 4 / 100 + 0.5) + else: value[side] = round((val - h_low) * 4 / (h_high - h_low) + 0.1) + if new: self.last = data[v] + self.graphs[self.current][h] += self.symbol[float(value["left"] + value["right"] / 10)] + self.last = data[-1] + self.out = "" + + for h in range(self.height): + if h > 0: self.out += f'{Mv.d(1)}{Mv.l(self.width)}' + self.out += f'{"" if not self.colors else self.colors[h]}{self.graphs[self.current][h if not self.invert else (self.height - 1) - h]}' + self.out += f'{Term.fg}' def add(self, value: int): self.current = not self.current - del self.graphs[self.current][0] + for n in range(self.height): + self.graphs[self.current][n] = self.graphs[self.current][n][1:] if self.max_value: value = value * 100 // self.max_value if value < self.max_value else 100 + self._create([value]) + return self.out def __str__(self): return self.out - + def __repr__(self): + return repr(self.out) class Graphs: @@ -923,18 +978,18 @@ class Meter: __call__(value) to set value and return meter as a string __str__ returns last set meter as a string ''' - out: str = "" + out: str color_gradient: List[str] color_inactive: Color - width: int = 0 - saved: Dict[int, str] = {} + width: int + saved: Dict[int, str] def __init__(self, value: int, width: int, gradient_name: str): self.color_gradient = THEME.gradient[gradient_name] self.color_inactive = THEME.inactive_fg self.width = width self.out = self._create(value) - self.saved[value] = self.out + self.saved = { value : self.out } def __call__(self, value: int): if value in self.saved.keys(): @@ -977,18 +1032,18 @@ class Meters: class Box: '''Box class with all needed attributes for create_box() function''' - name: str = "" - height_p: int = 0 - width_p: int = 0 - x: int = 1 - y: int = 1 - width: int = 0 - height: int = 0 - out: str = "" - bg: str = "" - _b_cpu_h: int = 0 - _b_mem_h: int = 0 - redraw_all: bool = False + name: str + height_p: int + width_p: int + x: int + y: int + width: int + height: int + out: str + bg: str + _b_cpu_h: int + _b_mem_h: int + redraw_all: bool @classmethod def calc_sizes(cls): @@ -1010,6 +1065,8 @@ class SubBox: class CpuBox(Box, SubBox): name = "cpu" + x = 0 + y = 0 height_p = 32 width_p = 100 @@ -1045,6 +1102,8 @@ class MemBox(Box): name = "mem" height_p = 40 width_p = 45 + x = 0 + y = 0 divider: int = 0 mem_width: int = 0 disks_width: int = 0 @@ -1072,6 +1131,8 @@ class NetBox(Box, SubBox): name = "net" height_p = 28 width_p = 45 + x = 0 + y = 0 @classmethod def _calc_size(cls): @@ -1093,6 +1154,8 @@ class ProcBox(Box): name = "proc" height_p = 68 width_p = 55 + x = 0 + y = 0 detailed: bool = False detailed_x: int = 0 detailed_y: int = 0 @@ -1502,6 +1565,34 @@ def testing_keyinput(): pass Draw.clear() +def testing_graphs(): + + from random import randint + + my_data = [x for x in range(0, 101)] + my_data += [x for x in range(100, -1, -1)] + + my_graph = Graph(Term.width, Term.height // 3, THEME.gradient['cpu'], my_data, invert=False) + my_graph2 = Graph(Term.width, Term.height // 3, THEME.gradient['cpu'], my_data, invert=True) + my_graph3 = Graph(Term.width, 1, THEME.proc_misc, my_data) + my_graph4 = Graph(Term.width, 2, None, my_data) + + Draw.now(f'{Mv.to(0, 0)}{my_graph}') + Draw.now(f'{Mv.to(Term.height // 3 + 1, 0)}{my_graph2}') + Draw.now(f'{Mv.to(Term.height - (Term.height // 3) + 2, 0)}{my_graph3}') + Draw.now(f'{Mv.to(Term.height - (Term.height // 3) + 4, 0)}{my_graph4}') + + + for _ in range(100): + sleep(0.1) + x = randint(0, 100) + Draw.now(f'{Mv.to(0, 0)}{my_graph.add(x)}') + Draw.now(f'{Mv.to(Term.height // 3 + 1, 0)}{my_graph2.add(x)}') + Draw.now(f'{Mv.to(Term.height - (Term.height // 3) + 2, 0)}{my_graph3.add(x)}') + Draw.now(f'{Mv.to(Term.height - (Term.height // 3) + 4, 0)}{my_graph4.add(x)}') + + Draw.now(Mv.to(Term.height -4, 0)) + # if not Key.reader.is_alive(): # clean_quit(1) # Key.new.wait(1.0) @@ -1613,19 +1704,20 @@ if __name__ == "__main__": #! For testing -------------------------------------------------------------------------------> if testing: try: - testing_collectors() - testing_humanizer() + testing_graphs() + #testing_collectors() + #testing_humanizer() # waitone(1) #testing_keyinput() - testing_banner() + #testing_banner() # waitone(1) - # testing_colors() + #testing_colors() # waitone(1) - testing_gradients() + #testing_gradients() # waitone(1) - testing_boxes() + #testing_boxes() # waitone(1) - testing_meter() + #testing_meter() # Draw.idle.clear() #Draw.now(f'{Mv.to(Term.height - 5, 1)}Any key to exit!') #waitone()