Added view mode toggle with 3 presets

pull/108/head
aristocratos 2020-09-05 16:29:06 +02:00
parent c21ef0d1ea
commit 6bf5a44b7f
1 changed files with 86 additions and 28 deletions

114
bpytop.py
View File

@ -61,7 +61,7 @@ VERSION: str = "1.0.22"
#? Argument parser ------------------------------------------------------------------------------->
if len(sys.argv) > 1:
for arg in sys.argv[1:]:
if not arg in ["-m", "--mini", "-v", "--version", "-h", "--help", "--debug"]:
if not arg in ["-m", "--mini", "-v", "--version", "-h", "--help", "--debug", "-f", "-p", "-s", "--full", "--proc", "--stat"]:
print(f'Unrecognized argument: {arg}\n'
f'Use argument -h or --help for help')
raise SystemExit(1)
@ -69,7 +69,9 @@ if len(sys.argv) > 1:
if "-h" in sys.argv or "--help" in sys.argv:
print(f'USAGE: {sys.argv[0]} [argument]\n\n'
f'Arguments:\n'
f' -m, --mini Start in minimal mode without memory and net boxes\n'
f' -f, --full Start in full mode showing all boxes [default]\n'
f' -p, --proc Start in minimal mode without memory and net boxes\n'
f' -s, --stat Start in minimal mode without process box\n'
f' -v, --version Show version info and exit\n'
f' -h, --help Show this help message and exit\n'
f' --debug Start with loglevel set to DEBUG overriding value set in config\n'
@ -80,6 +82,14 @@ elif "-v" in sys.argv or "--version" in sys.argv:
f'psutil version: {".".join(str(x) for x in psutil.version_info)}')
raise SystemExit(0)
ARG_MODE: str = ""
if "-f" in sys.argv or "--full" in sys.argv:
ARG_MODE = "full"
elif "-p" in sys.argv or "--proc" in sys.argv:
ARG_MODE = "proc"
elif "-s" in sys.argv or "--stat" in sys.argv:
ARG_MODE = "stat"
#? Variables ------------------------------------------------------------------------------------->
@ -102,6 +112,9 @@ color_theme="$color_theme"
#* If the theme set background should be shown, set to False if you want terminal background transparency
theme_background=$theme_background
#* Set bpytop view mode, "full" for everything shown, "proc" for cpu stats and processes, "stat" for cpu, mem, disks and net stats shown.
view_mode=$view_mode
#* Update time in milliseconds, increases automatically if set below internal loops processing time, recommended 2000 ms or above for better sample times for graphs.
update_ms=$update_ms
@ -171,9 +184,6 @@ show_init=$show_init
#* Enable check for new version from github.com/aristocratos/bpytop at start.
update_check=$update_check
#* Enable start in mini mode, can be toggled with shift+m at any time.
mini_mode=$mini_mode
#* Set loglevel for "~/.config/bpytop/error.log" levels are: "ERROR" "WARNING" "INFO" "DEBUG".
#* The level set includes all lower levels, i.e. "DEBUG" will show all logging info.
log_level=$log_level
@ -348,7 +358,7 @@ def timeit_decorator(func):
class Config:
'''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",
"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", "theme_background"]
"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", "view_mode", "theme_background"]
conf_dict: Dict[str, Union[str, int, bool]] = {}
color_theme: str = "Default"
theme_background: bool = True
@ -375,7 +385,7 @@ class Config:
net_color_fixed: bool = False
net_auto: bool = True
show_init: bool = True
mini_mode: bool = False
view_mode: str = "full"
log_level: str = "WARNING"
warnings: List[str] = []
@ -384,6 +394,8 @@ class Config:
sorting_options: List[str] = ["pid", "program", "arguments", "threads", "user", "memory", "cpu lazy", "cpu responsive"]
log_levels: List[str] = ["ERROR", "WARNING", "INFO", "DEBUG"]
view_modes: List[str] = ["full", "proc", "stat"]
changed: bool = False
recreate: bool = False
config_file: str = ""
@ -455,6 +467,9 @@ class Config:
if "log_level" in new_config and not new_config["log_level"] in self.log_levels:
new_config["log_level"] = "_error_"
self.warnings.append(f'Config key "log_level" didn\'t get an acceptable value!')
if "view_mode" in new_config and not new_config["view_mode"] in self.view_modes:
new_config["view_mode"] = "_error_"
self.warnings.append(f'Config key "view_mode" didn\'t get an acceptable value!')
if isinstance(new_config["update_ms"], int) and new_config["update_ms"] < 100:
new_config["update_ms"] = 100
self.warnings.append(f'Config key "update_ms" can\'t be lower than 100!')
@ -1458,7 +1473,8 @@ class Box:
y: int
width: int
height: int
mini_mode: bool = True if CONFIG.mini_mode or "-m" in sys.argv or "--mini" in sys.argv else False
proc_mode: bool = True if (CONFIG.view_mode == "proc" and not ARG_MODE) or ARG_MODE == "proc" else False
stat_mode: bool = True if (CONFIG.view_mode == "stat" and not ARG_MODE) or ARG_MODE == "stat" else False
out: str
bg: str
_b_cpu_h: int
@ -1530,7 +1546,7 @@ class CpuBox(Box, SubBox):
def _calc_size(cls):
cpu = CpuCollector
height_p: int
if cls.mini_mode: height_p = 20
if cls.proc_mode: height_p = 20
else: height_p = cls.height_p
cls.width = round(Term.width * cls.width_p / 100)
cls.height = round(Term.height * height_p / 100)
@ -1578,8 +1594,8 @@ class CpuBox(Box, SubBox):
if cls.resized or cls.redraw:
if not "m" in Key.mouse:
Key.mouse["m"] = [[cls.x + 16 + i, cls.y] for i in range(6)]
out_misc += f'{Mv.to(cls.y, cls.x + 16)}{THEME.cpu_box(Symbol.title_left)}{Fx.b if Box.mini_mode else ""}{THEME.hi_fg("m")}{THEME.title("ini")}{Fx.ub}{THEME.cpu_box(Symbol.title_right)}'
Key.mouse["m"] = [[cls.x + 16 + i, cls.y] for i in range(12)]
out_misc += f'{Mv.to(cls.y, cls.x + 16)}{THEME.cpu_box(Symbol.title_left)}{Fx.b}{THEME.hi_fg("m")}{THEME.title}ode:{ARG_MODE or CONFIG.view_mode}{Fx.ub}{THEME.cpu_box(Symbol.title_right)}'
Graphs.cpu["up"] = Graph(w - bw - 3, hh, THEME.gradient["cpu"], cpu.cpu_usage[0])
Graphs.cpu["down"] = Graph(w - bw - 3, h - hh, THEME.gradient["cpu"], cpu.cpu_usage[0], invert=True)
Meters.cpu = Meter(cpu.cpu_usage[0][-1], bw - (21 if cpu.got_sensors else 9), "cpu")
@ -1646,7 +1662,7 @@ class CpuBox(Box, SubBox):
class MemBox(Box):
name = "mem"
height_p = 40
height_p = 38
width_p = 45
x = 1
y = 1
@ -1667,8 +1683,13 @@ class MemBox(Box):
@classmethod
def _calc_size(cls):
cls.width = round(Term.width * cls.width_p / 100)
cls.height = round(Term.height * cls.height_p / 100) + 1
width_p: int; height_p: int
if cls.stat_mode:
width_p, height_p = 100, cls.height_p
else:
width_p, height_p = cls.width_p, cls.height_p
cls.width = round(Term.width * width_p / 100)
cls.height = round(Term.height * height_p / 100) + 1
Box._b_mem_h = cls.height
cls.y = Box._b_cpu_h + 1
if CONFIG.show_disks:
@ -1703,7 +1724,7 @@ class MemBox(Box):
@classmethod
def _draw_bg(cls) -> str:
if cls.mini_mode: return ""
if cls.proc_mode: return ""
out: str = ""
out += f'{create_box(box=cls, line_color=THEME.mem_box)}'
if CONFIG.show_disks:
@ -1715,7 +1736,7 @@ class MemBox(Box):
@classmethod
def _draw_fg(cls):
if cls.mini_mode: return
if cls.proc_mode: return
mem = MemCollector
if mem.redraw: cls.redraw = True
out: str = ""
@ -1830,7 +1851,7 @@ class MemBox(Box):
class NetBox(Box, SubBox):
name = "net"
height_p = 28
height_p = 30
width_p = 45
x = 1
y = 1
@ -1844,10 +1865,15 @@ class NetBox(Box, SubBox):
@classmethod
def _calc_size(cls):
cls.width = round(Term.width * cls.width_p / 100)
width_p: int
if cls.stat_mode:
width_p = 100
else:
width_p = cls.width_p
cls.width = round(Term.width * width_p / 100)
cls.height = Term.height - Box._b_cpu_h - Box._b_mem_h
cls.y = Term.height - cls.height + 1
cls.box_width = 27
cls.box_width = 27 if cls.width > 45 else 19
cls.box_height = 9 if cls.height > 10 else cls.height - 2
cls.box_x = cls.width - cls.box_width - 1
cls.box_y = cls.y + ((cls.height - 2) // 2) - cls.box_height // 2 + 1
@ -1857,13 +1883,13 @@ class NetBox(Box, SubBox):
@classmethod
def _draw_bg(cls) -> str:
if cls.mini_mode: return ""
if cls.proc_mode: return ""
return f'{create_box(box=cls, line_color=THEME.net_box)}\
{create_box(x=cls.box_x, y=cls.box_y, width=cls.box_width, height=cls.box_height, line_color=THEME.div_line, fill=False, title="Download", title2="Upload")}'
@classmethod
def _draw_fg(cls):
if cls.mini_mode: return
if cls.proc_mode: return
net = NetCollector
if net.redraw: cls.redraw = True
if not net.nic: return
@ -1900,7 +1926,8 @@ class NetBox(Box, SubBox):
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(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}' +
("" if bw < 20 else f'{Mv.to(by+cy, bx+bw - 12)}{"(" + strings["bit_ps"] + ")":>12.12}'))
cy += 1 if bh != 3 else 2
if bh >= 6:
out += f'{Mv.to(by+cy, bx)}{cls.symbols[direction]} {"Top:"}{Mv.to(by+cy, bx+bw - 12)}{"(" + strings["top"] + ")":>12.12}'
@ -1947,7 +1974,7 @@ class ProcBox(Box):
@classmethod
def _calc_size(cls):
width_p: int; height_p: int
if cls.mini_mode:
if cls.proc_mode:
width_p, height_p = 100, 80
else:
width_p, height_p = cls.width_p, cls.height_p
@ -1964,6 +1991,7 @@ class ProcBox(Box):
@classmethod
def _draw_bg(cls) -> str:
if cls.stat_mode: return ""
return create_box(box=cls, line_color=THEME.proc_box)
@classmethod
@ -2032,6 +2060,7 @@ class ProcBox(Box):
@classmethod
def _draw_fg(cls):
if cls.stat_mode: return
proc = ProcCollector
if proc.proc_interrupt: return
if proc.redraw: cls.redraw = True
@ -2978,6 +3007,7 @@ class ProcCollector(Collector):
@classmethod
def _collect(cls):
'''List all processess with pid, name, arguments, threads, username, memory percent and cpu percent'''
if Box.stat_mode: return
out: Dict = {}
cls.det_cpu = 0.0
sorting: str = CONFIG.proc_sorting
@ -3517,6 +3547,7 @@ class Menu:
d_quote: str
inputting: bool = False
input_val: str = ""
global ARG_MODE
Theme.refresh()
if not cls.background:
cls.background = f'{THEME.inactive_fg}' + Fx.uncolor(f'{Draw.saved_buffer()}') + f'{Term.fg}'
@ -3538,11 +3569,12 @@ class Menu:
'',
'Set to False if you want terminal background',
'transparency.'],
"mini_mode" : [
'Enable bpytop mini mode at start.',
"view_mode" : [
'Set bpytop view mode.',
'',
'Disables net and mem boxes and lowers height',
'of cpu box.'],
'"full" for everything shown.',
'"proc" for cpu stats and processes.',
'"stat" for cpu, mem, disks and net stats shown.'],
"update_ms" : [
'Update time in milliseconds.',
'',
@ -3707,6 +3739,7 @@ class Menu:
option_len: int = len(option_items) * 2
sorting_i: int = CONFIG.sorting_options.index(CONFIG.proc_sorting)
loglevel_i: int = CONFIG.log_levels.index(CONFIG.log_level)
view_mode_i: int = CONFIG.view_modes.index(CONFIG.view_mode)
color_i: int
while not cls.close:
key = ""
@ -3751,6 +3784,8 @@ class Menu:
counter = f' {sorting_i + 1}/{len(CONFIG.sorting_options)}'
elif opt == "log_level":
counter = f' {loglevel_i + 1}/{len(CONFIG.log_levels)}'
elif opt == "view_mode":
counter = f' {view_mode_i + 1}/{len(CONFIG.view_modes)}'
else:
counter = ""
out += f'{Mv.to(y+1+cy, x+1)}{t_color}{Fx.b}{opt.replace("_", " ").capitalize() + counter:^24.24}{Fx.ub}{Mv.to(y+2+cy, x+1)}{v_color}'
@ -3893,6 +3928,21 @@ class Menu:
CONFIG.log_level = CONFIG.log_levels[loglevel_i]
errlog.setLevel(getattr(logging, CONFIG.log_level))
errlog.info(f'Loglevel set to {CONFIG.log_level}')
elif key in ["left", "right"] and selected == "view_mode":
if key == "left":
view_mode_i -= 1
if view_mode_i < 0: view_mode_i = len(CONFIG.view_modes) - 1
elif key == "right":
view_mode_i += 1
if view_mode_i > len(CONFIG.view_modes) - 1: view_mode_i = 0
CONFIG.view_mode = CONFIG.view_modes[view_mode_i]
Box.proc_mode = True if CONFIG.view_mode == "proc" else False
Box.stat_mode = True if CONFIG.view_mode == "stat" else False
if ARG_MODE:
ARG_MODE = ""
Draw.clear(saved=True)
Term.refresh(force=True)
cls.resized = False
elif key == "up":
selected_int -= 1
if selected_int < 0: selected_int = len(option_items) - 1
@ -4254,6 +4304,7 @@ def units_to_bytes(value: str) -> int:
def process_keys():
mouse_pos: Tuple[int, int] = (0, 0)
filtered: bool = False
global ARG_MODE
while Key.has_key():
key = Key.get()
if key in ["mouse_scroll_up", "mouse_scroll_down", "mouse_click"]:
@ -4336,7 +4387,14 @@ def process_keys():
if not ProcCollector.search_filter: ProcBox.start = 0
Collector.collect(ProcCollector, redraw=True, only_draw=True)
elif key == "m":
Box.mini_mode = not Box.mini_mode
if ARG_MODE:
ARG_MODE = ""
elif CONFIG.view_modes.index(CONFIG.view_mode) + 1 > len(CONFIG.view_modes) - 1:
CONFIG.view_mode = CONFIG.view_modes[0]
else:
CONFIG.view_mode = CONFIG.view_modes[(CONFIG.view_modes.index(CONFIG.view_mode) + 1)]
Box.proc_mode = True if CONFIG.view_mode == "proc" else False
Box.stat_mode = True if CONFIG.view_mode == "stat" else False
Draw.clear(saved=True)
Term.refresh(force=True)
elif key.lower() in ["t", "k", "i"] and (ProcBox.selected > 0 or ProcCollector.detailed):