printbuddies.printbuddies

  1from os import get_terminal_size
  2from time import sleep
  3from typing import Any
  4
  5from noiftimer import Timer
  6
  7
  8def clear():
  9    """Erase the current line from the terminal."""
 10    print(" " * (get_terminal_size().columns - 1), flush=True, end="\r")
 11
 12
 13def print_in_place(string: str, animate: bool = False, animate_refresh: float = 0.01):
 14    """Calls to print_in_place will overwrite
 15    the previous line of text in the terminal
 16    with the 'string' param.
 17
 18    :param animate: Will cause the string
 19    to be printed to the terminal
 20    one character at a time.
 21
 22    :param animate_refresh: Number of seconds
 23    between the addition of characters
 24    when 'animate' is True."""
 25    clear()
 26    string = str(string)
 27    width = get_terminal_size().columns
 28    string = string[: width - 2]
 29    if animate:
 30        for i in range(len(string)):
 31            print(f"{string[:i+1]}", flush=True, end=" \r")
 32            sleep(animate_refresh)
 33    else:
 34        print(string, flush=True, end="\r")
 35
 36
 37def ticker(info: list[str]):
 38    """Prints info to terminal with
 39    top and bottom padding so that repeated
 40    calls print info without showing previous
 41    outputs from ticker calls.
 42
 43    Similar visually to print_in_place,
 44    but for multiple lines."""
 45    width = get_terminal_size().columns
 46    info = [str(line)[: width - 1] for line in info]
 47    height = get_terminal_size().lines - len(info)
 48    print("\n" * (height * 2), end="")
 49    print(*info, sep="\n", end="")
 50    print("\n" * (int((height) / 2)), end="")
 51
 52
 53class ProgBar:
 54    """Self incrementing, dynamically sized progress bar.
 55
 56    Includes a Timer object from noiftimer that starts timing
 57    on the first call to display and stops timing once
 58    self.counter >= self.total.
 59    It can be easily added to the progress bar display by calling
 60    Timer's checkTime function and passing the value to the 'prefix' or 'suffix'
 61    param of self.display():
 62
 63    bar = ProgBar(total=someTotal)
 64    bar.display(prefix=f"Run time: {bar.timer.checkTime()}")"""
 65
 66    def __init__(
 67        self,
 68        total: float,
 69        fill_ch: str = "_",
 70        unfill_ch: str = "/",
 71        width_ratio: float = 0.75,
 72        new_line_after_completion: bool = True,
 73        clear_after_completion: bool = False,
 74    ):
 75        """:param total: The number of calls to reach 100% completion.
 76
 77        :param fill_ch: The character used to represent the completed part of the bar.
 78
 79        :param unfill_ch: The character used to represent the uncompleted part of the bar.
 80
 81        :param width_ratio: The width of the progress bar relative to the width of the terminal window.
 82
 83        :param new_line_after_completion: Make a call to print() once self.counter >= self.total.
 84
 85        :param clear_after_completion: Make a call to printbuddies.clear() once self.counter >= self.total.
 86
 87        Note: if new_line_after_completion and clear_after_completion are both True, the line will be cleared
 88        then a call to print() will be made."""
 89        self.total = total
 90        self.fill_ch = fill_ch[0]
 91        self.unfill_ch = unfill_ch[0]
 92        self.width_ratio = width_ratio
 93        self.new_line_after_completion = new_line_after_completion
 94        self.clear_after_completion = clear_after_completion
 95        self.timer = Timer()
 96        self.reset()
 97
 98    def reset(self):
 99        self.counter = 0
100        self.percent = ""
101        self.prefix = ""
102        self.suffix = ""
103        self.filled = ""
104        self.unfilled = ""
105        self.bar = ""
106
107    def get_percent(self) -> str:
108        """Returns the percentage complete to two decimal places
109        as a string without the %."""
110        percent = str(round(100.0 * self.counter / self.total, 2))
111        if len(percent.split(".")[1]) == 1:
112            percent = percent + "0"
113        if len(percent.split(".")[0]) == 1:
114            percent = "0" + percent
115        return percent
116
117    def _prepare_bar(self):
118        self.terminal_width = get_terminal_size().columns - 1
119        bar_length = int(self.terminal_width * self.width_ratio)
120        progress = int(bar_length * self.counter / self.total)
121        self.filled = self.fill_ch * progress
122        self.unfilled = self.unfill_ch * (bar_length - progress)
123        self.percent = self.get_percent()
124        self.bar = self.get_bar()
125
126    def _trim_bar(self):
127        original_width = self.width_ratio
128        while len(self.bar) > self.terminal_width and self.width_ratio > 0:
129            self.width_ratio -= 0.01
130            self._prepare_bar()
131        self.width_ratio = original_width
132
133    def get_bar(self):
134        return f"{self.prefix} [{self.filled}{self.unfilled}]-{self.percent}% {self.suffix}"
135
136    def display(
137        self,
138        prefix: str = "",
139        suffix: str = "",
140        counter_override: float = None,
141        total_override: float = None,
142        return_object: Any = None,
143    ) -> Any:
144        """Writes the progress bar to the terminal.
145
146        :param prefix: String affixed to the front of the progress bar.
147
148        :param suffix: String appended to the end of the progress bar.
149
150        :param counter_override: When an externally incremented completion counter is needed.
151
152        :param total_override: When an externally controlled bar total is needed.
153
154        :param return_object: An object to be returned by display().
155
156        Allows display() to be called within a comprehension:
157
158        e.g.
159
160        bar = ProgBar(9)
161
162        myList = [bar.display(return_object=i) for i in range(10)]"""
163        if not self.timer.started:
164            self.timer.start()
165        if counter_override is not None:
166            self.counter = counter_override
167        if total_override:
168            self.total = total_override
169        # Don't wanna divide by 0 there, pal
170        while self.total <= 0:
171            self.total += 1
172        self.prefix = prefix
173        self.suffix = suffix
174        self._prepare_bar()
175        self._trim_bar()
176        pad = " " * (self.terminal_width - len(self.bar))
177        width = get_terminal_size().columns
178        print(f"{self.bar}{pad}"[: width - 2], flush=True, end="\r")
179        if self.counter >= self.total:
180            self.timer.stop()
181            if self.clear_after_completion:
182                clear()
183            if self.new_line_after_completion:
184                print()
185        self.counter += 1
186        return return_object
def clear():
 9def clear():
10    """Erase the current line from the terminal."""
11    print(" " * (get_terminal_size().columns - 1), flush=True, end="\r")

Erase the current line from the terminal.

def ticker(info: list[str]):
38def ticker(info: list[str]):
39    """Prints info to terminal with
40    top and bottom padding so that repeated
41    calls print info without showing previous
42    outputs from ticker calls.
43
44    Similar visually to print_in_place,
45    but for multiple lines."""
46    width = get_terminal_size().columns
47    info = [str(line)[: width - 1] for line in info]
48    height = get_terminal_size().lines - len(info)
49    print("\n" * (height * 2), end="")
50    print(*info, sep="\n", end="")
51    print("\n" * (int((height) / 2)), end="")

Prints info to terminal with top and bottom padding so that repeated calls print info without showing previous outputs from ticker calls.

Similar visually to print_in_place, but for multiple lines.

class ProgBar:
 54class ProgBar:
 55    """Self incrementing, dynamically sized progress bar.
 56
 57    Includes a Timer object from noiftimer that starts timing
 58    on the first call to display and stops timing once
 59    self.counter >= self.total.
 60    It can be easily added to the progress bar display by calling
 61    Timer's checkTime function and passing the value to the 'prefix' or 'suffix'
 62    param of self.display():
 63
 64    bar = ProgBar(total=someTotal)
 65    bar.display(prefix=f"Run time: {bar.timer.checkTime()}")"""
 66
 67    def __init__(
 68        self,
 69        total: float,
 70        fill_ch: str = "_",
 71        unfill_ch: str = "/",
 72        width_ratio: float = 0.75,
 73        new_line_after_completion: bool = True,
 74        clear_after_completion: bool = False,
 75    ):
 76        """:param total: The number of calls to reach 100% completion.
 77
 78        :param fill_ch: The character used to represent the completed part of the bar.
 79
 80        :param unfill_ch: The character used to represent the uncompleted part of the bar.
 81
 82        :param width_ratio: The width of the progress bar relative to the width of the terminal window.
 83
 84        :param new_line_after_completion: Make a call to print() once self.counter >= self.total.
 85
 86        :param clear_after_completion: Make a call to printbuddies.clear() once self.counter >= self.total.
 87
 88        Note: if new_line_after_completion and clear_after_completion are both True, the line will be cleared
 89        then a call to print() will be made."""
 90        self.total = total
 91        self.fill_ch = fill_ch[0]
 92        self.unfill_ch = unfill_ch[0]
 93        self.width_ratio = width_ratio
 94        self.new_line_after_completion = new_line_after_completion
 95        self.clear_after_completion = clear_after_completion
 96        self.timer = Timer()
 97        self.reset()
 98
 99    def reset(self):
100        self.counter = 0
101        self.percent = ""
102        self.prefix = ""
103        self.suffix = ""
104        self.filled = ""
105        self.unfilled = ""
106        self.bar = ""
107
108    def get_percent(self) -> str:
109        """Returns the percentage complete to two decimal places
110        as a string without the %."""
111        percent = str(round(100.0 * self.counter / self.total, 2))
112        if len(percent.split(".")[1]) == 1:
113            percent = percent + "0"
114        if len(percent.split(".")[0]) == 1:
115            percent = "0" + percent
116        return percent
117
118    def _prepare_bar(self):
119        self.terminal_width = get_terminal_size().columns - 1
120        bar_length = int(self.terminal_width * self.width_ratio)
121        progress = int(bar_length * self.counter / self.total)
122        self.filled = self.fill_ch * progress
123        self.unfilled = self.unfill_ch * (bar_length - progress)
124        self.percent = self.get_percent()
125        self.bar = self.get_bar()
126
127    def _trim_bar(self):
128        original_width = self.width_ratio
129        while len(self.bar) > self.terminal_width and self.width_ratio > 0:
130            self.width_ratio -= 0.01
131            self._prepare_bar()
132        self.width_ratio = original_width
133
134    def get_bar(self):
135        return f"{self.prefix} [{self.filled}{self.unfilled}]-{self.percent}% {self.suffix}"
136
137    def display(
138        self,
139        prefix: str = "",
140        suffix: str = "",
141        counter_override: float = None,
142        total_override: float = None,
143        return_object: Any = None,
144    ) -> Any:
145        """Writes the progress bar to the terminal.
146
147        :param prefix: String affixed to the front of the progress bar.
148
149        :param suffix: String appended to the end of the progress bar.
150
151        :param counter_override: When an externally incremented completion counter is needed.
152
153        :param total_override: When an externally controlled bar total is needed.
154
155        :param return_object: An object to be returned by display().
156
157        Allows display() to be called within a comprehension:
158
159        e.g.
160
161        bar = ProgBar(9)
162
163        myList = [bar.display(return_object=i) for i in range(10)]"""
164        if not self.timer.started:
165            self.timer.start()
166        if counter_override is not None:
167            self.counter = counter_override
168        if total_override:
169            self.total = total_override
170        # Don't wanna divide by 0 there, pal
171        while self.total <= 0:
172            self.total += 1
173        self.prefix = prefix
174        self.suffix = suffix
175        self._prepare_bar()
176        self._trim_bar()
177        pad = " " * (self.terminal_width - len(self.bar))
178        width = get_terminal_size().columns
179        print(f"{self.bar}{pad}"[: width - 2], flush=True, end="\r")
180        if self.counter >= self.total:
181            self.timer.stop()
182            if self.clear_after_completion:
183                clear()
184            if self.new_line_after_completion:
185                print()
186        self.counter += 1
187        return return_object

Self incrementing, dynamically sized progress bar.

Includes a Timer object from noiftimer that starts timing on the first call to display and stops timing once self.counter >= self.total. It can be easily added to the progress bar display by calling Timer's checkTime function and passing the value to the 'prefix' or 'suffix' param of self.display():

bar = ProgBar(total=someTotal) bar.display(prefix=f"Run time: {bar.timer.checkTime()}")

ProgBar( total: float, fill_ch: str = '_', unfill_ch: str = '/', width_ratio: float = 0.75, new_line_after_completion: bool = True, clear_after_completion: bool = False)
67    def __init__(
68        self,
69        total: float,
70        fill_ch: str = "_",
71        unfill_ch: str = "/",
72        width_ratio: float = 0.75,
73        new_line_after_completion: bool = True,
74        clear_after_completion: bool = False,
75    ):
76        """:param total: The number of calls to reach 100% completion.
77
78        :param fill_ch: The character used to represent the completed part of the bar.
79
80        :param unfill_ch: The character used to represent the uncompleted part of the bar.
81
82        :param width_ratio: The width of the progress bar relative to the width of the terminal window.
83
84        :param new_line_after_completion: Make a call to print() once self.counter >= self.total.
85
86        :param clear_after_completion: Make a call to printbuddies.clear() once self.counter >= self.total.
87
88        Note: if new_line_after_completion and clear_after_completion are both True, the line will be cleared
89        then a call to print() will be made."""
90        self.total = total
91        self.fill_ch = fill_ch[0]
92        self.unfill_ch = unfill_ch[0]
93        self.width_ratio = width_ratio
94        self.new_line_after_completion = new_line_after_completion
95        self.clear_after_completion = clear_after_completion
96        self.timer = Timer()
97        self.reset()
Parameters
  • total: The number of calls to reach 100% completion.

  • fill_ch: The character used to represent the completed part of the bar.

  • unfill_ch: The character used to represent the uncompleted part of the bar.

  • width_ratio: The width of the progress bar relative to the width of the terminal window.

  • new_line_after_completion: Make a call to print() once self.counter >= self.total.

  • clear_after_completion: Make a call to printbuddies.clear() once self.counter >= self.total.

Note: if new_line_after_completion and clear_after_completion are both True, the line will be cleared then a call to print() will be made.

def reset(self):
 99    def reset(self):
100        self.counter = 0
101        self.percent = ""
102        self.prefix = ""
103        self.suffix = ""
104        self.filled = ""
105        self.unfilled = ""
106        self.bar = ""
def get_percent(self) -> str:
108    def get_percent(self) -> str:
109        """Returns the percentage complete to two decimal places
110        as a string without the %."""
111        percent = str(round(100.0 * self.counter / self.total, 2))
112        if len(percent.split(".")[1]) == 1:
113            percent = percent + "0"
114        if len(percent.split(".")[0]) == 1:
115            percent = "0" + percent
116        return percent

Returns the percentage complete to two decimal places as a string without the %.

def get_bar(self):
134    def get_bar(self):
135        return f"{self.prefix} [{self.filled}{self.unfilled}]-{self.percent}% {self.suffix}"
def display( self, prefix: str = '', suffix: str = '', counter_override: float = None, total_override: float = None, return_object: Any = None) -> Any:
137    def display(
138        self,
139        prefix: str = "",
140        suffix: str = "",
141        counter_override: float = None,
142        total_override: float = None,
143        return_object: Any = None,
144    ) -> Any:
145        """Writes the progress bar to the terminal.
146
147        :param prefix: String affixed to the front of the progress bar.
148
149        :param suffix: String appended to the end of the progress bar.
150
151        :param counter_override: When an externally incremented completion counter is needed.
152
153        :param total_override: When an externally controlled bar total is needed.
154
155        :param return_object: An object to be returned by display().
156
157        Allows display() to be called within a comprehension:
158
159        e.g.
160
161        bar = ProgBar(9)
162
163        myList = [bar.display(return_object=i) for i in range(10)]"""
164        if not self.timer.started:
165            self.timer.start()
166        if counter_override is not None:
167            self.counter = counter_override
168        if total_override:
169            self.total = total_override
170        # Don't wanna divide by 0 there, pal
171        while self.total <= 0:
172            self.total += 1
173        self.prefix = prefix
174        self.suffix = suffix
175        self._prepare_bar()
176        self._trim_bar()
177        pad = " " * (self.terminal_width - len(self.bar))
178        width = get_terminal_size().columns
179        print(f"{self.bar}{pad}"[: width - 2], flush=True, end="\r")
180        if self.counter >= self.total:
181            self.timer.stop()
182            if self.clear_after_completion:
183                clear()
184            if self.new_line_after_completion:
185                print()
186        self.counter += 1
187        return return_object

Writes the progress bar to the terminal.

Parameters
  • prefix: String affixed to the front of the progress bar.

  • suffix: String appended to the end of the progress bar.

  • counter_override: When an externally incremented completion counter is needed.

  • total_override: When an externally controlled bar total is needed.

  • return_object: An object to be returned by display().

Allows display() to be called within a comprehension:

e.g.

bar = ProgBar(9)

myList = [bar.display(return_object=i) for i in range(10)]