pythonpython-3.xprogress-barblack-box

Python CLI Progress bar/spinner WITHOUT iteration


There are numerous existing questions regarding the display of progress bars in the terminal while a Python script executes, but every one of them is based on a loop where you perform an operation and then update the progress graphic.

Unfortunately, the function whose progress I want to show--or at least a spinner object to show that it's working--is a black-box that I can't (at least really, really shouldn't) alter. Essentially, what I want to do is:

#pseudocode input
print('Loading')
spinner.begin()
blackbox() #a few thousand operations happen in here
spinner.end()
print('Finished')

#pseudocode output
Loading.
Loading..
Loading...
Loading.
Loading..
Loading...
Finished

Although ideally that would be an animation of the ellipsis instead of printing multiple lines. Before I can even start building silly ascii animations though, there's the main hurdle:

Is there a way to run spinner and blackbox() at the same time? Alternately, is there a hack to pause blackbox(), regardless of its content, every few hundred milliseconds, update the spinner graphic, and then resume where it left off?

I've tried this with the progress module but had no luck... I couldn't even get the example code to work, it just hung up after I started iterating until I Ctrl+C'd out.


Solution

  • Threads is probably the easiest way to make this work. Here is a vastly simplified version that should get the point across. I wasn't sure whether you actually have the spinner function or not, so I made my own.

    import threading
    import time
    
    def blackbox():
        time.sleep(10)
    
    thread = threading.Thread(target=blackbox)
    thread.start()
    
    eli_count = 0
    while thread.is_alive():
        print('Loading', '.'*(eli_count+1), ' '*(2-eli_count), end='\r')
        eli_count = (eli_count + 1) % 3
        time.sleep(0.1)
    thread.join()
    print('Done      ')
    

    So, while blackbox runs, the loading message is updated periodically. Once it finishes, the thread is joined and the loading message is replaced with a completed message.