Skip to content

Instantly share code, notes, and snippets.

@fgimian
Last active December 29, 2016 04:07
Show Gist options
  • Save fgimian/d18bb491c50f448c0a80e8effb2593b1 to your computer and use it in GitHub Desktop.
Save fgimian/d18bb491c50f448c0a80e8effb2593b1 to your computer and use it in GitHub Desktop.
import math
import sys
import time
# Colours
BOLD = '\033[1m'
RED = '\033[91m'
GREEN = '\033[92m'
YELLOW = '\033[93m'
BLUE = '\033[94m'
ENDC = '\033[0m'
# Other Escape Sequences
MOVE_UP_TWO = '\033[2A'
CLEAR_LINE = '\033[0K'
HIDE_CURSOR = '\033[?25l'
SHOW_CURSOR = '\033[?25h'
def print_overlap(text, first=False, file=sys.stdout):
"""
Prints a block of overlapping text ensuring that previous output is cleared.
:param text: The text to display as a multiline string or list (where each item is one line).
:param first: Whether or not this is the first run of the function.
:param file: The stream to display the output to.
"""
# Obtain the lines that we will print
lines = text.split('\n') if isinstance(text, str) else text
# Determine how many times we need to move up two lines
num_moves_up_two = math.ceil(len(lines) / 2)
if not first:
print(MOVE_UP_TWO * num_moves_up_two, end='', file=file, flush=True)
# If an odd number of lines is found, we need to move the cursor down one line with a \n
if len(lines) % 2 == 1:
print(file=file, flush=True)
for line in lines:
print(f'{CLEAR_LINE}{line}', file=file, flush=True)
def progress_bar(
percent, width=40, boundary_symbol_left='|', boundary_symbol_right='|',
progress_symbols=['▏', '▎', '▍', '▌', '▋', '▊', '▉', '█']
):
"""
Builds a progress bar based on the required appearance.
:param percent: The current percentage to display (as a number between 0 and 100).
:param width: The total width of the progress bar (includes the boundaries).
:param boundary_symbol_left: The left boundary symbol to display.
:param boundary_symbol_right: The right boundary symbol to display.
:param progress_symbols: A list containing gradual progress indicators.
:return: a unicode string which may be printed to display the progress bar
"""
# Remove the two boundary items to determine the width of the progress bar
progress_width = width - 2
# Calculate how far we've progressed based on the width requested
complete_width = progress_width * percent / 100
# Calculate how many full blocks we will display for completeed progress
complete_blocks = int(complete_width)
# Determine which progress symbol to use for the last block
partial_block_width = complete_width - complete_blocks
partial_block_symbol_index = int(partial_block_width * len(progress_symbols))
# If progress is less than 100%, we need to show the partial block
partial_block_show = complete_blocks < progress_width
# Calculate how many full blocks we will display for incomplete progress
incomplete_blocks = progress_width - complete_blocks - (1 if partial_block_show else 1)
return (
# left boundary
f'{boundary_symbol_left}'
# completed blocks
f'{progress_symbols[-1] * (complete_blocks)}'
# partial block
f'{progress_symbols[partial_block_symbol_index] if partial_block_show else ""}'
# incomplete blocks
f'{" " * incomplete_blocks}'
# right boundary
f'{boundary_symbol_right}'
)
def main():
# Progress bar example
try:
print(HIDE_CURSOR, end='', flush=True)
progress = 0
while progress <= 100:
print_overlap([
'',
f'{BOLD}Installing Something Awesome{ENDC}',
'',
f'{BLUE}Extracting the_awesome_thing.zip...{ENDC}',
'',
f'{progress:8.1f} % {progress_bar(progress, width=50)} file_{int(progress)}.txt',
], first=True if progress == 0 else False)
progress += 0.01
time.sleep(0.001)
finally:
print()
print(SHOW_CURSOR, end='', flush=True)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment