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

Feature: multilevel progressbar

Open lig opened this issue 6 years ago • 9 comments

It would be great to be able to wrap several nested iterables like

for i in progressbar.progressbar(range(20)):
    for j in progressbar.progressbar(range(5)):
        time.sleep(0.1)

As for now this results in multiple progress bars appearing one after another. It could look like this instead

20% (1 of 5) |#####                     | Elapsed Time: 0:00:02 ETA:   0:00:03
30% (6 of 20) |#####                    | Elapsed Time: 0:01:02 ETA:   0:06:01

Also, it looks like this could be a possible step on the way to that one https://github.com/WoLpH/python-progressbar/issues/176

lig avatar Apr 17 '19 06:04 lig

It's indeed very similar to #176 and I'm guessing the solution will be the same. Although the threading versions might take slightly more effort.

It's been on my to-do list for a while but it's going to be a bit of work to do it right.

wolph avatar Apr 18 '19 21:04 wolph

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Aug 22 '19 09:08 stale[bot]

Not quite the same, but I implemented a multi-bar: #208

paulo-raca avatar Sep 18 '19 23:09 paulo-raca

@paulo-raca it looks like something that solves it and even looks gorgeous. Thanks for your work!

lig avatar Sep 19 '19 08:09 lig

@WoLpH I'm happy with #208.

Feel free to close this one if it looks redundant to you now.

lig avatar Sep 24 '19 09:09 lig

I love the multibar hack but I still think there is a need for a good multi progressbar solution. There are several limitations to this implementation which makes it hard/impractical to use in some cases.

That doesn't make this any less useful, it could be an addition to this feature. Once the multiple progressbars are working this one could be the "global" progressbar between them :)

wolph avatar Sep 24 '19 10:09 wolph

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Nov 23 '19 11:11 stale[bot]

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

stale[bot] avatar Jan 30 '20 02:01 stale[bot]

As a reasonable workaround, I've wrote this little bit of script:

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)

It's certainly not as foolproof and well-tested as the rest of the progressbar code, but I think it should do the trick for the common use-cases :)

wolph avatar Sep 14 '22 01:09 wolph

I've properly added the feature of multiple progressbars now. The approach is a bit different from tqdm but I personally think this is a more obvious (although slightly more verbose) approach.

The MultiBar can be used both using a with MultiBar() as multibar: and using a regular instance. You can manually render on demand using multibar.render() if you already have a display loop. If you want to have it render in the background you can use multibar.start() to run it as a background thread.

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

The new version with this feature has been released :)

wolph avatar Dec 18 '23 13:12 wolph

You did not add an example in the examples.py file from what I can see. Would be nice to have one!

wabiloo avatar Dec 19 '23 12:12 wabiloo

The issue is that the threaded example, opposed to all other examples, doesn't work in all environments so I'm not a 100% sure where I should enable/disable it. But I'll add it to the examples at least for where I can detect an ANSI terminal :)

wolph avatar Jan 02 '24 21:01 wolph