终端中带有块字符的文本进度条

2022-01-11 00:00:00 python console updating progress

问题描述

我编写了一个简单的控制台应用程序来使用 ftplib 从 FTP 服务器上传和下载文件.

I wrote a simple console app to upload and download files from an FTP server using the ftplib.

我希望该应用程序为用户显示其下载/上传进度的一些可视化;每次下载数据块时,我希望它提供进度更新,即使它只是像百分比这样的数字表示.

I would like the app to show some visualization of its download/upload progress for the user; each time a data chunk is downloaded, I would like it to provide a progress update, even if it's just a numeric representation like a percentage.

重要的是,我想避免删除在前几行中打印到控制台的所有文本(即,我不想在打印更新的进度时清除"整个终端).

Importantly, I want to avoid erasing all the text that's been printed to the console in previous lines (i.e. I don't want to "clear" the entire terminal while printing the updated progress).

这似乎是一项相当常见的任务——我如何才能在保留先前程序输出的同时制作进度条或类似的可视化输出到我的控制台?

This seems a fairly common task – how can I go about making a progress bar or similar visualization that outputs to my console while preserving prior program output?


解决方案

Python 3

一个简单、可自定义的进度条

以下是我经常使用的许多答案的汇总(无需导入).

Python 3

A Simple, Customizable Progress Bar

Here's an aggregate of many of the answers below that I use regularly (no imports required).

注意:此答案中的所有代码都是为 Python 3 创建的;请参阅答案的结尾以将此代码与 Python 2 一起使用.

Note: All code in this answer was created for Python 3; see end of answer to use this code with Python 2.

# Print iterations progress
def printProgressBar (iteration, total, prefix = '', suffix = '', decimals = 1, length = 100, fill = '█', printEnd = ""):
    """
    Call in a loop to create terminal progress bar
    @params:
        iteration   - Required  : current iteration (Int)
        total       - Required  : total iterations (Int)
        prefix      - Optional  : prefix string (Str)
        suffix      - Optional  : suffix string (Str)
        decimals    - Optional  : positive number of decimals in percent complete (Int)
        length      - Optional  : character length of bar (Int)
        fill        - Optional  : bar fill character (Str)
        printEnd    - Optional  : end character (e.g. "", "
") (Str)
    """
    percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
    filledLength = int(length * iteration // total)
    bar = fill * filledLength + '-' * (length - filledLength)
    print(f'{prefix} |{bar}| {percent}% {suffix}', end = printEnd)
    # Print New Line on Complete
    if iteration == total: 
        print()

示例用法

import time

# A List of Items
items = list(range(0, 57))
l = len(items)

# Initial call to print 0% progress
printProgressBar(0, l, prefix = 'Progress:', suffix = 'Complete', length = 50)
for i, item in enumerate(items):
    # Do stuff...
    time.sleep(0.1)
    # Update Progress Bar
    printProgressBar(i + 1, l, prefix = 'Progress:', suffix = 'Complete', length = 50)

样本输出

Progress: |█████████████████████████████████████████████-----| 90.0% Complete

更新

评论中有关于允许进度条动态调整到终端窗口宽度的选项的讨论.虽然我不推荐这样做,但这里有一个实现此功能的 gist(并注意注意事项).

下面的评论引用了一个很好的答案来回答类似的问题.我喜欢它展示的易用性并写了一个类似的,但选择省略 sys 模块的导入,同时添加原始 printProgressBar 的一些功能上面的函数.

A comment below referenced a nice answer posted in response to a similar question. I liked the ease of use it demonstrated and wrote a similar one, but opted to leave out the import of the sys module while adding in some of the features of the original printProgressBar function above.

与上面的原始函数相比,这种方法的一些好处包括消除了对函数的初始调用以在 0% 处打印进度条,并且使用 enumerate 成为可选的(即它不是更明确地需要使函数工作).

Some benefits of this approach over the original function above include the elimination of an initial call to the function to print the progress bar at 0% and the use of enumerate becoming optional (i.e. it is no longer explicitly required to make the function work).

def progressBar(iterable, prefix = '', suffix = '', decimals = 1, length = 100, fill = '█', printEnd = ""):
    """
    Call in a loop to create terminal progress bar
    @params:
        iterable    - Required  : iterable object (Iterable)
        prefix      - Optional  : prefix string (Str)
        suffix      - Optional  : suffix string (Str)
        decimals    - Optional  : positive number of decimals in percent complete (Int)
        length      - Optional  : character length of bar (Int)
        fill        - Optional  : bar fill character (Str)
        printEnd    - Optional  : end character (e.g. "", "
") (Str)
    """
    total = len(iterable)
    # Progress Bar Printing Function
    def printProgressBar (iteration):
        percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
        filledLength = int(length * iteration // total)
        bar = fill * filledLength + '-' * (length - filledLength)
        print(f'{prefix} |{bar}| {percent}% {suffix}', end = printEnd)
    # Initial Call
    printProgressBar(0)
    # Update Progress Bar
    for i, item in enumerate(iterable):
        yield item
        printProgressBar(i + 1)
    # Print New Line on Complete
    print()

示例用法

import time

# A List of Items
items = list(range(0, 57))

# A Nicer, Single-Call Usage
for item in progressBar(items, prefix = 'Progress:', suffix = 'Complete', length = 50):
    # Do stuff...
    time.sleep(0.1)

样本输出

Progress: |█████████████████████████████████████████████-----| 90.0% Complete

Python 2

要在 Python 2 中使用上述函数,请在脚本顶部将编码设置为 UTF-8:

Python 2

To use the above functions in Python 2, set the encoding to UTF-8 at the top of your script:

# -*- coding: utf-8 -*-

并替换这一行中的 Python 3 字符串格式:

And replace the Python 3 string formatting in this line:

print(f'{prefix} |{bar}| {percent}% {suffix}', end = printEnd)

使用 Python 2 字符串格式化:

With Python 2 string formatting:

print('%s |%s| %s%% %s' % (prefix, bar, percent, suffix), end = printEnd)

相关文章