Module TermTk.libbpytop.term

Expand source code
#!/usr/bin/env python3

# Copyright 2021 Eugenio Parodi <ceccopierangiolieugenio AT googlemail DOT com>
# Copyright 2020 Aristocratos (https://github.com/aristocratos/bpytop)
#
#    Licensed under the Apache License, Version 2.0 (the "License");
#    you may not use this file except in compliance with the License.
#    You may obtain a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS,
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    See the License for the specific language governing permissions and
#    limitations under the License.

import os, sys, io, threading, signal, re, subprocess, logging, logging.handlers, argparse
import queue
from select import select
from time import time, sleep, strftime, localtime
from typing import List, Set, Dict, Tuple, Optional, Union, Any, Callable, ContextManager, Iterable, Type, NamedTuple

try: import fcntl, termios, tty, pwd
except Exception as e:
    print(f'ERROR: {e}')
    exit(1)

class Mv:
    """Class with collection of cursor movement functions: .t[o](line, column) | .r[ight](columns) | .l[eft](columns) | .u[p](lines) | .d[own](lines) | .save() | .restore()"""
    @staticmethod
    def to(line: int, col: int) -> str:
        return f'\033[{line};{col}f' #* Move cursor to line, column
    @staticmethod
    def right(x: int) -> str:        #* Move cursor right x columns
        return f'\033[{x}C'
    @staticmethod
    def left(x: int) -> str:         #* Move cursor left x columns
        return f'\033[{x}D'
    @staticmethod
    def up(x: int) -> str:           #* Move cursor up x lines
        return f'\033[{x}A'
    @staticmethod
    def down(x: int) -> str:         #* Move cursor down x lines
        return f'\033[{x}B'

    save: str = "\033[s"             #* Save cursor position
    restore: str = "\033[u"          #* Restore saved cursor postion
    t = to
    r = right
    l = left
    u = up
    d = down

class Term:
    """Terminal info and commands"""
    title: str = "TermTk"
    mouse: bool = True
    width: int = 0
    height: int = 0
    fg: str = ""                                           #* Default foreground color
    bg: str = ""                                           #* Default background color
    hide_cursor      = "\033[?25l"                         #* Hide terminal cursor
    show_cursor      = "\033[?25h"                         #* Show terminal cursor
    alt_screen       = "\033[?1049h"                       #* Switch to alternate screen
    normal_screen    = "\033[?1049l"                       #* Switch to normal screen
    clear            = "\033[2J\033[0;0f"                  #* Clear screen and set cursor to position 0,0
    mouse_on         = "\033[?1002h\033[?1015h\033[?1006h" #* Enable reporting of mouse position on click and release
    mouse_off        = "\033[?1002l"                       #* Disable mouse reporting
    mouse_direct_on  = "\033[?1003h"                       #* Enable reporting of mouse position at any movement
    mouse_direct_off = "\033[?1003l"                       #* Disable direct mouse reporting

    # from:
    # https://superuser.com/questions/607478/how-do-you-change-the-xterm-cursor-to-an-i-beam-or-vertical-bar
    # echo -e -n "\x1b[\x30 q" # changes to blinking block
    # echo -e -n "\x1b[\x31 q" # changes to blinking block also
    # echo -e -n "\x1b[\x32 q" # changes to steady block
    # echo -e -n "\x1b[\x33 q" # changes to blinking underline
    # echo -e -n "\x1b[\x34 q" # changes to steady underline
    # echo -e -n "\x1b[\x35 q" # changes to blinking bar
    # echo -e -n "\x1b[\x36 q" # changes to steady bar

    cursor_blinking_block      = "\033[\x30 q"
    cursor_blinking_block_also = "\033[\x31 q"
    cursor_steady_block        = "\033[\x32 q"
    cursor_blinking_underline  = "\033[\x33 q"
    cursor_steady_underline    = "\033[\x34 q"
    cursor_blinking_bar        = "\033[\x35 q"
    cursor_steady_bar          = "\033[\x36 q"

    _sigWinChCb = None

    @staticmethod
    def showCursor(cursor):
        Term.push(cursor)
        Term.push(Term.show_cursor)
    @staticmethod
    def hideCursor():
        Term.push(Term.hide_cursor)

    @staticmethod
    def init(mouse: bool = True, title: str = "TermTk"):
        Term.title = title
        Term.mouse = mouse
        Term.push(Term.alt_screen, Term.clear, Term.hide_cursor, Term.escTitle(Term.title))
        if Term.mouse:
            Term.push(Term.mouse_on)
        Term.echo(False)

    @staticmethod
    def stop():
        Term.push(Term.mouse_off, Term.mouse_direct_off)
        Term.push(Term.clear, Term.normal_screen, Term.show_cursor, Term.escTitle())
        Term.echo(True)

    @staticmethod
    def cont():
        Term.push(Term.alt_screen, Term.clear, Term.hide_cursor, Term.escTitle(Term.title))
        if Term.mouse:
            Term.push(Term.mouse_on)
        Term.echo(False)

    @staticmethod
    def exit():
        Term.push(Term.mouse_off, Term.mouse_direct_off)
        Term.push(Term.clear, Term.normal_screen, Term.show_cursor, Term.escTitle())
        Term.echo(True)


    @staticmethod
    def echo(on: bool):
        """Toggle input echo"""
        (iflag, oflag, cflag, lflag, ispeed, ospeed, cc) = termios.tcgetattr(sys.stdin.fileno())
        if on:
            lflag |= termios.ECHO # type: ignore
        else:
            lflag &= ~termios.ECHO # type: ignore
        new_attr = [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]
        termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, new_attr)

    @staticmethod
    def push(*args):
        try:
            print(*args, sep="", end="", flush=True)
        except BlockingIOError:
            pass
            print(*args, sep="", end="", flush=True)

    @staticmethod
    def escTitle(text: str = "") -> str:
        out: str = f'{os.environ.get("TERMINAL_TITLE", "")}'
        if out and text: out += " "
        if text: out += f'{text}'
        return f'\033]0;{out}\a'

    @staticmethod
    def _sigWinCh(signum, frame):
        Term.width, Term.height = os.get_terminal_size()
        if Term._sigWinChCb is not None:
            Term._sigWinChCb(Term.width, Term.height)

    @staticmethod
    def registerResizeCb(callback):
        Term._sigWinChCb = callback
        # Dummy call to retrieve the terminal size
        Term._sigWinCh(signal.SIGWINCH, None)
        signal.signal(signal.SIGWINCH, Term._sigWinCh)

Classes

class Mv

Class with collection of cursor movement functions: .to | .right | .left | .up | .down | .save() | .restore()

Expand source code
class Mv:
    """Class with collection of cursor movement functions: .t[o](line, column) | .r[ight](columns) | .l[eft](columns) | .u[p](lines) | .d[own](lines) | .save() | .restore()"""
    @staticmethod
    def to(line: int, col: int) -> str:
        return f'\033[{line};{col}f' #* Move cursor to line, column
    @staticmethod
    def right(x: int) -> str:        #* Move cursor right x columns
        return f'\033[{x}C'
    @staticmethod
    def left(x: int) -> str:         #* Move cursor left x columns
        return f'\033[{x}D'
    @staticmethod
    def up(x: int) -> str:           #* Move cursor up x lines
        return f'\033[{x}A'
    @staticmethod
    def down(x: int) -> str:         #* Move cursor down x lines
        return f'\033[{x}B'

    save: str = "\033[s"             #* Save cursor position
    restore: str = "\033[u"          #* Restore saved cursor postion
    t = to
    r = right
    l = left
    u = up
    d = down

Subclasses

Class variables

var restore : str
var save : str

Static methods

def d(x: int) ‑> str
Expand source code
@staticmethod
def down(x: int) -> str:         #* Move cursor down x lines
    return f'\033[{x}B'
def down(x: int) ‑> str
Expand source code
@staticmethod
def down(x: int) -> str:         #* Move cursor down x lines
    return f'\033[{x}B'
def l(x: int) ‑> str
Expand source code
@staticmethod
def left(x: int) -> str:         #* Move cursor left x columns
    return f'\033[{x}D'
def left(x: int) ‑> str
Expand source code
@staticmethod
def left(x: int) -> str:         #* Move cursor left x columns
    return f'\033[{x}D'
def r(x: int) ‑> str
Expand source code
@staticmethod
def right(x: int) -> str:        #* Move cursor right x columns
    return f'\033[{x}C'
def right(x: int) ‑> str
Expand source code
@staticmethod
def right(x: int) -> str:        #* Move cursor right x columns
    return f'\033[{x}C'
def t(line: int, col: int) ‑> str
Expand source code
@staticmethod
def to(line: int, col: int) -> str:
    return f'\033[{line};{col}f' #* Move cursor to line, column
def to(line: int, col: int) ‑> str
Expand source code
@staticmethod
def to(line: int, col: int) -> str:
    return f'\033[{line};{col}f' #* Move cursor to line, column
def u(x: int) ‑> str
Expand source code
@staticmethod
def up(x: int) -> str:           #* Move cursor up x lines
    return f'\033[{x}A'
def up(x: int) ‑> str
Expand source code
@staticmethod
def up(x: int) -> str:           #* Move cursor up x lines
    return f'\033[{x}A'
class Term

Terminal info and commands

Expand source code
class Term:
    """Terminal info and commands"""
    title: str = "TermTk"
    mouse: bool = True
    width: int = 0
    height: int = 0
    fg: str = ""                                           #* Default foreground color
    bg: str = ""                                           #* Default background color
    hide_cursor      = "\033[?25l"                         #* Hide terminal cursor
    show_cursor      = "\033[?25h"                         #* Show terminal cursor
    alt_screen       = "\033[?1049h"                       #* Switch to alternate screen
    normal_screen    = "\033[?1049l"                       #* Switch to normal screen
    clear            = "\033[2J\033[0;0f"                  #* Clear screen and set cursor to position 0,0
    mouse_on         = "\033[?1002h\033[?1015h\033[?1006h" #* Enable reporting of mouse position on click and release
    mouse_off        = "\033[?1002l"                       #* Disable mouse reporting
    mouse_direct_on  = "\033[?1003h"                       #* Enable reporting of mouse position at any movement
    mouse_direct_off = "\033[?1003l"                       #* Disable direct mouse reporting

    # from:
    # https://superuser.com/questions/607478/how-do-you-change-the-xterm-cursor-to-an-i-beam-or-vertical-bar
    # echo -e -n "\x1b[\x30 q" # changes to blinking block
    # echo -e -n "\x1b[\x31 q" # changes to blinking block also
    # echo -e -n "\x1b[\x32 q" # changes to steady block
    # echo -e -n "\x1b[\x33 q" # changes to blinking underline
    # echo -e -n "\x1b[\x34 q" # changes to steady underline
    # echo -e -n "\x1b[\x35 q" # changes to blinking bar
    # echo -e -n "\x1b[\x36 q" # changes to steady bar

    cursor_blinking_block      = "\033[\x30 q"
    cursor_blinking_block_also = "\033[\x31 q"
    cursor_steady_block        = "\033[\x32 q"
    cursor_blinking_underline  = "\033[\x33 q"
    cursor_steady_underline    = "\033[\x34 q"
    cursor_blinking_bar        = "\033[\x35 q"
    cursor_steady_bar          = "\033[\x36 q"

    _sigWinChCb = None

    @staticmethod
    def showCursor(cursor):
        Term.push(cursor)
        Term.push(Term.show_cursor)
    @staticmethod
    def hideCursor():
        Term.push(Term.hide_cursor)

    @staticmethod
    def init(mouse: bool = True, title: str = "TermTk"):
        Term.title = title
        Term.mouse = mouse
        Term.push(Term.alt_screen, Term.clear, Term.hide_cursor, Term.escTitle(Term.title))
        if Term.mouse:
            Term.push(Term.mouse_on)
        Term.echo(False)

    @staticmethod
    def stop():
        Term.push(Term.mouse_off, Term.mouse_direct_off)
        Term.push(Term.clear, Term.normal_screen, Term.show_cursor, Term.escTitle())
        Term.echo(True)

    @staticmethod
    def cont():
        Term.push(Term.alt_screen, Term.clear, Term.hide_cursor, Term.escTitle(Term.title))
        if Term.mouse:
            Term.push(Term.mouse_on)
        Term.echo(False)

    @staticmethod
    def exit():
        Term.push(Term.mouse_off, Term.mouse_direct_off)
        Term.push(Term.clear, Term.normal_screen, Term.show_cursor, Term.escTitle())
        Term.echo(True)


    @staticmethod
    def echo(on: bool):
        """Toggle input echo"""
        (iflag, oflag, cflag, lflag, ispeed, ospeed, cc) = termios.tcgetattr(sys.stdin.fileno())
        if on:
            lflag |= termios.ECHO # type: ignore
        else:
            lflag &= ~termios.ECHO # type: ignore
        new_attr = [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]
        termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, new_attr)

    @staticmethod
    def push(*args):
        try:
            print(*args, sep="", end="", flush=True)
        except BlockingIOError:
            pass
            print(*args, sep="", end="", flush=True)

    @staticmethod
    def escTitle(text: str = "") -> str:
        out: str = f'{os.environ.get("TERMINAL_TITLE", "")}'
        if out and text: out += " "
        if text: out += f'{text}'
        return f'\033]0;{out}\a'

    @staticmethod
    def _sigWinCh(signum, frame):
        Term.width, Term.height = os.get_terminal_size()
        if Term._sigWinChCb is not None:
            Term._sigWinChCb(Term.width, Term.height)

    @staticmethod
    def registerResizeCb(callback):
        Term._sigWinChCb = callback
        # Dummy call to retrieve the terminal size
        Term._sigWinCh(signal.SIGWINCH, None)
        signal.signal(signal.SIGWINCH, Term._sigWinCh)

Class variables

var alt_screen
var bg : str
var clear
var cursor_blinking_bar
var cursor_blinking_block
var cursor_blinking_block_also
var cursor_blinking_underline
var cursor_steady_bar
var cursor_steady_block
var cursor_steady_underline
var fg : str
var height : int
var hide_cursor
var mouse : bool
var mouse_direct_off
var mouse_direct_on
var mouse_off
var mouse_on
var normal_screen
var show_cursor
var title : str
var width : int

Static methods

def cont()
Expand source code
@staticmethod
def cont():
    Term.push(Term.alt_screen, Term.clear, Term.hide_cursor, Term.escTitle(Term.title))
    if Term.mouse:
        Term.push(Term.mouse_on)
    Term.echo(False)
def echo(on: bool)

Toggle input echo

Expand source code
@staticmethod
def echo(on: bool):
    """Toggle input echo"""
    (iflag, oflag, cflag, lflag, ispeed, ospeed, cc) = termios.tcgetattr(sys.stdin.fileno())
    if on:
        lflag |= termios.ECHO # type: ignore
    else:
        lflag &= ~termios.ECHO # type: ignore
    new_attr = [iflag, oflag, cflag, lflag, ispeed, ospeed, cc]
    termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, new_attr)
def escTitle(text: str = '') ‑> str
Expand source code
@staticmethod
def escTitle(text: str = "") -> str:
    out: str = f'{os.environ.get("TERMINAL_TITLE", "")}'
    if out and text: out += " "
    if text: out += f'{text}'
    return f'\033]0;{out}\a'
def exit()
Expand source code
@staticmethod
def exit():
    Term.push(Term.mouse_off, Term.mouse_direct_off)
    Term.push(Term.clear, Term.normal_screen, Term.show_cursor, Term.escTitle())
    Term.echo(True)
def hideCursor()
Expand source code
@staticmethod
def hideCursor():
    Term.push(Term.hide_cursor)
def init(mouse: bool = True, title: str = 'TermTk')
Expand source code
@staticmethod
def init(mouse: bool = True, title: str = "TermTk"):
    Term.title = title
    Term.mouse = mouse
    Term.push(Term.alt_screen, Term.clear, Term.hide_cursor, Term.escTitle(Term.title))
    if Term.mouse:
        Term.push(Term.mouse_on)
    Term.echo(False)
def push(*args)
Expand source code
@staticmethod
def push(*args):
    try:
        print(*args, sep="", end="", flush=True)
    except BlockingIOError:
        pass
        print(*args, sep="", end="", flush=True)
def registerResizeCb(callback)
Expand source code
@staticmethod
def registerResizeCb(callback):
    Term._sigWinChCb = callback
    # Dummy call to retrieve the terminal size
    Term._sigWinCh(signal.SIGWINCH, None)
    signal.signal(signal.SIGWINCH, Term._sigWinCh)
def showCursor(cursor)
Expand source code
@staticmethod
def showCursor(cursor):
    Term.push(cursor)
    Term.push(Term.show_cursor)
def stop()
Expand source code
@staticmethod
def stop():
    Term.push(Term.mouse_off, Term.mouse_direct_off)
    Term.push(Term.clear, Term.normal_screen, Term.show_cursor, Term.escTitle())
    Term.echo(True)