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
3
  • You need to "clean" the rest of the line. Check out this example solution 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) Commented May 24, 2019 at 12:47
  • Possible duplicate of Print to the same line and not a new line in python Commented May 24, 2019 at 12:47
  • @fabianegli, thanks for your suggestion! Didn't know that one could use flush inside a print. Commented May 24, 2019 at 15:01

1 Answer 1

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.               '
Sign up to request clarification or add additional context in comments.

1 Comment

Thank you, @Kevin! I ended up with combining yours suggestion and @fabianegli's one from above, it works pretty well now.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.