progress bar in python import sys
import time
import multiprocessing


def change_char_at(s: str, ch: str, i: int, j: int=None):
    '''strings are immutable so i made this handy-little function to 'mutate' strings
    similar to =&gt;
    s[i] = ch    # if j is not specified
    s[i:j] = ch  # if j is specified

    *note*: negative indices does not work!
    '''
    if not j:
        j = i + 1
    return s[:i] + ch + s[j:]


def loader(width: int=20, bar_width: int=3):
    '''
    A simple loading bar which shows no information or whatever
    the function should be run in a different thread and must be killed.
    '''
    s = '[' + ' ' * (width - bar_width - 1) + ']'
    while 1:
        for i in range(1, width - bar_width - 1):
            sys.stdout.write('\r' + change_char_at(s, '===', i))
            sys.stdout.flush()
            time.sleep(0.1)
        for i in range(width - bar_width - 1, 0, -1):
            sys.stdout.write('\r' + change_char_at(s, '===', i))
            sys.stdout.flush()
            time.sleep(0.1)


def loader_with_wait(wait: int=None, width: int=20, bar_width: int=3):
    '''
    A simple loading bar which shows no information or whatever.
    it will quit after `wait` seconds or will *run forever* if wait is not specified

    param: wait: for how much time should the loading bar run.
    '''
    # Start the loader
    p = multiprocessing.Process(target=loader, name='loader', args=(width, bar_width))
    p.start()

    if wait:
        # let the loader run for `wait` seconds
        time.sleep(wait)
        # terminate the loader() function
        p.terminate()
        # Cleanup
        p.join()
        # Start from newline
        sys.stdout.write('\n')


if __name__ == '__main__':
    try:
        loader_with_wait(10)
    except KeyboardInterrupt:
        pass


Hey Guys!
I made a simple progress bar in python. The animation is cool!
it can wait for a few seconds or it can run forever, the choice is upto you.
it can be used in other programs and can run in a separate thread.
Implementing the wait function was the hardest thing to do. is there any other way to do it?

Any suggestions will be helpful...  :-)

see it in action: https://asciinema.org/a/323180
The variable names in change_char_at are terrible. s for the string, ch for the char? i for the start? j for the end.

Seriously just write out names.

def change_char_at(string: str, change: str, start: int, end: int = None):
    return string[:start] + change + string[end or start+1:]

Your code isn't fully typed:


change_char_at has no return type.
end in change_char_at and wait in loader_with_wait are assumed to be Optional but not specified to be optional.
loader and loader_with_wait have no return types should they be None or NoReturn?

The functionallity of change_char_at can be eclipsed by the format mini-language.


You have a standard structure where the bar is surrounded by [] and filled with the contents.

bar = '[{}]'

The content of your bar is left aligned by width.

bar = '[{: &lt;{width}}]'

Each iteration you increase the left hand space by 1.

bar.format(' ' * i + '=' * bar_width, width=width)


bar_width doesn't actually change the size of the bar.
I would prefer the progress bar to be a generator function or list, so the rest of your code is simpler.


import itertools


def progress_bar(width: int = 20, bar_width: int = 3):
    bar = f'[{{: &lt;{width}}}]'
    for i in itertools.chain(
        range(0, width - bar_width + 1),
        reversed(range(0, width - bar_width)),
    ):
        yield bar.format(' ' * i + '=' * bar_width)