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

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

重要的是,我想避免擦除之前打印到控制台的所有文本(即,我不想在打印更新的进度时“清除”整个终端)。

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


当前回答

我意识到我在游戏中迟到了,但这里是我写的一个稍微有点yum风格的(Red Hat)(这里不是100%的准确性,但如果你使用进度条来达到这个准确度,那么你就错了):

import sys

def cli_progress_test(end_val, bar_length=20):
    for i in xrange(0, end_val):
        percent = float(i) / end_val
        hashes = '#' * int(round(percent * bar_length))
        spaces = ' ' * (bar_length - len(hashes))
        sys.stdout.write("\rPercent: [{0}] {1}%".format(hashes + spaces, int(round(percent * 100))))
        sys.stdout.flush()

应该产生如下所示的结果:

Percent: [##############      ] 69%

... 括号保持不变,只有哈希值增加。

作为装饰器,这可能会更好。再等一天……

其他回答

http://code.activestate.com/recipes/168639-progress-bar-class/ (2002) http://code.activestate.com/recipes/299207-console-text-progress-indicator-class/ (2004) http://pypi.python.org/pypi/progressbar (2006)

还有很多教程等着你去谷歌。

一个非常简单的解决方案是将以下代码放入循环中:

把这个放在你的文件的主体(即顶部):

import sys

把这个放在你的循环体中:

sys.stdout.write("-") # prints a dash for each iteration of loop
sys.stdout.flush() # ensures bar is displayed incrementally
import time,sys

for i in range(100+1):
    time.sleep(0.1)
    sys.stdout.write(('='*i)+(''*(100-i))+("\r [ %d"%i+"% ] "))
    sys.stdout.flush()

输出

[29 percent] ===================

下面是一个用Python编写的进度条的好例子:http://nadiana.com/animated-terminal-progress-bar-in-python

但如果你想自己写。你可以使用curses模块让事情变得更简单:)

(编辑) 也许更容易不是诅咒这个词。但是如果你想创建一个成熟的cui,那么curses会为你照顾很多东西。

(编辑) 由于旧的链接已经失效,我已经发布了我自己的Python Progressbar版本,请在这里获取:https://github.com/WoLpH/python-progressbar

我推荐使用tqdm - https://pypi.python.org/pypi/tqdm -它可以简单地将任何可迭代对象或进程转换为进度条,并处理所需终端的所有混乱。

从文档中可以看到:“tqdm可以很容易地支持回调/钩子和手动更新。这里有一个urllib的例子。”

import urllib
from tqdm import tqdm

def my_hook(t):
  """
  Wraps tqdm instance. Don't forget to close() or __exit__()
  the tqdm instance once you're done with it (easiest using `with` syntax).

  Example
  -------

  >>> with tqdm(...) as t:
  ...     reporthook = my_hook(t)
  ...     urllib.urlretrieve(..., reporthook=reporthook)

  """
  last_b = [0]

  def inner(b=1, bsize=1, tsize=None):
    """
    b  : int, optional
        Number of blocks just transferred [default: 1].
    bsize  : int, optional
        Size of each block (in tqdm units) [default: 1].
    tsize  : int, optional
        Total size (in tqdm units). If [default: None] remains unchanged.
    """
    if tsize is not None:
        t.total = tsize
    t.update((b - last_b[0]) * bsize)
    last_b[0] = b
  return inner

eg_link = 'http://www.doc.ic.ac.uk/~cod11/matryoshka.zip'
with tqdm(unit='B', unit_scale=True, miniters=1,
          desc=eg_link.split('/')[-1]) as t:  # all optional kwargs
    urllib.urlretrieve(eg_link, filename='/dev/null',
                       reporthook=my_hook(t), data=None)