Changed: All boxes are now toggeable with key 1-4

pull/229/head
aristocratos 2021-01-02 20:51:27 +01:00
parent 24432152a7
commit 3cde5a3f9d
1 changed files with 262 additions and 168 deletions

430
bpytop.py
View File

@ -59,11 +59,9 @@ VERSION: str = "1.0.54"
#? Argument parser -------------------------------------------------------------------------------> #? Argument parser ------------------------------------------------------------------------------->
args = argparse.ArgumentParser() args = argparse.ArgumentParser()
args.add_argument("-f" , "--full" ,action="store_true" ,help ="Start in full mode showing all boxes [default]") args.add_argument("-b", "--boxes", action="store", dest="boxes", help ="Which boxes to show at start, example: -b \"cpu mem net proc\"")
args.add_argument("-p" , "--proc" ,action="store_true" ,help ="Start in minimal mode without memory and net boxes") args.add_argument("-v", "--version", action="store_true" ,help ="Show version info and exit")
args.add_argument("-s" , "--stat" ,action="store_true" ,help ="Start in minimal mode without process box") args.add_argument("--debug", action="store_true" ,help ="Start with loglevel set to DEBUG overriding value set in config")
args.add_argument("-v" , "--version" ,action="store_true" ,help ="Show version info and exit")
args.add_argument("--debug" ,action="store_true" ,help ="Start with loglevel set to DEBUG overriding value set in config")
stdargs = args.parse_args() stdargs = args.parse_args()
if stdargs.version: if stdargs.version:
@ -71,14 +69,7 @@ if stdargs.version:
f'psutil version: {".".join(str(x) for x in psutil.version_info)}') f'psutil version: {".".join(str(x) for x in psutil.version_info)}')
raise SystemExit(0) raise SystemExit(0)
ARG_MODE: str = "" ARG_BOXES: str = stdargs.boxes
if stdargs.full:
ARG_MODE = "full"
elif stdargs.proc:
ARG_MODE = "proc"
elif stdargs.stat:
ARG_MODE = "stat"
DEBUG = stdargs.debug DEBUG = stdargs.debug
@ -103,8 +94,8 @@ color_theme="$color_theme"
#* If the theme set background should be shown, set to False if you want terminal background transparency #* If the theme set background should be shown, set to False if you want terminal background transparency
theme_background=$theme_background 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. #* Manually set which boxes to show. Available values are "cpu mem net proc", seperate values with whitespace.
view_mode=$view_mode shown_boxes="$shown_boxes"
#* 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 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 update_ms=$update_ms
@ -310,6 +301,8 @@ UNITS: Dict[str, Tuple[str, ...]] = {
"byte" : ("Byte", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "BiB", "GEB") "byte" : ("Byte", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "BiB", "GEB")
} }
SUBSCRIPT: Tuple[str, ...] = ("", "", "", "", "", "", "", "", "", "")
#? Setup error logger ----------------------------------------------------------------> #? Setup error logger ---------------------------------------------------------------->
try: try:
@ -363,11 +356,12 @@ 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", 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", "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", "view_mode", "theme_background", "swap_disk", "show_disks", "net_download", "net_upload", "net_auto", "net_color_fixed", "show_init", "theme_background",
"net_sync", "show_battery", "tree_depth", "cpu_sensor", "show_coretemp", "proc_update_mult"] "net_sync", "show_battery", "tree_depth", "cpu_sensor", "show_coretemp", "proc_update_mult", "shown_boxes"]
conf_dict: Dict[str, Union[str, int, bool]] = {} conf_dict: Dict[str, Union[str, int, bool]] = {}
color_theme: str = "Default" color_theme: str = "Default"
theme_background: bool = True theme_background: bool = True
shown_boxes: str = "cpu mem net proc"
update_ms: int = 2000 update_ms: int = 2000
proc_update_mult: int = 2 proc_update_mult: int = 2
proc_sorting: str = "cpu lazy" proc_sorting: str = "cpu lazy"
@ -397,7 +391,6 @@ class Config:
net_sync: bool = False net_sync: bool = False
show_battery: bool = True show_battery: bool = True
show_init: bool = True show_init: bool = True
view_mode: str = "full"
log_level: str = "WARNING" log_level: str = "WARNING"
warnings: List[str] = [] warnings: List[str] = []
@ -406,8 +399,6 @@ class Config:
sorting_options: List[str] = ["pid", "program", "arguments", "threads", "user", "memory", "cpu lazy", "cpu responsive"] sorting_options: List[str] = ["pid", "program", "arguments", "threads", "user", "memory", "cpu lazy", "cpu responsive"]
log_levels: List[str] = ["ERROR", "WARNING", "INFO", "DEBUG"] log_levels: List[str] = ["ERROR", "WARNING", "INFO", "DEBUG"]
view_modes: List[str] = ["full", "proc", "stat"]
cpu_sensors: List[str] = [ "Auto" ] cpu_sensors: List[str] = [ "Auto" ]
if hasattr(psutil, "sensors_temperatures"): if hasattr(psutil, "sensors_temperatures"):
@ -494,9 +485,6 @@ class Config:
if "log_level" in new_config and not new_config["log_level"] in self.log_levels: if "log_level" in new_config and not new_config["log_level"] in self.log_levels:
new_config["log_level"] = "_error_" new_config["log_level"] = "_error_"
self.warnings.append(f'Config key "log_level" didn\'t get an acceptable value!') 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 "update_ms" in new_config and int(new_config["update_ms"]) < 100: if "update_ms" in new_config and int(new_config["update_ms"]) < 100:
new_config["update_ms"] = 100 new_config["update_ms"] = 100
self.warnings.append(f'Config key "update_ms" can\'t be lower than 100!') self.warnings.append(f'Config key "update_ms" can\'t be lower than 100!')
@ -506,6 +494,12 @@ class Config:
if "cpu_sensor" in new_config and not new_config["cpu_sensor"] in self.cpu_sensors: if "cpu_sensor" in new_config and not new_config["cpu_sensor"] in self.cpu_sensors:
new_config["cpu_sensor"] = "_error_" new_config["cpu_sensor"] = "_error_"
self.warnings.append(f'Config key "cpu_sensor" does not contain an available sensor!') self.warnings.append(f'Config key "cpu_sensor" does not contain an available sensor!')
if "shown_boxes" in new_config and not new_config["shown_boxes"] == "":
for box in new_config["shown_boxes"].split(): #type: ignore
if not box in ["cpu", "mem", "net", "proc"]:
new_config["shown_boxes"] = "_error_"
self.warnings.append(f'Config key "shown_boxes" contains invalid box names!')
break
return new_config return new_config
def save_config(self): def save_config(self):
@ -540,6 +534,14 @@ except Exception as e:
errlog.exception(f'{e}') errlog.exception(f'{e}')
raise SystemExit(1) raise SystemExit(1)
if ARG_BOXES:
_new_boxes: List = []
for _box in ARG_BOXES.split():
if _box in ["cpu", "mem", "net", "proc"]:
_new_boxes.append(_box)
CONFIG.shown_boxes = " ".join(_new_boxes)
del _box, _new_boxes
if SYSTEM == "Linux" and not os.path.isdir("/sys/class/power_supply"): if SYSTEM == "Linux" and not os.path.isdir("/sys/class/power_supply"):
CONFIG.show_battery = False CONFIG.show_battery = False
@ -1504,14 +1506,22 @@ class Meters:
class Box: class Box:
'''Box class with all needed attributes for create_box() function''' '''Box class with all needed attributes for create_box() function'''
name: str name: str
num: int = 0
boxes: List = []
view_modes: Dict[str, List] = {"full" : ["cpu", "mem", "net", "proc"], "stat" : ["cpu", "mem", "net"], "proc" : ["cpu", "proc"]}
view_mode: str
for view_mode in view_modes:
if sorted(CONFIG.shown_boxes.split(), key=str.lower) == view_modes[view_mode]:
break
else:
view_mode = "user"
view_modes["user"] = CONFIG.shown_boxes.split()
height_p: int height_p: int
width_p: int width_p: int
x: int x: int
y: int y: int
width: int width: int
height: int height: int
proc_mode: bool = (CONFIG.view_mode == "proc" and not ARG_MODE) or ARG_MODE == "proc"
stat_mode: bool = (CONFIG.view_mode == "stat" and not ARG_MODE) or ARG_MODE == "stat"
out: str out: str
bg: str bg: str
_b_cpu_h: int _b_cpu_h: int
@ -1532,12 +1542,14 @@ class Box:
@classmethod @classmethod
def calc_sizes(cls): def calc_sizes(cls):
'''Calculate sizes of boxes''' '''Calculate sizes of boxes'''
cls.boxes = CONFIG.shown_boxes.split()
for sub in cls.__subclasses__(): for sub in cls.__subclasses__():
sub._calc_size() # type: ignore sub._calc_size() # type: ignore
sub.resized = True # type: ignore sub.resized = True # type: ignore
@classmethod @classmethod
def draw_update_ms(cls, now: bool = True): def draw_update_ms(cls, now: bool = True):
if not "cpu" in cls.boxes: return
update_string: str = f'{CONFIG.update_ms}ms' update_string: str = f'{CONFIG.update_ms}ms'
xpos: int = CpuBox.x + CpuBox.width - len(update_string) - 15 xpos: int = CpuBox.x + CpuBox.width - len(update_string) - 15
if not "+" in Key.mouse: if not "+" in Key.mouse:
@ -1553,9 +1565,10 @@ class Box:
@classmethod @classmethod
def draw_clock(cls, force: bool = False): def draw_clock(cls, force: bool = False):
if not "cpu" in cls.boxes or not cls.clock_on: return
out: str = "" out: str = ""
if force: pass if force: pass
elif not cls.clock_on or Term.resized or strftime(CONFIG.draw_clock) == cls.clock: return elif Term.resized or strftime(CONFIG.draw_clock) == cls.clock: return
clock_string = cls.clock = strftime(CONFIG.draw_clock) clock_string = cls.clock = strftime(CONFIG.draw_clock)
for custom in cls.clock_custom_format: for custom in cls.clock_custom_format:
if custom in clock_string: if custom in clock_string:
@ -1572,10 +1585,28 @@ class Box:
if CONFIG.show_battery and hasattr(psutil, "sensors_battery") and psutil.sensors_battery(): if CONFIG.show_battery and hasattr(psutil, "sensors_battery") and psutil.sensors_battery():
Draw.out("battery") Draw.out("battery")
@classmethod
def empty_bg(cls) -> str:
return (f'{Term.clear}{Banner.draw(Term.height // 2 - 10, center=True)}'
f'{Mv.d(1)}{Mv.l(46)}{Colors.black_bg}{Colors.default}{Fx.b}[esc] Menu'
f'{Mv.r(25)}{Fx.i}Version: {VERSION}{Fx.ui}'
f'{Mv.d(1)}{Mv.l(34)}{Fx.b}All boxes hidden!'
f'{Mv.d(1)}{Mv.l(17)}{Fx.b}[1] {Fx.ub}Toggle CPU box'
f'{Mv.d(1)}{Mv.l(18)}{Fx.b}[2] {Fx.ub}Toggle MEM box'
f'{Mv.d(1)}{Mv.l(18)}{Fx.b}[3] {Fx.ub}Toggle NET box'
f'{Mv.d(1)}{Mv.l(18)}{Fx.b}[4] {Fx.ub}Toggle PROC box'
f'{Mv.d(1)}{Mv.l(19)}{Fx.b}[m] {Fx.ub}Cycle presets'
f'{Mv.d(1)}{Mv.l(17)}{Fx.b}[q] Quit {Fx.ub}{Term.bg}{Term.fg}')
@classmethod @classmethod
def draw_bg(cls, now: bool = True): def draw_bg(cls, now: bool = True):
'''Draw all boxes outlines and titles''' '''Draw all boxes outlines and titles'''
Draw.buffer("bg", "".join(sub._draw_bg() for sub in cls.__subclasses__()), now=now, z=1000, only_save=Menu.active, once=True) # type: ignore out: str = ""
if not cls.boxes:
out = cls.empty_bg()
else:
out = "".join(sub._draw_bg() for sub in cls.__subclasses__()) # type: ignore
Draw.buffer("bg", out, now=now, z=1000, only_save=Menu.active, once=True)
cls.draw_update_ms(now=now) cls.draw_update_ms(now=now)
if CONFIG.draw_clock: cls.draw_clock(force=True) if CONFIG.draw_clock: cls.draw_clock(force=True)
@ -1589,6 +1620,7 @@ class SubBox:
class CpuBox(Box, SubBox): class CpuBox(Box, SubBox):
name = "cpu" name = "cpu"
num = 1
x = 1 x = 1
y = 1 y = 1
height_p = 32 height_p = 32
@ -1612,10 +1644,16 @@ class CpuBox(Box, SubBox):
@classmethod @classmethod
def _calc_size(cls): def _calc_size(cls):
if not "cpu" in cls.boxes:
Box._b_cpu_h = 0
cls.width = Term.width
return
cpu = CpuCollector cpu = CpuCollector
height_p: int height_p: int
if cls.proc_mode: height_p = 20 if cls.boxes == ["cpu"]:
else: height_p = cls.height_p height_p = 100
else:
height_p = cls.height_p
cls.width = round(Term.width * cls.width_p / 100) cls.width = round(Term.width * cls.width_p / 100)
cls.height = round(Term.height * height_p / 100) cls.height = round(Term.height * height_p / 100)
if cls.height < 8: cls.height = 8 if cls.height < 8: cls.height = 8
@ -1643,6 +1681,7 @@ class CpuBox(Box, SubBox):
@classmethod @classmethod
def _draw_bg(cls) -> str: def _draw_bg(cls) -> str:
if not "cpu" in cls.boxes: return ""
if not "M" in Key.mouse: if not "M" in Key.mouse:
Key.mouse["M"] = [[cls.x + 10 + i, cls.y] for i in range(6)] Key.mouse["M"] = [[cls.x + 10 + i, cls.y] for i in range(6)]
return (f'{create_box(box=cls, line_color=THEME.cpu_box)}' return (f'{create_box(box=cls, line_color=THEME.cpu_box)}'
@ -1692,6 +1731,7 @@ class CpuBox(Box, SubBox):
@classmethod @classmethod
def _draw_fg(cls): def _draw_fg(cls):
if not "cpu" in cls.boxes: return
cpu = CpuCollector cpu = CpuCollector
if cpu.redraw: cls.redraw = True if cpu.redraw: cls.redraw = True
out: str = "" out: str = ""
@ -1706,7 +1746,7 @@ class CpuBox(Box, SubBox):
if cls.resized or cls.redraw: if cls.resized or cls.redraw:
if not "m" in Key.mouse: if not "m" in Key.mouse:
Key.mouse["m"] = [[cls.x + 16 + i, cls.y] for i in range(12)] 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)}' 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:{Box.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["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) 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") Meters.cpu = Meter(cpu.cpu_usage[0][-1], bw - (21 if cpu.got_sensors else 9), "cpu")
@ -1812,6 +1852,7 @@ class CpuBox(Box, SubBox):
class MemBox(Box): class MemBox(Box):
name = "mem" name = "mem"
num = 2
height_p = 38 height_p = 38
width_p = 45 width_p = 45
x = 1 x = 1
@ -1833,11 +1874,23 @@ class MemBox(Box):
@classmethod @classmethod
def _calc_size(cls): def _calc_size(cls):
if not "mem" in cls.boxes:
Box._b_mem_h = 0
cls.width = Term.width
return
width_p: int; height_p: int width_p: int; height_p: int
if cls.stat_mode: if not "proc" in cls.boxes:
width_p, height_p = 100, cls.height_p width_p = 100
else: else:
width_p, height_p = cls.width_p, cls.height_p width_p = cls.width_p
if not "cpu" in cls.boxes:
height_p = 60 if "net" in cls.boxes else 98
elif not "net" in cls.boxes:
height_p = 98 - CpuBox.height_p
else:
height_p = cls.height_p
cls.width = round(Term.width * width_p / 100) cls.width = round(Term.width * width_p / 100)
cls.height = round(Term.height * height_p / 100) + 1 cls.height = round(Term.height * height_p / 100) + 1
Box._b_mem_h = cls.height Box._b_mem_h = cls.height
@ -1874,19 +1927,23 @@ class MemBox(Box):
@classmethod @classmethod
def _draw_bg(cls) -> str: def _draw_bg(cls) -> str:
if cls.proc_mode: return "" if not "mem" in cls.boxes: return ""
out: str = "" out: str = ""
out += f'{create_box(box=cls, line_color=THEME.mem_box)}' out += f'{create_box(box=cls, line_color=THEME.mem_box)}'
if CONFIG.show_disks: if CONFIG.show_disks:
out += (f'{Mv.to(cls.y, cls.divider + 2)}{THEME.mem_box(Symbol.title_left)}{Fx.b}{THEME.title("disks")}{Fx.ub}{THEME.mem_box(Symbol.title_right)}' out += (f'{Mv.to(cls.y, cls.divider + 2)}{THEME.mem_box(Symbol.title_left)}{Fx.b}{THEME.hi_fg("d")}{THEME.title("isks")}{Fx.ub}{THEME.mem_box(Symbol.title_right)}'
f'{Mv.to(cls.y, cls.divider)}{THEME.mem_box(Symbol.div_up)}' f'{Mv.to(cls.y, cls.divider)}{THEME.mem_box(Symbol.div_up)}'
f'{Mv.to(cls.y + cls.height - 1, cls.divider)}{THEME.mem_box(Symbol.div_down)}{THEME.div_line}' f'{Mv.to(cls.y + cls.height - 1, cls.divider)}{THEME.mem_box(Symbol.div_down)}{THEME.div_line}'
f'{"".join(f"{Mv.to(cls.y + i, cls.divider)}{Symbol.v_line}" for i in range(1, cls.height - 1))}') f'{"".join(f"{Mv.to(cls.y + i, cls.divider)}{Symbol.v_line}" for i in range(1, cls.height - 1))}')
Key.mouse["d"] = [[cls.divider + 3 + i, cls.y] for i in range(5)]
else:
out += f'{Mv.to(cls.y, cls.x + cls.width - 9)}{THEME.mem_box(Symbol.title_left)}{THEME.hi_fg("d")}{THEME.title("isks")}{THEME.mem_box(Symbol.title_right)}'
Key.mouse["d"] = [[cls.x + cls.width - 8 + i, cls.y] for i in range(5)]
return out return out
@classmethod @classmethod
def _draw_fg(cls): def _draw_fg(cls):
if cls.proc_mode: return if not "mem" in cls.boxes: return
mem = MemCollector mem = MemCollector
if mem.redraw: cls.redraw = True if mem.redraw: cls.redraw = True
out: str = "" out: str = ""
@ -1923,8 +1980,8 @@ class MemBox(Box):
if len(mem.disks) * 3 <= h + 1: if len(mem.disks) * 3 <= h + 1:
Meters.disks_free[name] = Meter(mem.disks[name]["free_percent"], cls.disk_meter, "free") Meters.disks_free[name] = Meter(mem.disks[name]["free_percent"], cls.disk_meter, "free")
if not "g" in Key.mouse: if not "g" in Key.mouse:
Key.mouse["g"] = [[x + cls.mem_width - 8 + i, y-1] for i in range(5)] Key.mouse["g"] = [[x + 8 + i, y-1] for i in range(5)]
out_misc += (f'{Mv.to(y-1, x + cls.mem_width - 9)}{THEME.mem_box(Symbol.title_left)}{Fx.b if CONFIG.mem_graphs else ""}' out_misc += (f'{Mv.to(y-1, x + 7)}{THEME.mem_box(Symbol.title_left)}{Fx.b if CONFIG.mem_graphs else ""}'
f'{THEME.hi_fg("g")}{THEME.title("raph")}{Fx.ub}{THEME.mem_box(Symbol.title_right)}') f'{THEME.hi_fg("g")}{THEME.title("raph")}{Fx.ub}{THEME.mem_box(Symbol.title_right)}')
if CONFIG.show_disks: if CONFIG.show_disks:
if not "s" in Key.mouse: if not "s" in Key.mouse:
@ -2004,6 +2061,7 @@ class MemBox(Box):
class NetBox(Box, SubBox): class NetBox(Box, SubBox):
name = "net" name = "net"
num = 3
height_p = 30 height_p = 30
width_p = 45 width_p = 45
x = 1 x = 1
@ -2018,11 +2076,14 @@ class NetBox(Box, SubBox):
@classmethod @classmethod
def _calc_size(cls): def _calc_size(cls):
width_p: int if not "net" in cls.boxes:
if cls.stat_mode: cls.width = Term.width
return
if not "proc" in cls.boxes:
width_p = 100 width_p = 100
else: else:
width_p = cls.width_p width_p = cls.width_p
cls.width = round(Term.width * width_p / 100) cls.width = round(Term.width * width_p / 100)
cls.height = Term.height - Box._b_cpu_h - Box._b_mem_h cls.height = Term.height - Box._b_cpu_h - Box._b_mem_h
cls.y = Term.height - cls.height + 1 cls.y = Term.height - cls.height + 1
@ -2036,13 +2097,13 @@ class NetBox(Box, SubBox):
@classmethod @classmethod
def _draw_bg(cls) -> str: def _draw_bg(cls) -> str:
if cls.proc_mode: return "" if not "net" in cls.boxes: return ""
return f'{create_box(box=cls, line_color=THEME.net_box)}\ 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")}' {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 @classmethod
def _draw_fg(cls): def _draw_fg(cls):
if cls.proc_mode: return if not "net" in cls.boxes: return
net = NetCollector net = NetCollector
if net.redraw: cls.redraw = True if net.redraw: cls.redraw = True
if not net.nic: return if not net.nic: return
@ -2103,6 +2164,7 @@ class NetBox(Box, SubBox):
class ProcBox(Box): class ProcBox(Box):
name = "proc" name = "proc"
num = 4
height_p = 68 height_p = 68
width_p = 55 width_p = 55
x = 1 x = 1
@ -2131,11 +2193,20 @@ class ProcBox(Box):
@classmethod @classmethod
def _calc_size(cls): def _calc_size(cls):
if not "proc" in cls.boxes:
cls.width = Term.width
return
width_p: int; height_p: int width_p: int; height_p: int
if cls.proc_mode: if not "net" in cls.boxes and not "mem" in cls.boxes:
width_p, height_p = 100, 80 width_p = 100
else: else:
width_p, height_p = cls.width_p, cls.height_p width_p = cls.width_p
if not "cpu" in cls.boxes:
height_p = 100
else:
height_p = cls.height_p
cls.width = round(Term.width * width_p / 100) cls.width = round(Term.width * width_p / 100)
cls.height = round(Term.height * height_p / 100) cls.height = round(Term.height * height_p / 100)
if cls.height + Box._b_cpu_h > Term.height: cls.height = Term.height - Box._b_cpu_h if cls.height + Box._b_cpu_h > Term.height: cls.height = Term.height - Box._b_cpu_h
@ -2149,7 +2220,7 @@ class ProcBox(Box):
@classmethod @classmethod
def _draw_bg(cls) -> str: def _draw_bg(cls) -> str:
if cls.stat_mode: return "" if not "proc" in cls.boxes: return ""
return create_box(box=cls, line_color=THEME.proc_box) return create_box(box=cls, line_color=THEME.proc_box)
@classmethod @classmethod
@ -2218,7 +2289,7 @@ class ProcBox(Box):
@classmethod @classmethod
def _draw_fg(cls): def _draw_fg(cls):
if cls.stat_mode: return if not "proc" in cls.boxes: return
proc = ProcCollector proc = ProcCollector
if proc.proc_interrupt: return if proc.proc_interrupt: return
if proc.redraw: cls.redraw = True if proc.redraw: cls.redraw = True
@ -2276,7 +2347,7 @@ class ProcBox(Box):
for k in ["e", "r", "c", "t", "k", "i", "enter", "left", " ", "f", "delete"]: for k in ["e", "r", "c", "t", "k", "i", "enter", "left", " ", "f", "delete"]:
if k in Key.mouse: del Key.mouse[k] if k in Key.mouse: del Key.mouse[k]
if proc.detailed: if proc.detailed:
killed = proc.details["killed"] killed = proc.details.get("killed", False)
main = THEME.main_fg if cls.selected == 0 and not killed else THEME.inactive_fg main = THEME.main_fg if cls.selected == 0 and not killed else THEME.inactive_fg
hi = THEME.hi_fg if cls.selected == 0 and not killed else THEME.inactive_fg hi = THEME.hi_fg if cls.selected == 0 and not killed else THEME.inactive_fg
title = THEME.title if cls.selected == 0 and not killed else THEME.inactive_fg title = THEME.title if cls.selected == 0 and not killed else THEME.inactive_fg
@ -2285,7 +2356,7 @@ class ProcBox(Box):
cls.current_h = cls.height - 8 cls.current_h = cls.height - 8
for i in range(7): out_misc += f'{Mv.to(dy+i, x)}{" " * w}' for i in range(7): out_misc += f'{Mv.to(dy+i, x)}{" " * w}'
out_misc += (f'{Mv.to(dy+7, x-1)}{THEME.proc_box}{Symbol.title_right}{Symbol.h_line*w}{Symbol.title_left}' out_misc += (f'{Mv.to(dy+7, x-1)}{THEME.proc_box}{Symbol.title_right}{Symbol.h_line*w}{Symbol.title_left}'
f'{Mv.to(dy+7, x+1)}{THEME.proc_box(Symbol.title_left)}{Fx.b}{THEME.title(cls.name)}{Fx.ub}{THEME.proc_box(Symbol.title_right)}{THEME.div_line}') f'{Mv.to(dy+7, x+1)}{THEME.proc_box(Symbol.title_left)}{Fx.b}{THEME.hi_fg(SUBSCRIPT[cls.num])}{THEME.title(cls.name)}{Fx.ub}{THEME.proc_box(Symbol.title_right)}{THEME.div_line}')
for i in range(7): for i in range(7):
out_misc += f'{Mv.to(dy + i, dgx + dgw + 1)}{Symbol.v_line}' out_misc += f'{Mv.to(dy + i, dgx + dgw + 1)}{Symbol.v_line}'
@ -2322,7 +2393,7 @@ class ProcBox(Box):
cls.current_h = cls.height cls.current_h = cls.height
y, h = cls.y + 1, cls.height - 2 y, h = cls.y + 1, cls.height - 2
out_misc += (f'{Mv.to(y-1, x-1)}{THEME.proc_box}{Symbol.left_up}{Symbol.h_line*w}{Symbol.right_up}' out_misc += (f'{Mv.to(y-1, x-1)}{THEME.proc_box}{Symbol.left_up}{Symbol.h_line*w}{Symbol.right_up}'
f'{Mv.to(y-1, x+1)}{THEME.proc_box(Symbol.title_left)}{Fx.b}{THEME.title(cls.name)}{Fx.ub}{THEME.proc_box(Symbol.title_right)}' f'{Mv.to(y-1, x+1)}{THEME.proc_box(Symbol.title_left)}{Fx.b}{THEME.hi_fg(SUBSCRIPT[cls.num])}{THEME.title(cls.name)}{Fx.ub}{THEME.proc_box(Symbol.title_right)}'
f'{Mv.to(y+7, x-1)}{THEME.proc_box(Symbol.v_line)}{Mv.r(w)}{THEME.proc_box(Symbol.v_line)}') f'{Mv.to(y+7, x-1)}{THEME.proc_box(Symbol.v_line)}{Mv.r(w)}{THEME.proc_box(Symbol.v_line)}')
cls.select_max = cls.height - 3 cls.select_max = cls.height - 3
@ -2352,12 +2423,12 @@ class ProcBox(Box):
out_misc += (f'{Mv.to(y-1, sort_pos - 25)}{THEME.proc_box(Symbol.title_left)}{Fx.b if CONFIG.proc_per_core else ""}' out_misc += (f'{Mv.to(y-1, sort_pos - 25)}{THEME.proc_box(Symbol.title_left)}{Fx.b if CONFIG.proc_per_core else ""}'
f'{THEME.title("per-")}{THEME.hi_fg("c")}{THEME.title("ore")}{Fx.ub}{THEME.proc_box(Symbol.title_right)}') f'{THEME.title("per-")}{THEME.hi_fg("c")}{THEME.title("ore")}{Fx.ub}{THEME.proc_box(Symbol.title_right)}')
if not "f" in Key.mouse or cls.resized: Key.mouse["f"] = [[x+5 + i, y-1] for i in range(6 if not proc.search_filter else 2 + len(proc.search_filter[-10:]))] if not "f" in Key.mouse or cls.resized: Key.mouse["f"] = [[x+6 + i, y-1] for i in range(6 if not proc.search_filter else 2 + len(proc.search_filter[-10:]))]
if proc.search_filter: if proc.search_filter:
if not "delete" in Key.mouse: Key.mouse["delete"] = [[x+11 + len(proc.search_filter[-10:]) + i, y-1] for i in range(3)] if not "delete" in Key.mouse: Key.mouse["delete"] = [[x+12 + len(proc.search_filter[-10:]) + i, y-1] for i in range(3)]
elif "delete" in Key.mouse: elif "delete" in Key.mouse:
del Key.mouse["delete"] del Key.mouse["delete"]
out_misc += (f'{Mv.to(y-1, x + 7)}{THEME.proc_box(Symbol.title_left)}{Fx.b if cls.filtering or proc.search_filter else ""}{THEME.hi_fg("f")}{THEME.title}' + out_misc += (f'{Mv.to(y-1, x + 8)}{THEME.proc_box(Symbol.title_left)}{Fx.b if cls.filtering or proc.search_filter else ""}{THEME.hi_fg("f")}{THEME.title}' +
("ilter" if not proc.search_filter and not cls.filtering else f' {proc.search_filter[-(10 if w < 83 else w - 74):]}{(Fx.bl + "" + Fx.ubl) if cls.filtering else THEME.hi_fg(" del")}') + ("ilter" if not proc.search_filter and not cls.filtering else f' {proc.search_filter[-(10 if w < 83 else w - 74):]}{(Fx.bl + "" + Fx.ubl) if cls.filtering else THEME.hi_fg(" del")}') +
f'{THEME.proc_box(Symbol.title_right)}') f'{THEME.proc_box(Symbol.title_right)}')
@ -2656,8 +2727,6 @@ class Collector:
cls.proc_counter = 0 cls.proc_counter = 0
cls.proc_counter += 1 cls.proc_counter += 1
cls.collect_run.set() cls.collect_run.set()
@ -3278,7 +3347,7 @@ class ProcCollector(Collector):
@classmethod @classmethod
def _collect(cls): def _collect(cls):
'''List all processess with pid, name, arguments, threads, username, memory percent and cpu percent''' '''List all processess with pid, name, arguments, threads, username, memory percent and cpu percent'''
if Box.stat_mode: return if not "proc" in Box.boxes: return
out: Dict = {} out: Dict = {}
cls.det_cpu = 0.0 cls.det_cpu = 0.0
sorting: str = CONFIG.proc_sorting sorting: str = CONFIG.proc_sorting
@ -3694,7 +3763,12 @@ class Menu:
"Selected (Mouse 1)" : "Show detailed information for selected process.", "Selected (Mouse 1)" : "Show detailed information for selected process.",
"(Mouse scroll)" : "Scrolls any scrollable list/text under cursor.", "(Mouse scroll)" : "Scrolls any scrollable list/text under cursor.",
"(Esc, shift+m)" : "Toggles main menu.", "(Esc, shift+m)" : "Toggles main menu.",
"(m)" : "Change current view mode, order full->proc->stat.", "(m)" : "Cycle view presets, order: full->proc->stat->user.",
"(1)" : "Toggle CPU box.",
"(2)" : "Toggle MEM box.",
"(3)" : "Toggle NET box.",
"(4)" : "Toggle PROC box.",
"(d)" : "Toggle disks view in MEM box.",
"(F2, o)" : "Shows options.", "(F2, o)" : "Shows options.",
"(F1, h)" : "Shows this window.", "(F1, h)" : "Shows this window.",
"(ctrl+z)" : "Sleep program and put in background.", "(ctrl+z)" : "Sleep program and put in background.",
@ -3819,7 +3893,6 @@ class Menu:
d_quote: str d_quote: str
inputting: bool = False inputting: bool = False
input_val: str = "" input_val: str = ""
global ARG_MODE
Theme.refresh() Theme.refresh()
if not cls.background: if not cls.background:
cls.background = f'{THEME.inactive_fg}' + Fx.uncolor(f'{Draw.saved_buffer()}') + f'{Term.fg}' cls.background = f'{THEME.inactive_fg}' + Fx.uncolor(f'{Draw.saved_buffer()}') + f'{Term.fg}'
@ -3841,12 +3914,13 @@ class Menu:
'', '',
'Set to False if you want terminal background', 'Set to False if you want terminal background',
'transparency.'], 'transparency.'],
"view_mode" : [ "shown_boxes" : [
'Set bpytop view mode.', 'Manually set which boxes to show.',
'', '',
'"full" for everything shown.', 'Available values are "cpu mem net proc".',
'"proc" for cpu stats and processes.', 'Seperate values with whitespace.',
'"stat" for cpu, mem, disks and net stats shown.'], '',
'Toggle between presets with mode key "m".'],
"update_ms" : [ "update_ms" : [
'Update time in milliseconds.', 'Update time in milliseconds.',
'', '',
@ -4050,7 +4124,6 @@ class Menu:
option_len: int = len(option_items) * 2 option_len: int = len(option_items) * 2
sorting_i: int = CONFIG.sorting_options.index(CONFIG.proc_sorting) sorting_i: int = CONFIG.sorting_options.index(CONFIG.proc_sorting)
loglevel_i: int = CONFIG.log_levels.index(CONFIG.log_level) loglevel_i: int = CONFIG.log_levels.index(CONFIG.log_level)
view_mode_i: int = CONFIG.view_modes.index(CONFIG.view_mode)
cpu_sensor_i: int = CONFIG.cpu_sensors.index(CONFIG.cpu_sensor) cpu_sensor_i: int = CONFIG.cpu_sensors.index(CONFIG.cpu_sensor)
color_i: int color_i: int
while not cls.close: while not cls.close:
@ -4096,15 +4169,13 @@ class Menu:
counter = f' {sorting_i + 1}/{len(CONFIG.sorting_options)}' counter = f' {sorting_i + 1}/{len(CONFIG.sorting_options)}'
elif opt == "log_level": elif opt == "log_level":
counter = f' {loglevel_i + 1}/{len(CONFIG.log_levels)}' counter = f' {loglevel_i + 1}/{len(CONFIG.log_levels)}'
elif opt == "view_mode":
counter = f' {view_mode_i + 1}/{len(CONFIG.view_modes)}'
elif opt == "cpu_sensor": elif opt == "cpu_sensor":
counter = f' {cpu_sensor_i + 1}/{len(CONFIG.cpu_sensors)}' counter = f' {cpu_sensor_i + 1}/{len(CONFIG.cpu_sensors)}'
else: else:
counter = "" 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}' 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}'
if opt == selected: if opt == selected:
if isinstance(value, bool) or opt in ["color_theme", "proc_sorting", "log_level", "view_mode", "cpu_sensor"]: if isinstance(value, bool) or opt in ["color_theme", "proc_sorting", "log_level", "cpu_sensor"]:
out += f'{t_color} {Symbol.left}{v_color}{d_quote + str(value) + d_quote:^20.20}{t_color}{Symbol.right} ' out += f'{t_color} {Symbol.left}{v_color}{d_quote + str(value) + d_quote:^20.20}{t_color}{Symbol.right} '
elif inputting: elif inputting:
out += f'{str(input_val)[-17:] + Fx.bl + "" + Fx.ubl + "" + Symbol.enter:^33.33}' out += f'{str(input_val)[-17:] + Fx.bl + "" + Fx.ubl + "" + Symbol.enter:^33.33}'
@ -4183,6 +4254,15 @@ class Menu:
else: else:
CONFIG.tree_depth = int(input_val) CONFIG.tree_depth = int(input_val)
ProcCollector.collapsed = {} ProcCollector.collapsed = {}
elif selected == "shown_boxes":
new_boxes: List = []
for box in input_val.split():
if box in ["cpu", "mem", "net", "proc"]:
new_boxes.append(box)
CONFIG.shown_boxes = " ".join(new_boxes)
Box.view_mode = "user"
Box.view_modes["user"] = CONFIG.shown_boxes.split()
Draw.clear(saved=True)
elif isinstance(getattr(CONFIG, selected), str): elif isinstance(getattr(CONFIG, selected), str):
setattr(CONFIG, selected, input_val) setattr(CONFIG, selected, input_val)
if selected.startswith("net_"): if selected.startswith("net_"):
@ -4205,7 +4285,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", "net_upload", "draw_clock", "tree_depth", "proc_update_mult"]: elif key == "enter" and selected in ["update_ms", "disks_filter", "custom_cpu_name", "net_download", "net_upload", "draw_clock", "tree_depth", "proc_update_mult", "shown_boxes"]:
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:
@ -4282,21 +4362,6 @@ class Menu:
CpuCollector.get_sensors() CpuCollector.get_sensors()
Term.refresh(force=True) Term.refresh(force=True)
cls.resized = False cls.resized = False
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 = CONFIG.view_mode == "proc"
Box.stat_mode = CONFIG.view_mode == "stat"
if ARG_MODE:
ARG_MODE = ""
Draw.clear(saved=True)
Term.refresh(force=True)
cls.resized = False
elif key == "up": elif key == "up":
selected_int -= 1 selected_int -= 1
if selected_int < 0: selected_int = len(option_items) - 1 if selected_int < 0: selected_int = len(option_items) - 1
@ -4527,6 +4592,7 @@ def get_cpu_core_mapping() -> List[int]:
def create_box(x: int = 0, y: int = 0, width: int = 0, height: int = 0, title: str = "", title2: str = "", line_color: Color = None, title_color: Color = None, fill: bool = True, box = None) -> str: def create_box(x: int = 0, y: int = 0, width: int = 0, height: int = 0, title: str = "", title2: str = "", line_color: Color = None, title_color: Color = None, fill: bool = True, box = None) -> str:
'''Create a box from a box object or by given arguments''' '''Create a box from a box object or by given arguments'''
out: str = f'{Term.fg}{Term.bg}' out: str = f'{Term.fg}{Term.bg}'
num: int = 0
if not line_color: line_color = THEME.div_line if not line_color: line_color = THEME.div_line
if not title_color: title_color = THEME.title if not title_color: title_color = THEME.title
@ -4535,8 +4601,9 @@ def create_box(x: int = 0, y: int = 0, width: int = 0, height: int = 0, title: s
x = box.x x = box.x
y = box.y y = box.y
width = box.width width = box.width
height =box.height height = box.height
title = box.name title = box.name
num = box.num
hlines: Tuple[int, int] = (y, y + height - 1) hlines: Tuple[int, int] = (y, y + height - 1)
out += f'{line_color}' out += f'{line_color}'
@ -4557,7 +4624,8 @@ def create_box(x: int = 0, y: int = 0, width: int = 0, height: int = 0, title: s
#* Draw titles if enabled #* Draw titles if enabled
if title: if title:
out += f'{Mv.to(y, x + 2)}{Symbol.title_left}{title_color}{Fx.b}{title}{Fx.ub}{line_color}{Symbol.title_right}' numbered: str = "" if not num else f'{THEME.hi_fg(SUBSCRIPT[num])}'
out += f'{Mv.to(y, x + 2)}{Symbol.title_left}{Fx.b}{numbered}{title_color}{title}{Fx.ub}{line_color}{Symbol.title_right}'
if title2: if title2:
out += f'{Mv.to(hlines[1], x + 2)}{Symbol.title_left}{title_color}{Fx.b}{title2}{Fx.ub}{line_color}{Symbol.title_right}' out += f'{Mv.to(hlines[1], x + 2)}{Symbol.title_left}{title_color}{Fx.b}{title2}{Fx.ub}{line_color}{Symbol.title_right}'
@ -4701,9 +4769,10 @@ def readfile(file: str, default: str = "") -> str:
def process_keys(): def process_keys():
mouse_pos: Tuple[int, int] = (0, 0) mouse_pos: Tuple[int, int] = (0, 0)
filtered: bool = False filtered: bool = False
global ARG_MODE box_keys = {"1" : "cpu", "2" : "mem", "3" : "net", "4" : "proc"}
while Key.has_key(): while Key.has_key():
key = Key.get() key = Key.get()
found: bool = True
if key in ["mouse_scroll_up", "mouse_scroll_down", "mouse_click"]: if key in ["mouse_scroll_up", "mouse_scroll_down", "mouse_click"]:
mouse_pos = Key.get_mouse() mouse_pos = Key.get_mouse()
if mouse_pos[0] >= ProcBox.x and ProcBox.current_y + 1 <= mouse_pos[1] < ProcBox.current_y + ProcBox.current_h - 1: if mouse_pos[0] >= ProcBox.x and ProcBox.current_y + 1 <= mouse_pos[1] < ProcBox.current_y + ProcBox.current_h - 1:
@ -4732,7 +4801,6 @@ def process_keys():
filtered = True filtered = True
continue continue
if key == "_null": if key == "_null":
continue continue
elif key == "q": elif key == "q":
@ -4743,99 +4811,125 @@ def process_keys():
elif key == "-" and CONFIG.update_ms - 100 >= 100: elif key == "-" and CONFIG.update_ms - 100 >= 100:
CONFIG.update_ms -= 100 CONFIG.update_ms -= 100
Box.draw_update_ms() Box.draw_update_ms()
elif key in ["b", "n"]:
NetCollector.switch(key)
elif key in ["M", "escape"]: elif key in ["M", "escape"]:
Menu.main() Menu.main()
elif key in ["o", "f2"]: elif key in ["o", "f2"]:
Menu.options() Menu.options()
elif key in ["h", "f1"]: elif key in ["h", "f1"]:
Menu.help() Menu.help()
elif key == "z":
NetCollector.reset = not NetCollector.reset
Collector.collect(NetCollector, redraw=True)
elif key == "y":
CONFIG.net_sync = not CONFIG.net_sync
Collector.collect(NetCollector, redraw=True)
elif key == "a":
NetCollector.auto_min = not NetCollector.auto_min
NetCollector.net_min = {"download" : -1, "upload" : -1}
Collector.collect(NetCollector, redraw=True)
elif key in ["left", "right"]:
ProcCollector.sorting(key)
elif key == " " and CONFIG.proc_tree and ProcBox.selected > 0:
if ProcBox.selected_pid in ProcCollector.collapsed:
ProcCollector.collapsed[ProcBox.selected_pid] = not ProcCollector.collapsed[ProcBox.selected_pid]
Collector.collect(ProcCollector, interrupt=True, redraw=True)
elif key == "e":
CONFIG.proc_tree = not CONFIG.proc_tree
Collector.collect(ProcCollector, interrupt=True, redraw=True)
elif key == "r":
CONFIG.proc_reversed = not CONFIG.proc_reversed
Collector.collect(ProcCollector, interrupt=True, redraw=True)
elif key == "c":
CONFIG.proc_per_core = not CONFIG.proc_per_core
Collector.collect(ProcCollector, interrupt=True, redraw=True)
elif key == "g":
CONFIG.mem_graphs = not CONFIG.mem_graphs
Collector.collect(MemCollector, interrupt=True, redraw=True)
elif key == "s":
Collector.collect_idle.wait()
CONFIG.swap_disk = not CONFIG.swap_disk
Collector.collect(MemCollector, interrupt=True, redraw=True)
elif key == "f":
ProcBox.filtering = True
if not ProcCollector.search_filter: ProcBox.start = 0
Collector.collect(ProcCollector, redraw=True, only_draw=True)
elif key == "m": elif key == "m":
if ARG_MODE: if list(Box.view_modes).index(Box.view_mode) + 1 > len(list(Box.view_modes)) - 1:
ARG_MODE = "" Box.view_mode = list(Box.view_modes)[0]
elif CONFIG.view_modes.index(CONFIG.view_mode) + 1 > len(CONFIG.view_modes) - 1:
CONFIG.view_mode = CONFIG.view_modes[0]
else: else:
CONFIG.view_mode = CONFIG.view_modes[(CONFIG.view_modes.index(CONFIG.view_mode) + 1)] Box.view_mode = list(Box.view_modes)[(list(Box.view_modes).index(Box.view_mode) + 1)]
Box.proc_mode = CONFIG.view_mode == "proc" CONFIG.shown_boxes = " ".join(Box.view_modes[Box.view_mode])
Box.stat_mode = CONFIG.view_mode == "stat"
Draw.clear(saved=True) Draw.clear(saved=True)
Term.refresh(force=True) Term.refresh(force=True)
elif key.lower() in ["t", "k", "i"] and (ProcBox.selected > 0 or ProcCollector.detailed): elif key in box_keys:
pid: int = ProcBox.selected_pid if ProcBox.selected > 0 else ProcCollector.detailed_pid # type: ignore boxes = CONFIG.shown_boxes.split()
if psutil.pid_exists(pid): if box_keys[key] in boxes:
if key.lower() == "t": sig = signal.SIGTERM boxes.remove(box_keys[key])
elif key.lower() == "k": sig = signal.SIGKILL
elif key.lower() == "i": sig = signal.SIGINT
try:
os.kill(pid, sig)
except Exception as e:
errlog.error(f'Exception when sending signal {sig} to pid {pid}')
errlog.exception(f'{e}')
elif key == "delete" and ProcCollector.search_filter:
ProcCollector.search_filter = ""
Collector.collect(ProcCollector, proc_interrupt=True, redraw=True)
elif key == "enter":
if ProcBox.selected > 0 and ProcCollector.detailed_pid != ProcBox.selected_pid and psutil.pid_exists(ProcBox.selected_pid):
ProcCollector.detailed = True
ProcBox.last_selection = ProcBox.selected
ProcBox.selected = 0
ProcCollector.detailed_pid = ProcBox.selected_pid
ProcBox.resized = True
elif ProcCollector.detailed:
ProcBox.selected = ProcBox.last_selection
ProcBox.last_selection = 0
ProcCollector.detailed = False
ProcCollector.detailed_pid = None
ProcBox.resized = True
else: else:
continue boxes.append(box_keys[key])
ProcCollector.details = {} CONFIG.shown_boxes = " ".join(boxes)
ProcCollector.details_cpu = [] Box.view_mode = "user"
ProcCollector.details_mem = [] Box.view_modes["user"] = CONFIG.shown_boxes.split()
Graphs.detailed_cpu = NotImplemented Draw.clear(saved=True)
Graphs.detailed_mem = NotImplemented Term.refresh(force=True)
Collector.collect(ProcCollector, proc_interrupt=True, redraw=True) else:
found = False
if found: continue
if "proc" in Box.boxes:
if key in ["left", "right"]:
ProcCollector.sorting(key)
elif key == " " and CONFIG.proc_tree and ProcBox.selected > 0:
if ProcBox.selected_pid in ProcCollector.collapsed:
ProcCollector.collapsed[ProcBox.selected_pid] = not ProcCollector.collapsed[ProcBox.selected_pid]
Collector.collect(ProcCollector, interrupt=True, redraw=True)
elif key == "e":
CONFIG.proc_tree = not CONFIG.proc_tree
Collector.collect(ProcCollector, interrupt=True, redraw=True)
elif key == "r":
CONFIG.proc_reversed = not CONFIG.proc_reversed
Collector.collect(ProcCollector, interrupt=True, redraw=True)
elif key == "c":
CONFIG.proc_per_core = not CONFIG.proc_per_core
Collector.collect(ProcCollector, interrupt=True, redraw=True)
elif key == "f":
ProcBox.filtering = True
if not ProcCollector.search_filter: ProcBox.start = 0
Collector.collect(ProcCollector, redraw=True, only_draw=True)
elif key.lower() in ["t", "k", "i"] and (ProcBox.selected > 0 or ProcCollector.detailed):
pid: int = ProcBox.selected_pid if ProcBox.selected > 0 else ProcCollector.detailed_pid # type: ignore
if psutil.pid_exists(pid):
if key.lower() == "t": sig = signal.SIGTERM
elif key.lower() == "k": sig = signal.SIGKILL
elif key.lower() == "i": sig = signal.SIGINT
try:
os.kill(pid, sig)
except Exception as e:
errlog.error(f'Exception when sending signal {sig} to pid {pid}')
errlog.exception(f'{e}')
elif key == "delete" and ProcCollector.search_filter:
ProcCollector.search_filter = ""
Collector.collect(ProcCollector, proc_interrupt=True, redraw=True)
elif key == "enter":
if ProcBox.selected > 0 and ProcCollector.detailed_pid != ProcBox.selected_pid and psutil.pid_exists(ProcBox.selected_pid):
ProcCollector.detailed = True
ProcBox.last_selection = ProcBox.selected
ProcBox.selected = 0
ProcCollector.detailed_pid = ProcBox.selected_pid
ProcBox.resized = True
Collector.proc_counter = 1
elif ProcCollector.detailed:
ProcBox.selected = ProcBox.last_selection
ProcBox.last_selection = 0
ProcCollector.detailed = False
ProcCollector.detailed_pid = None
ProcBox.resized = True
Collector.proc_counter = 1
else:
continue
ProcCollector.details = {}
ProcCollector.details_cpu = []
ProcCollector.details_mem = []
Graphs.detailed_cpu = NotImplemented
Graphs.detailed_mem = NotImplemented
Collector.collect(ProcCollector, proc_interrupt=True, redraw=True)
elif key in ["up", "down", "mouse_scroll_up", "mouse_scroll_down", "page_up", "page_down", "home", "end", "mouse_click", "mouse_unselect"]:
ProcBox.selector(key, mouse_pos)
if "net" in Box.boxes:
if key in ["b", "n"]:
NetCollector.switch(key)
elif key == "z":
NetCollector.reset = not NetCollector.reset
Collector.collect(NetCollector, redraw=True)
elif key == "y":
CONFIG.net_sync = not CONFIG.net_sync
Collector.collect(NetCollector, redraw=True)
elif key == "a":
NetCollector.auto_min = not NetCollector.auto_min
NetCollector.net_min = {"download" : -1, "upload" : -1}
Collector.collect(NetCollector, redraw=True)
if "mem" in Box.boxes:
if key == "g":
CONFIG.mem_graphs = not CONFIG.mem_graphs
Collector.collect(MemCollector, interrupt=True, redraw=True)
elif key == "s":
Collector.collect_idle.wait()
CONFIG.swap_disk = not CONFIG.swap_disk
Collector.collect(MemCollector, interrupt=True, redraw=True)
elif key == "d":
Collector.collect_idle.wait()
CONFIG.show_disks = not CONFIG.show_disks
Collector.collect(MemCollector, interrupt=True, redraw=True)
elif key in ["up", "down", "mouse_scroll_up", "mouse_scroll_down", "page_up", "page_down", "home", "end", "mouse_click", "mouse_unselect"]:
ProcBox.selector(key, mouse_pos)
#? Pre main --------------------------------------------------------------------------------------> #? Pre main -------------------------------------------------------------------------------------->