Added network graph color gradient bandwidth option by @drazil100

pull/81/head
aristocratos 2020-08-15 17:39:45 +02:00
parent 0226b55df0
commit c10bb09371
1 changed files with 88 additions and 58 deletions

146
bpytop.py
View File

@ -152,12 +152,15 @@ swap_disk=$swap_disk
#* If mem box should be split to also show disks info. #* If mem box should be split to also show disks info.
show_disks=$show_disks show_disks=$show_disks
#* Minimum value the network graphs scales down to, default "10K" = 10 KibiBytes, possible units "K" (KiB), "M" (MiB), "G" (GiB), no unit for bytes. #* Set fixed values for network graphs, default "10M" = 10 Mibibytes, possible units "K", "M", "G", append with "bit" for bits instead of bytes, i.e "100mbit"
net_download_min="$net_download_min" net_download="$net_download"
net_upload_min="$net_upload_min" net_upload="$net_upload"
#* Start in network graphs auto rescaling mode, ignores any values set above and rescale down to default value "10K". #* Start in network graphs auto rescaling mode, ignores any values set above and rescales down to 10 Kibibytes at the lowest.
net_auto_min=$net_auto_min net_auto=$net_auto
#* If the network graphs color gradient should scale to bandwith usage or auto scale, bandwith usage is based on "net_download" and "net_upload" values
net_color_fixed=$net_color_fixed
#* Show init screen at startup, the init screen is purely cosmetical #* Show init screen at startup, the init screen is purely cosmetical
show_init=$show_init show_init=$show_init
@ -337,7 +340,7 @@ def timeit_decorator(func):
class Config: class Config:
'''Holds all config variables and functions for loading from and saving to disk''' '''Holds all config variables and functions for loading from and saving to disk'''
keys: List[str] = ["color_theme", "update_ms", "proc_sorting", "proc_reversed", "proc_tree", "check_temp", "draw_clock", "background_update", "custom_cpu_name", "proc_colors", "proc_gradient", "proc_per_core", "proc_mem_bytes", keys: List[str] = ["color_theme", "update_ms", "proc_sorting", "proc_reversed", "proc_tree", "check_temp", "draw_clock", "background_update", "custom_cpu_name", "proc_colors", "proc_gradient", "proc_per_core", "proc_mem_bytes",
"disks_filter", "update_check", "log_level", "mem_graphs", "show_swap", "swap_disk", "show_disks", "net_download_min", "net_upload_min", "net_auto_min", "show_init", "mini_mode"] "disks_filter", "update_check", "log_level", "mem_graphs", "show_swap", "swap_disk", "show_disks", "net_download", "net_upload", "net_auto", "net_color_fixed", "show_init", "mini_mode"]
conf_dict: Dict[str, Union[str, int, bool]] = {} conf_dict: Dict[str, Union[str, int, bool]] = {}
color_theme: str = "Default" color_theme: str = "Default"
update_ms: int = 2000 update_ms: int = 2000
@ -358,9 +361,10 @@ class Config:
show_swap: bool = True show_swap: bool = True
swap_disk: bool = True swap_disk: bool = True
show_disks: bool = True show_disks: bool = True
net_download_min: str = "10K" net_download: str = "10M"
net_upload_min: str = "10K" net_upload: str = "10M"
net_auto_min: bool = False net_color_fixed: bool = False
net_auto: bool = False
show_init: bool = True show_init: bool = True
mini_mode: bool = False mini_mode: bool = False
log_level: str = "WARNING" log_level: str = "WARNING"
@ -1249,7 +1253,7 @@ class Graph:
last: int last: int
symbol: Dict[float, str] symbol: Dict[float, str]
def __init__(self, width: int, height: int, color: Union[List[str], Color, None], data: List[int], invert: bool = False, max_value: int = 0, offset: int = 0, color_max_value: int = None): def __init__(self, width: int, height: int, color: Union[List[str], Color, None], data: List[int], invert: bool = False, max_value: int = 0, offset: int = 0, color_max_value: Union[int, None] = None):
self.graphs: Dict[bool, List[str]] = {False : [], True : []} self.graphs: Dict[bool, List[str]] = {False : [], True : []}
self.current: bool = True self.current: bool = True
self.width = width self.width = width
@ -1259,7 +1263,7 @@ class Graph:
if not data: data = [0] if not data: data = [0]
if max_value: if max_value:
self.max_value = max_value self.max_value = max_value
data = [ (v + offset) * 100 // (max_value + offset) if v < max_value else 100 for v in data ] #* Convert values to percentage values of max_value with max_value as ceiling data = [ min(100, (v + offset) * 100 // (max_value + offset)) for v in data ] #* Convert values to percentage values of max_value with max_value as ceiling
else: else:
self.max_value = 0 self.max_value = 0
if color_max_value: if color_max_value:
@ -1873,7 +1877,8 @@ class NetBox(Box, SubBox):
stats = net.stats[net.nic][direction] stats = net.stats[net.nic][direction]
if stats["redraw"] or cls.resized: if stats["redraw"] or cls.resized:
if cls.redraw: stats["redraw"] = True if cls.redraw: stats["redraw"] = True
Graphs.net[direction] = Graph(w - bw - 3, cls.graph_height[direction], THEME.gradient[direction], stats["speed"], max_value=stats["graph_top"], invert=False if direction == "download" else True, color_max_value=stats["top"]) Graphs.net[direction] = Graph(w - bw - 3, cls.graph_height[direction], THEME.gradient[direction], stats["speed"], max_value=stats["graph_top"],
invert=False if direction == "download" else True, color_max_value=net.net_min.get(direction) if CONFIG.net_color_fixed else None)
out += f'{Mv.to(y if direction == "download" else y + cls.graph_height["download"], x)}{Graphs.net[direction](None if stats["redraw"] else stats["speed"][-1])}' out += f'{Mv.to(y if direction == "download" else y + cls.graph_height["download"], x)}{Graphs.net[direction](None if stats["redraw"] else stats["speed"][-1])}'
out += f'{Mv.to(by+cy, bx)}{THEME.main_fg}{cls.symbols[direction]} {strings["byte_ps"]:<10.10}{Mv.to(by+cy, bx+bw - 12)}{"(" + strings["bit_ps"] + ")":>12.12}' out += f'{Mv.to(by+cy, bx)}{THEME.main_fg}{cls.symbols[direction]} {strings["byte_ps"]:<10.10}{Mv.to(by+cy, bx+bw - 12)}{"(" + strings["bit_ps"] + ")":>12.12}'
@ -2804,7 +2809,7 @@ class NetCollector(Collector):
switched: bool = False switched: bool = False
timestamp: float = time() timestamp: float = time()
net_min: Dict[str, int] = {"download" : -1, "upload" : -1} net_min: Dict[str, int] = {"download" : -1, "upload" : -1}
auto_min: bool = CONFIG.net_auto_min auto_min: bool = CONFIG.net_auto
@classmethod @classmethod
def _get_nics(cls): def _get_nics(cls):
@ -2842,15 +2847,6 @@ class NetCollector(Collector):
stat: Dict stat: Dict
up_stat = psutil.net_if_stats() up_stat = psutil.net_if_stats()
if cls.net_min["download"] == -1 or cls.net_min["upload"] == -1:
cls.net_min["download"] = 10 << 10 if cls.auto_min else units_to_bytes(CONFIG.net_download_min)
cls.net_min["upload"] = 10 << 10 if cls.auto_min else units_to_bytes(CONFIG.net_upload_min)
try:
cls.stats[cls.nic]["download"].update({"graph_top" : cls.net_min["download"], "graph_lower" : 7})
cls.stats[cls.nic]["upload"].update({"graph_top" : cls.net_min["upload"], "graph_lower" : 7})
except:
pass
if cls.switched: if cls.switched:
cls.nic = cls.new_nic cls.nic = cls.new_nic
cls.switched = False cls.switched = False
@ -2867,7 +2863,7 @@ class NetCollector(Collector):
cls.stats[cls.nic] = {} cls.stats[cls.nic] = {}
cls.strings[cls.nic] = { "download" : {}, "upload" : {}} cls.strings[cls.nic] = { "download" : {}, "upload" : {}}
for direction, value in ["download", io_all.bytes_recv], ["upload", io_all.bytes_sent]: for direction, value in ["download", io_all.bytes_recv], ["upload", io_all.bytes_sent]:
cls.stats[cls.nic][direction] = { "total" : value, "last" : value, "top" : 0, "graph_top" : cls.net_min[direction], "offset" : 0, "speed" : [], "redraw" : True, "graph_raise" : 5, "graph_lower" : 5 } cls.stats[cls.nic][direction] = { "total" : value, "last" : value, "top" : 0, "graph_top" : 0, "offset" : 0, "speed" : [], "redraw" : True, "graph_raise" : 0, "graph_lower" : 7 }
for v in ["total", "byte_ps", "bit_ps", "top", "graph_top"]: for v in ["total", "byte_ps", "bit_ps", "top", "graph_top"]:
cls.strings[cls.nic][direction][v] = "" cls.strings[cls.nic][direction][v] = ""
@ -2882,6 +2878,14 @@ class NetCollector(Collector):
stat["last"] = stat["total"] stat["last"] = stat["total"]
speed = stat["speed"][-1] speed = stat["speed"][-1]
if cls.net_min[direction] == -1:
cls.net_min[direction] = units_to_bytes(CONFIG.net_download)
stat["graph_top"] = cls.net_min[direction]
stat["graph_lower"] = 7
if not cls.auto_min:
stat["redraw"] = True
strings["graph_top"] = floating_humanizer(stat["graph_top"], short=True)
if stat["offset"] and stat["offset"] > stat["total"]: if stat["offset"] and stat["offset"] > stat["total"]:
cls.reset = True cls.reset = True
@ -2905,23 +2909,23 @@ class NetCollector(Collector):
stat["top"] = speed stat["top"] = speed
strings["top"] = floating_humanizer(stat["top"], bit=True, per_second=True) strings["top"] = floating_humanizer(stat["top"], bit=True, per_second=True)
if speed > stat["graph_top"]: if cls.auto_min:
stat["graph_raise"] += 1 if speed > stat["graph_top"]:
if stat["graph_lower"] > 0: stat["graph_lower"] -= 1 stat["graph_raise"] += 1
elif stat["graph_top"] > cls.net_min[direction] and speed < stat["graph_top"] // 10: if stat["graph_lower"] > 0: stat["graph_lower"] -= 1
stat["graph_lower"] += 1 elif stat["graph_top"] > cls.net_min[direction] and speed < stat["graph_top"] // 10:
if stat["graph_raise"] > 0: stat["graph_raise"] -= 1 stat["graph_lower"] += 1
if stat["graph_raise"] > 0: stat["graph_raise"] -= 1
if stat["graph_raise"] >= 5 or stat["graph_lower"] >= 5: if stat["graph_raise"] >= 5 or stat["graph_lower"] >= 5:
if stat["graph_raise"] >= 5: if stat["graph_raise"] >= 5:
stat["graph_top"] = round(max(stat["speed"][-10:]) / 0.8) stat["graph_top"] = round(max(stat["speed"][-10:]) / 0.8)
elif stat["graph_lower"] >= 5: elif stat["graph_lower"] >= 5:
stat["graph_top"] = max(stat["speed"][-10:]) * 3 stat["graph_top"] = max(10 << 10, max(stat["speed"][-10:]) * 3)
if stat["graph_top"] < cls.net_min[direction]: stat["graph_top"] = cls.net_min[direction] stat["graph_raise"] = 0
stat["graph_raise"] = 0 stat["graph_lower"] = 0
stat["graph_lower"] = 0 stat["redraw"] = True
stat["redraw"] = True strings["graph_top"] = floating_humanizer(stat["graph_top"], short=True)
strings["graph_top"] = floating_humanizer(stat["graph_top"], short=True)
cls.timestamp = time() cls.timestamp = time()
@ -3596,31 +3600,45 @@ class Menu:
'Split memory box to also show disks.', 'Split memory box to also show disks.',
'', '',
'True or False.'], 'True or False.'],
"net_download_min" : [ "net_download" : [
'Min value the network graphs scales down to.', 'Fixed network graph download value.',
'', '',
'Default "10K" = 10 KibiBytes.', 'Default "10M" = 10 MibiBytes.',
'Possible units:', 'Possible units:',
'"K" (KiB), "M" (MiB), "G" (GiB),', '"K" (KiB), "M" (MiB), "G" (GiB).',
'no unit for bytes.',
'', '',
'Default values can be toggled with auto button.'], 'Append "bit" for bits instead of bytes,',
"net_upload_min" : [ 'i.e "100Mbit"',
'Min value the network graphs scales down to.',
'', '',
'Default "10K" = 10 KibiBytes.', 'Can be toggled with auto button.'],
"net_upload" : [
'Fixed network graph upload value.',
'',
'Default "10M" = 10 MibiBytes.',
'Possible units:', 'Possible units:',
'"K" (KiB), "M" (MiB), "G" (GiB),', '"K" (KiB), "M" (MiB), "G" (GiB).',
'no unit for bytes.',
'', '',
'Default values can be toggled with auto button.'], 'Append "bit" for bits instead of bytes,',
"net_auto_min" : [ 'i.e "100Mbit"',
'',
'Can be toggled with auto button.'],
"net_auto" : [
'Start in network graphs auto rescaling mode.', 'Start in network graphs auto rescaling mode.',
'', '',
'Ignores any values set above at start and', 'Ignores any values set above at start and',
'rescale down to default value "10K".', 'rescales down to 10KibiBytes at he lowest.',
'', '',
'True or False.'], 'True or False.'],
"net_color_fixed" : [
'Set network graphs color gradient to fixed.',
'',
'If True the network graphs color is based',
'on the total bandwidth usage instead of',
'the current autoscaling.',
'',
'The bandwidth usage is based on the',
'"net_download" and "net_upload" values set',
'above.'],
"show_init" : [ "show_init" : [
'Show init screen at startup.', 'Show init screen at startup.',
'', '',
@ -3776,7 +3794,7 @@ class Menu:
elif key in ["escape", "o", "M", "f2"]: elif key in ["escape", "o", "M", "f2"]:
cls.close = True cls.close = True
break break
elif key == "enter" and selected in ["update_ms", "disks_filter", "custom_cpu_name", "net_download_min", "net_upload_min", "draw_clock"]: elif key == "enter" and selected in ["update_ms", "disks_filter", "custom_cpu_name", "net_download", "net_upload", "draw_clock"]:
inputting = True inputting = True
input_val = str(getattr(CONFIG, selected)) input_val = str(getattr(CONFIG, selected))
elif key == "left" and selected == "update_ms" and CONFIG.update_ms - 100 >= 100: elif key == "left" and selected == "update_ms" and CONFIG.update_ms - 100 >= 100:
@ -3793,6 +3811,8 @@ class Menu:
else: else:
CpuCollector.sensor_method = "" CpuCollector.sensor_method = ""
CpuCollector.got_sensors = False CpuCollector.got_sensors = False
if selected in ["net_auto", "net_color_fixed"]:
NetBox.redraw = True
Term.refresh(force=True) Term.refresh(force=True)
cls.resized = False cls.resized = False
elif key in ["left", "right"] and selected == "color_theme" and len(Theme.themes) > 1: elif key in ["left", "right"] and selected == "color_theme" and len(Theme.themes) > 1:
@ -4081,11 +4101,20 @@ def units_to_bytes(value: str) -> int:
if not value: return 0 if not value: return 0
out: int = 0 out: int = 0
mult: int = 0 mult: int = 0
bit: bool = False
value_i: int = 0 value_i: int = 0
units: Dict[str, int] = {"K" : 1, "M" : 2, "G" : 3} units: Dict[str, int] = {"k" : 1, "m" : 2, "g" : 3}
try: try:
if value[-1].upper() in units: if value.lower().endswith("s"):
mult = units[value[-1].upper()] value = value[:-1]
if value.lower().endswith("bit"):
bit = True
value = value[:-3]
elif value.lower().endswith("byte"):
value = value[:-4]
if value[-1].lower() in units:
mult = units[value[-1].lower()]
value = value[:-1] value = value[:-1]
if "." in value and value.replace(".", "").isdigit(): if "." in value and value.replace(".", "").isdigit():
@ -4097,7 +4126,8 @@ def units_to_bytes(value: str) -> int:
elif value.isdigit(): elif value.isdigit():
value_i = int(value) value_i = int(value)
out = int(value_i) << 10 * mult if bit: value_i = round(value_i / 8)
out = int(value_i) << (10 * mult)
except ValueError: except ValueError:
out = 0 out = 0
return out return out