rsalmei / alive-progress

A new kind of Progress Bar, with real-time throughput, ETA, and very cool animations!
MIT License
5.53k stars 206 forks source link

Disable bar based on a parameter/variable? #103

Closed sortedcord closed 3 years ago

sortedcord commented 3 years ago

Is it possible to disable the bar based on certain conditions? A good example of what I'm saying could be something like:

progress_bar = False
if progress_bar == False:
  # code to disable the progressbar
else:
  #progressbar remains enabled

with alive_bar(100) as bar:
  # Process code

In this case the visibility of alive_bar depends on the value of progress_bar ... Another solution that I thought of was writing the process code 2 times to account for the progress_bar value but that is quite unconventional...

sortedcord commented 3 years ago

Another way I tried to do this was something like so:


def foo():
  try:
    br()
  except NameError:
    def br():
      pass

  #Some process code
  br()

if progress_bar == True:
  with alive_bar(1) as br:
    foo()
else:
  foo()

This is another hacky solution I came up with which works halfway... That is, if I use .text along with then it causes an error if br() is not defined. I cannot add a try exception case every time I use br

TheTechRobo commented 3 years ago

For future reference, you should use if progress_bar or if progress_bar is True, and if not progressbar or if progress_bar is False, not ==. is is faster and better for this case, as 1 == True is true but 1 is True is false.

rsalmei commented 3 years ago

Hey @sortedcord,

Why would you want to disable the progress bar based on a parameter? What is your use case?

sortedcord commented 3 years ago

I'm working upon a script in which I'm trying to implement a "quiet mode" sort of thing with no verbose. That's why I wanted to disable the progress bar based on a certain condition

sortedcord commented 3 years ago

For future reference, you should use if progress_bar or if progress_bar is True, and if not progressbar or if progress_bar is False, not ==. is is faster and better for this case, as 1 == True is true but 1 is True is false.

Yeah, didn't have that in mind writing the code. Thanks

TheTechRobo commented 3 years ago

This is another hacky solution I came up with which works halfway... That is, if I use .text along with then it causes an error if br() is not defined. I cannot add a try exception case every time I use br

Try something like this ( Haven't tested it, and it's quite hacky):

class bar_placeholder:
    @staticmethod
    def text(*args): pass
br = bar_placeholder #if the progress bar is active, br will be overwritten by the progress bar. otherwise, bar_placeholder will be br
if progress_bar is True:
  with alive_bar(1) as br:
    foo()
else:
  foo()
sortedcord commented 3 years ago

Another way I tried to do this was something like so:

def foo():
  try:
    br()
  except NameError:
    def br():
      pass

  #Some process code
  br()

if progress_bar == True:
  with alive_bar(1) as br:
    foo()
else:
  foo()

This is another hacky solution I came up with which works halfway... That is, if I use .text along with then it causes an error if br() is not defined. I cannot add a try exception case every time I use br

Now that I look at this bit of code, I do realize its quite unintuitive, so I made some modifications-

def foo(bar=None):
  def progress():
    try:
      bar()
    except TypeError:
      #Some code 
  #Process
  progress()

if progress_bar:
  with alive_bar(1) as br:
    foo(br)
else:
  foo()

Not a permanent solution but gets the job done. So I'm closing this issue for now..

radugrosu commented 3 years ago

Disabling a progress bar based on a switch is possible in tqdm, for good reasons. One example is avoiding clutter whenever a function using a progress bar is being called while another progress bar is active (especially relevant when the said function is called from tens of processes). In such a case I'd call tqdm with disable=True.

radugrosu commented 3 years ago

The workaround I'm using atm looks like this (it's supposed to support tqdm specific arguments):

class NoOpProgressBar:
    def __init__(self, iterable=None, total=None, *, calibrate=None, **options):
        self.iterable = iterable
        self.total = total
        self.calibrate = calibrate
        self.options = options

    def __iter__(self):
        return iter(self.iterable)

    def __enter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        pass

    def update(self, *_, **__):
        pass

class AliveProgressBar:
    def __init__(self, iterable=None, total=None, *, calibrate=None, **options):
        self.iterable = iterable
        self.total = total
        self.calibrate = calibrate
        for key in "unit", "leave":
            options.pop(key, None)
        if "desc" in options:
            options["title"] = options.pop("desc")
        self.options = options
        self.disable = self.options.pop("disable", False)

    def __enter__(self):
        from alive_progress import alive_bar
        if self.disable:
            return NoOpProgressBar()

        self._bar = bar = alive_bar(total=self.total, calibrate=self.calibrate, **self.options)
        enter = bar.__enter__()
        enter.update = enter.__call__
        return enter

    def __exit__(self, exc_type, exc_val, exc_tb):
        if not self.disable:
            self._bar.__exit__(exc_type, exc_val, exc_tb)

    def __iter__(self):
        from alive_progress import alive_it
        if self.disable:
            return iter(self.iterable)
        else:
            return iter(alive_it(self.iterable, total=self.total, calibrate=self.calibrate, **self.options))
rsalmei commented 3 years ago

Hey, I've taken a look at the disable field you mentioned, and it's exactly my force_tty! In 2.0 you can send force_tty=False, and the bar will be completely disabled, except for the final receipt. Does that work for you?

radugrosu commented 3 years ago

Hi Rogério, I must be doing something wrong, since I'm on 2.0.0, and the progress bar still appears for me even when force_tty=False. The only difference is the live spinner is gone (not visible in the final result):

❯ python test.py 
|████████████████████████████████████████| 4/4 [100%] in 0.5s (8.53/s)
|████████████████████████████████████████| 4/4 [100%] in 0.4s (9.98/s)

where test.py is:

import time
import os
from alive_progress import alive_it

for i in alive_it(range(4), force_tty=True):
    time.sleep(0.1)
for i in alive_it(range(4), force_tty=False):
    time.sleep(0.1)
rsalmei commented 3 years ago

Yes, this is absolutely right, in this mode I disable every animated feature, actually I do not even start the background thread. But the final receipt I do print, since I find it really important. It shows some processing did occur, and finished with success. Is this a problem to show the final result only? Both @sortedcord (original OP) and @radugrosu.

radugrosu commented 3 years ago

For me the disable flag should make the progress bar a complete no-op. In these cases even having final receipts is undesirable, since they would tend to make the result unreadable / unwieldy.

rsalmei commented 3 years ago

I see... Now I understand it. I'm going to include this feature then, along with Jupyter support!

rsalmei commented 3 years ago

Hey, the PR is ready to fix this! #111 I'll release it very soon...

radugrosu commented 3 years ago

Looks amazing :hugs:

rsalmei commented 3 years ago

Hey, version 2.1 is released!! This should be fixed, please report back if anything new arises.