0

I'm new to Python and working on a little program that copies all files of given extension from a folder and it's subfolders to an another directory. Recently I added a simple progress bar and a counter of remaining files. The problem is that when I run it from cmd and counter comes from say 1000 to 999 cmd adds a zero in the place of a last digit instead of space. Moreover, when the program is finished remaining files counter should be substituted by the word "Done." and it also doesn't work well.

I tried to replace sys.stdout.write with print and tried not to use f-strings, the result is the same.

def show_progress_bar(total, counter=0, length=80):
    percent = round(100 * (counter / total))
    filled_length = int(length * counter // total)
    bar = '=' * filled_length + '-' * (length - filled_length)
    if counter < total:
        suffix = f'Files left: {total - counter}'
    else:
        suffix = 'Done.'
    sys.stdout.write(f'\rProgress: |{bar}| {percent}% {suffix}')
    sys.stdout.flush()

def selective_copy(source, destination, extension):
    global counter
    show_progress_bar(total)
    for foldername, subfolders, filenames in os.walk(source):
        for filename in filenames:
            if filename.endswith(extension):
                if not os.path.exists(os.path.join(destination, filename)):
                    shutil.copy(os.path.join(foldername, filename), os.path.join(destination, filename))
                else:
                    new_filename = f'{os.path.basename(foldername)}_{filename}'
                    shutil.copy(os.path.join(foldername, filename), os.path.join(destination, new_filename))
                counter += 1
                show_progress_bar(total, counter)

I expected that the output in cmd will be the same as in the console, which is this: Program running:

Progress: |=========-----------------------------------------------------------------------| 12% Files left: 976

Program finished:

Progress: |================================================================================| 100% Done.

But in the cmd I got this: Program running:

Progress: |=========-----------------------------------------------------------------------| 12% Files left: 9760

Program finished:

Progress: |================================================================================| 100% Done. left: 100
pltnk
  • 47
  • 2
  • 7
  • You need to "clean" the rest of the line. Check out this example solution https://stackoverflow.com/a/3419991/6018688. The issue in your case is, that you have an unknown number of characters, since the file total file count can probably change. So you need to account for changing file numbers by either allowing the largest expected count or calculate the maximal length of the progress line in advance to clean the whole rest of the line. Also, no need to use sys - use print("foo", end="\r", flush=True) – fabianegli May 24 '19 at 12:47
  • Possible duplicate of [Print to the same line and not a new line in python](https://stackoverflow.com/questions/3419984/print-to-the-same-line-and-not-a-new-line-in-python) – fabianegli May 24 '19 at 12:47
  • @fabianegli, thanks for your suggestion! Didn't know that one could use flush inside a print. – pltnk May 24 '19 at 15:01

1 Answers1

2

Typically, printing "\r" will return the cursor to the beginning of the line, but it won't erase anything already written. So if you write "1000" followed by "\r" followed by "999", the last 0 of "1000" will still be visible.

(I'm not sure why this isn't happening in your Python console. Maybe it interprets "\r" in a different way. Hard to say without knowing exactly what software you're running.)

One solution is to print a couple of spaces after your output to ensure that slightly longer old messages get overwritten. You can probably get away with just one space for your "Files left:" suffix, since that only decreases by one character at most, but the "done" suffix will need more.

if counter < total:
    suffix = f'Files left: {total - counter} '
else:
    suffix = 'Done.               '
Kevin
  • 74,910
  • 12
  • 133
  • 166
  • Thank you, @Kevin! I ended up with combining yours suggestion and @fabianegli's one from above, it works pretty well now. – pltnk May 24 '19 at 14:36