diff --git a/.gitignore b/.gitignore index 04ca910..9a91afd 100644 --- a/.gitignore +++ b/.gitignore @@ -17,5 +17,6 @@ syntax: glob .tox/ build/ dist/ +__pycache__ .mypy_cache .vscode \ No newline at end of file diff --git a/bpytop-themes b/bpytop-themes new file mode 120000 index 0000000..bfa4097 --- /dev/null +++ b/bpytop-themes @@ -0,0 +1 @@ +themes \ No newline at end of file diff --git a/bpytop.py b/bpytop.py index d000f3a..89b6860 100755 --- a/bpytop.py +++ b/bpytop.py @@ -17,7 +17,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -import os, sys, threading, signal, re, subprocess, logging, logging.handlers +import os, sys, threading, signal, re, subprocess, logging, logging.handlers, site import urllib.request from time import time, sleep, strftime, localtime from datetime import timedelta @@ -186,10 +186,15 @@ if not os.path.isdir(CONFIG_DIR): raise SystemExit(1) CONFIG_FILE: str = f'{CONFIG_DIR}/bpytop.conf' THEME_DIR: str = "" -for td in ["local/", ""]: - if os.path.isdir(f'/usr/{td}share/bpytop/themes'): - THEME_DIR = f'/usr/{td}share/bpytop/themes' +for td in site.getsitepackages() + [site.getusersitepackages()]: + if os.path.isdir(f'{td}/bpytop-themes'): + THEME_DIR = f'{td}/bpytop-themes' break +else: + for td in ["local/", ""]: + if os.path.isdir(f'/usr/{td}share/bpytop/themes'): + THEME_DIR = f'/usr/{td}share/bpytop/themes' + break USER_THEME_DIR: str = f'{CONFIG_DIR}/themes' CORES: int = psutil.cpu_count(logical=False) or 1 @@ -3819,6 +3824,9 @@ class Menu: setattr(CONFIG, selected, input_val) if selected.startswith("net_"): NetCollector.net_min = {"download" : -1, "upload" : -1} + elif selected == "draw_clock": + Box.clock_on = True if len(CONFIG.draw_clock) > 0 else False + if not Box.clock_on: Draw.clear("clock", saved=True) Term.refresh(force=True) cls.resized = False elif key == "backspace" and len(input_val) > 0: @@ -3974,6 +3982,68 @@ class UpdateChecker: except Exception as e: errlog.exception(f'{e}') +class Init: + running: bool = True + initbg_colors: List[str] = [] + initbg_data: List[int] + initbg_up: Graph + initbg_down: Graph + resized = False + + @classmethod + def start(cls): + Draw.buffer("init", z=1) + Draw.buffer("initbg", z=10) + for i in range(51): + for _ in range(2): cls.initbg_colors.append(Color.fg(i, i, i)) + Draw.buffer("banner", (f'{Banner.draw(Term.height // 2 - 10, center=True)}{Mv.d(1)}{Mv.l(11)}{Colors.black_bg}{Colors.default}' + f'{Fx.b}{Fx.i}Version: {VERSION}{Fx.ui}{Fx.ub}{Term.bg}{Term.fg}{Color.fg("#50")}'), z=2) + for _i in range(7): + perc = f'{str(round((_i + 1) * 14 + 2)) + "%":>5}' + Draw.buffer("+banner", f'{Mv.to(Term.height // 2 - 2 + _i, Term.width // 2 - 28)}{Fx.trans(perc)}{Symbol.v_line}') + + Draw.out("banner") + Draw.buffer("+init!", f'{Color.fg("#cc")}{Fx.b}{Mv.to(Term.height // 2 - 2, Term.width // 2 - 21)}{Mv.save}') + + cls.initbg_data = [randint(0, 100) for _ in range(Term.width * 2)] + cls.initbg_up = Graph(Term.width, Term.height // 2, cls.initbg_colors, cls.initbg_data, invert=True) + cls.initbg_down = Graph(Term.width, Term.height // 2, cls.initbg_colors, cls.initbg_data, invert=False) + + @classmethod + def success(cls): + if not CONFIG.show_init or cls.resized: return + cls.draw_bg(5) + Draw.buffer("+init!", f'{Mv.restore}{Symbol.ok}\n{Mv.r(Term.width // 2 - 22)}{Mv.save}') + + @staticmethod + def fail(err): + if CONFIG.show_init: + Draw.buffer("+init!", f'{Mv.restore}{Symbol.fail}') + sleep(2) + errlog.exception(f'{err}') + clean_quit(1, errmsg=f'Error during init! See {CONFIG_DIR}/error.log for more information.') + + @classmethod + def draw_bg(cls, times: int = 5): + for _ in range(times): + sleep(0.05) + x = randint(0, 100) + Draw.buffer("initbg", f'{Fx.ub}{Mv.to(0, 0)}{cls.initbg_up(x)}{Mv.to(Term.height // 2, 0)}{cls.initbg_down(x)}') + Draw.out("initbg", "banner", "init") + + @classmethod + def done(cls): + cls.running = False + if not CONFIG.show_init: return + if cls.resized: + Draw.now(Term.clear) + else: + cls.draw_bg(10) + Draw.clear("initbg", "banner", "init", saved=True) + if cls.resized: return + del cls.initbg_up, cls.initbg_down, cls.initbg_data, cls.initbg_colors + + #? Functions -------------------------------------------------------------------------------------> def get_cpu_name() -> str: @@ -4313,90 +4383,31 @@ def process_keys(): CPU_NAME: str = get_cpu_name() +THEME: Theme -if __name__ == "__main__": +def main(): + global THEME #? Init --------------------------------------------------------------------------------------> if DEBUG: TimeIt.start("Init") - class Init: - running: bool = True - initbg_colors: List[str] = [] - initbg_data: List[int] - initbg_up: Graph - initbg_down: Graph - resized = False - - @staticmethod - def fail(err): - if CONFIG.show_init: - Draw.buffer("+init!", f'{Mv.restore}{Symbol.fail}') - sleep(2) - errlog.exception(f'{err}') - clean_quit(1, errmsg=f'Error during init! See {CONFIG_DIR}/error.log for more information.') - - @classmethod - def success(cls, start: bool = False): - if not CONFIG.show_init or cls.resized: return - if start: - Draw.buffer("init", z=1) - Draw.buffer("initbg", z=10) - for i in range(51): - for _ in range(2): cls.initbg_colors.append(Color.fg(i, i, i)) - Draw.buffer("banner", (f'{Banner.draw(Term.height // 2 - 10, center=True)}{Mv.d(1)}{Mv.l(11)}{Colors.black_bg}{Colors.default}' - f'{Fx.b}{Fx.i}Version: {VERSION}{Fx.ui}{Fx.ub}{Term.bg}{Term.fg}{Color.fg("#50")}'), z=2) - for _i in range(7): - perc = f'{str(round((_i + 1) * 14 + 2)) + "%":>5}' - Draw.buffer("+banner", f'{Mv.to(Term.height // 2 - 2 + _i, Term.width // 2 - 28)}{Fx.trans(perc)}{Symbol.v_line}') - - Draw.out("banner") - Draw.buffer("+init!", f'{Color.fg("#cc")}{Fx.b}{Mv.to(Term.height // 2 - 2, Term.width // 2 - 21)}{Mv.save}') - - cls.initbg_data = [randint(0, 100) for _ in range(Term.width * 2)] - cls.initbg_up = Graph(Term.width, Term.height // 2, cls.initbg_colors, cls.initbg_data, invert=True) - cls.initbg_down = Graph(Term.width, Term.height // 2, cls.initbg_colors, cls.initbg_data, invert=False) - - if start: return - - cls.draw_bg(5) - Draw.buffer("+init!", f'{Mv.restore}{Symbol.ok}\n{Mv.r(Term.width // 2 - 22)}{Mv.save}') - - @classmethod - def draw_bg(cls, times: int = 5): - for _ in range(times): - sleep(0.05) - x = randint(0, 100) - Draw.buffer("initbg", f'{Fx.ub}{Mv.to(0, 0)}{cls.initbg_up(x)}{Mv.to(Term.height // 2, 0)}{cls.initbg_down(x)}') - Draw.out("initbg", "banner", "init") - - @classmethod - def done(cls): - cls.running = False - if not CONFIG.show_init: return - if cls.resized: - Draw.now(Term.clear) - else: - cls.draw_bg(10) - Draw.clear("initbg", "banner", "init", saved=True) - if cls.resized: return - del cls.initbg_up, cls.initbg_down, cls.initbg_data, cls.initbg_colors - - #? Switch to alternate screen, clear screen, hide cursor, enable mouse reporting and disable input echo Draw.now(Term.alt_screen, Term.clear, Term.hide_cursor, Term.mouse_on, Term.title("BpyTOP")) Term.echo(False) Term.refresh(force=True) + + #? Start a thread checking for updates while running init if CONFIG.update_check: UpdateChecker.run() #? Draw banner and init status - if CONFIG.show_init: - Init.success(start=True) + if CONFIG.show_init and not Init.resized: + Init.start() #? Load theme if CONFIG.show_init: Draw.buffer("+init!", f'{Mv.restore}{Fx.trans("Loading theme and creating colors... ")}{Mv.save}') try: - THEME: Theme = Theme(CONFIG.color_theme) + THEME = Theme(CONFIG.color_theme) except Exception as e: Init.fail(e) else: @@ -4468,7 +4479,6 @@ if __name__ == "__main__": else: Init.success() - Init.done() Term.refresh() Draw.out(clear=True) @@ -4478,7 +4488,7 @@ if __name__ == "__main__": #? Main loop -------------------------------------------------------------------------------------> - def main(): + def run(): while not False: Term.refresh() Timer.stamp() @@ -4491,10 +4501,14 @@ if __name__ == "__main__": #? Start main loop try: - main() + run() except Exception as e: errlog.exception(f'{e}') clean_quit(1) else: #? Quit cleanly even if false starts being true... clean_quit() + + +if __name__ == "__main__": + main() diff --git a/poetry.lock b/poetry.lock new file mode 100644 index 0000000..d4735df --- /dev/null +++ b/poetry.lock @@ -0,0 +1,30 @@ +[[package]] +category = "main" +description = "Cross-platform lib for process and system monitoring in Python." +name = "psutil" +optional = false +python-versions = ">=2.6, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +version = "5.7.2" + +[package.extras] +test = ["ipaddress", "mock", "unittest2", "enum34", "pywin32", "wmi"] + +[metadata] +content-hash = "258639373f0e151ea9f8efb81c3e413c2c8886631570411344520b408e2eb8ab" +lock-version = "1.0" +python-versions = "^3.6" + +[metadata.files] +psutil = [ + {file = "psutil-5.7.2-cp27-none-win32.whl", hash = "sha256:f2018461733b23f308c298653c8903d32aaad7873d25e1d228765e91ae42c3f2"}, + {file = "psutil-5.7.2-cp27-none-win_amd64.whl", hash = "sha256:66c18ca7680a31bf16ee22b1d21b6397869dda8059dbdb57d9f27efa6615f195"}, + {file = "psutil-5.7.2-cp35-cp35m-win32.whl", hash = "sha256:5e9d0f26d4194479a13d5f4b3798260c20cecf9ac9a461e718eb59ea520a360c"}, + {file = "psutil-5.7.2-cp35-cp35m-win_amd64.whl", hash = "sha256:4080869ed93cce662905b029a1770fe89c98787e543fa7347f075ade761b19d6"}, + {file = "psutil-5.7.2-cp36-cp36m-win32.whl", hash = "sha256:d8a82162f23c53b8525cf5f14a355f5d1eea86fa8edde27287dd3a98399e4fdf"}, + {file = "psutil-5.7.2-cp36-cp36m-win_amd64.whl", hash = "sha256:0ee3c36428f160d2d8fce3c583a0353e848abb7de9732c50cf3356dd49ad63f8"}, + {file = "psutil-5.7.2-cp37-cp37m-win32.whl", hash = "sha256:ff1977ba1a5f71f89166d5145c3da1cea89a0fdb044075a12c720ee9123ec818"}, + {file = "psutil-5.7.2-cp37-cp37m-win_amd64.whl", hash = "sha256:a5b120bb3c0c71dfe27551f9da2f3209a8257a178ed6c628a819037a8df487f1"}, + {file = "psutil-5.7.2-cp38-cp38-win32.whl", hash = "sha256:10512b46c95b02842c225f58fa00385c08fa00c68bac7da2d9a58ebe2c517498"}, + {file = "psutil-5.7.2-cp38-cp38-win_amd64.whl", hash = "sha256:68d36986ded5dac7c2dcd42f2682af1db80d4bce3faa126a6145c1637e1b559f"}, + {file = "psutil-5.7.2.tar.gz", hash = "sha256:90990af1c3c67195c44c9a889184f84f5b2320dce3ee3acbd054e3ba0b4a7beb"}, +] diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..a395b03 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,20 @@ +[tool.poetry] +name = "bpytop" +version = "1.0.16" +description = "Resource monitor that shows usage and stats for processor, memory, disks, network and processes." +authors = ["Aristocratos "] +license = "Apache-2.0" +include = ["bpytop-themes/*.theme"] + +[tool.poetry.dependencies] +python = "^3.6" +psutil = "^5.7.0" + +[tool.poetry.dev-dependencies] + +[tool.poetry.scripts] +bpytop = "bpytop:main" + +[build-system] +requires = ["poetry>=0.12"] +build-backend = "poetry.masonry.api"