python-progressbar icon indicating copy to clipboard operation
python-progressbar copied to clipboard

Is there a way to have multiple concurrent and independent progress bars?

Open wabiloo opened this issue 3 years ago • 4 comments

Description

I have 2 concurrent processes (threads really) performing operations that are independent. I would like to have 2 progress bars (as 2 lines) to show the progress of each. The first one is bound (number of tasks known), the second one is not (number of tasks unknown)

Is that possible? I didn't see anything obvious in the examples

wabiloo avatar Sep 07 '22 10:09 wabiloo

There's a simpler version available called the multi-bar: https://github.com/WoLpH/python-progressbar/pull/208

But the full feature is still a work in progress: https://github.com/WoLpH/python-progressbar/issues/189

wolph avatar Sep 08 '22 10:09 wolph

Yes, I'm aware of the multi-bar (I already use it for something else), but here the 2 processes are (almost) completely independent and of different nature, so the multi-bar wouldn't work.

Any idea when you think the full feature might become available?

wabiloo avatar Sep 08 '22 15:09 wabiloo

Well... I've been thinking about the feature for a few years now with little time to work on it. But I've probably been thinking of a much too difficult solution for the issue :)

I've got a workaround that isn't full support, but I think it works rather well :)

import sys
import time

import progressbar


class LineOffsetStreamWrapper:
    UP = '\033[F'
    DOWN = '\033[B'

    def __init__(self, lines=0, stream=sys.stderr):
        self.stream = stream
        self.lines = lines

    def write(self, data):
        self.stream.write(self.UP * self.lines)
        self.stream.write(data)
        self.stream.write(self.DOWN * self.lines)
        self.stream.flush()

    def __getattr__(self, name):
        return getattr(self.stream, name)


bars = []
for i in range(5):
    bars.append(progressbar.ProgressBar(
        fd=LineOffsetStreamWrapper(i),
        max_value=1000,
    ))

    if i:
        print('Reserve a line for the progressbar')

for i in range(100):
    for j, bar in enumerate(bars, 1):
        bar.update(i * j)
    time.sleep(0.1)

Would this work for you? :)

wolph avatar Sep 14 '22 01:09 wolph

I will test this and get back to you.

wabiloo avatar Sep 15 '22 10:09 wabiloo

I've properly added the feature now. I'm still testing with it but I believe it works quite well. Usage: https://github.com/wolph/python-progressbar#multiple-threaded-progressbars

import random
import threading
import time

import progressbar

BARS = 5
N = 50


def do_something(bar):
    for i in bar(range(N)):
        # Sleep up to 0.1 seconds
        time.sleep(random.random() * 0.1)

        # print messages at random intervals to show how extra output works
        if random.random() > 0.9:
            bar.print('random message for bar', bar, i)


with progressbar.MultiBar() as multibar:
    for i in range(BARS):
        # Get a progressbar
        bar = multibar[f'Thread label here {i}']
        # Create a thread and pass the progressbar
        threading.Thread(target=do_something, args=(bar,)).start()

You can add your custom progressbars to a MultiBar instance like this:

multi['Some label'] = your_progress

wolph avatar Jan 23 '23 14:01 wolph

Thanks. I'm afraid I've now changed job, and the project I was planning on using that on is now dead...

Hopefully I get to make use of it in the near future in a new project

wabiloo avatar Jan 31 '23 07:01 wabiloo

No worries :)

If you ever need it in the future you know where to find it ;)

wolph avatar Feb 06 '23 01:02 wolph