替换Python中的控制台输出


106

我想知道如何像某些C / C ++程序那样在Python中创建这些漂亮的控制台计数器之一。

我在做一个循环,当前输出如下:

Doing thing 0
Doing thing 1
Doing thing 2
...

更整洁的是只更新最后一行;

X things done.

我已经在许多控制台程序中看到了这一点,并且想知道是否/如何在Python中做到这一点。




1
@BjörnPollex太curses过分了(请参阅已接受的答案)。
Alexey

Answers:


151

一个简单的解决方案是只"\r"在字符串之前编写而不添加换行符。如果字符串永远不会变短,那就足够了...

sys.stdout.write("\rDoing thing %i" % i)
sys.stdout.flush()

进度条稍微复杂一点……这是我正在使用的东西:

def startProgress(title):
    global progress_x
    sys.stdout.write(title + ": [" + "-"*40 + "]" + chr(8)*41)
    sys.stdout.flush()
    progress_x = 0

def progress(x):
    global progress_x
    x = int(x * 40 // 100)
    sys.stdout.write("#" * (x - progress_x))
    sys.stdout.flush()
    progress_x = x

def endProgress():
    sys.stdout.write("#" * (40 - progress_x) + "]\n")
    sys.stdout.flush()

你调用startProgress传递操作的描述,然后progress(x)在那里x是个和最后endProgress()


2
如果字符串比前一个短呢?
math2001 '16

6
@ math2001用空格填充。
felipsmartins

仅投票前两行代码。在某些情况下,进度条部分会变慢。无论如何,感谢@ 6502
WaterRocket8236 '17

有些程序(resticflatpak)可以更新控制台输出的几行。您是否知道如何实现这一目标?
Alexey

1
@Alexey:您可以使用ANSI转义码来移动光标,清除屏幕部分并更改颜色...请参见en.wikipedia.org/wiki/ANSI_escape_code
6502

39

一个更优雅的解决方案可能是:

def progressBar(current, total, barLength = 20):
    percent = float(current) * 100 / total
    arrow   = '-' * int(percent/100 * barLength - 1) + '>'
    spaces  = ' ' * (barLength - len(arrow))

    print('Progress: [%s%s] %d %%' % (arrow, spaces, percent), end='\r')

value和调用此函数endvalue,结果应为

Progress: [------------->      ] 69 %

注意:此处为 Python 2.x版本。


您应该使用Halo以获得更好的进度条和微调器。
Aravind Voggu

17

python 3中,您可以执行以下操作以在同一行上打印:

print('', end='\r')

跟踪最新更新和进度特别有用。

如果有人想查看循环的进度,我也建议从这里推荐tqdm。它将当前迭代和总迭代打印为带有预期完成时间的进度条。超级有用且快速。适用于python2和python3。


7

另一个答案可能更好,但这是我在做什么。首先,我创建了一个名为progress的函数,该函数可以打印出退格字符:

def progress(x):
    out = '%s things done' % x  # The output
    bs = '\b' * 1000            # The backspace
    print bs,
    print out,

然后我在主函数中循环调用它,如下所示:

def main():
    for x in range(20):
        progress(x)
    return

当然,这会擦除整行,但是您可以将其弄乱以完全执行您想要的操作。我最终使用这种方法制作了一个进度条。


4
可以,但是如果前一行比下一行有更多字符,则新行结束后的字符将从前一行保留:“拼写检查记录417/701 [服务已更改为表面] [当发光时] “
Lil'Bits

7

对于那些在几年后迷失了方向的人(像我一样),我对6502的方法进行了一些调整,以使进度条既可以增加也可以减少。在更多情况下很有用。感谢6502提供了出色的工具!

基本上,唯一的区别是每次调用progress(x)时都会写入整行#s和-s,并且光标始终返回到小节的开头。

def startprogress(title):
    """Creates a progress bar 40 chars long on the console
    and moves cursor back to beginning with BS character"""
    global progress_x
    sys.stdout.write(title + ": [" + "-" * 40 + "]" + chr(8) * 41)
    sys.stdout.flush()
    progress_x = 0


def progress(x):
    """Sets progress bar to a certain percentage x.
    Progress is given as whole percentage, i.e. 50% done
    is given by x = 50"""
    global progress_x
    x = int(x * 40 // 100)                      
    sys.stdout.write("#" * x + "-" * (40 - x) + "]" + chr(8) * 41)
    sys.stdout.flush()
    progress_x = x


def endprogress():
    """End of progress bar;
    Write full bar, then move to next line"""
    sys.stdout.write("#" * 40 + "]\n")
    sys.stdout.flush()

1
不过,我发现,如果代码过于频繁地调用它,可能会导致性能下降,所以我猜是YMMV
jat255 2014年

6

如果我不太了解(不确定),则要使用<CR>而不是<LR>

如果可以的话,只要控制台终端允许这样做(当输出si重定向到文件时,它将中断)。

from __future__ import print_function
print("count x\r", file=sys.stdout, end=" ")

5

如果我们看一下print()函数,可以不用使用sys库来完成

print(*objects, sep=' ', end='\n', file=sys.stdout, flush=False)

这是我的代码:

def update(n):
    for i in range(n):
        print("i:",i,sep='',end="\r",flush=True)
        #time.sleep(1)

5

我前一段时间写了这篇文章,对此我感到非常满意。随意使用它。

它需要一个indextotal,也可以选择titlebar_length。完成后,用复选标记替换沙漏。

⏳ Calculating: [████░░░░░░░░░░░░░░░░░░░░░] 18.0% done

✅ Calculating: [█████████████████████████] 100.0% done

我提供了一个可以运行以对其进行测试的示例。

import sys
import time

def print_percent_done(index, total, bar_len=50, title='Please wait'):
    '''
    index is expected to be 0 based index. 
    0 <= index < total
    '''
    percent_done = (index+1)/total*100
    percent_done = round(percent_done, 1)

    done = round(percent_done/(100/bar_len))
    togo = bar_len-done

    done_str = '█'*int(done)
    togo_str = '░'*int(togo)

    print(f'\t⏳{title}: [{done_str}{togo_str}] {percent_done}% done', end='\r')

    if round(percent_done) == 100:
        print('\t✅')


r = 50
for i in range(r):
    print_percent_done(i,r)
    time.sleep(.02)

我也有一个带有响应进度栏的版本,具体取决于终端宽度,shutil.get_terminal_size()如果感兴趣的话。


4

Aravind Voggu的示例中增加了更多功能

def progressBar(name, value, endvalue, bar_length = 50, width = 20):
        percent = float(value) / endvalue
        arrow = '-' * int(round(percent*bar_length) - 1) + '>'
        spaces = ' ' * (bar_length - len(arrow))
        sys.stdout.write("\r{0: <{1}} : [{2}]{3}%".format(\
                         name, width, arrow + spaces, int(round(percent*100))))
        sys.stdout.flush()
        if value == endvalue:     
             sys.stdout.write('\n\n')

现在,您可以生成多个进度条,而无需替换前一个。

我还添加了 name了一个固定宽度的值。

对于两次循环两次progressBar(),结果的使用将类似于:

进度栏动画


-1

下面的代码将每0.3秒从0到137的消息计数,替换先前的数字。

到后台的符号数=数字位数。

stream = sys.stdout
for i in range(137):
    stream.write('\b' * (len(str(i)) + 10))
    stream.write("Message : " + str(i))
    stream.flush()
    time.sleep(0.3)
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.