【问题标题】:Python to print out status bar and percentagePython打印状态栏和百分比
【发布时间】:2011-03-01 10:24:01
【问题描述】:

实现如下状态栏:

[==========                ]  45%
[================          ]  60%
[==========================] 100%

我希望将其打印到标准输出,并不断刷新它,而不是打印到另一行。如何做到这一点?

【问题讨论】:

标签: python


【解决方案1】:

发布的答案都没有完全满足我的需求。所以我写了我自己的,如上图所示。我需要的功能:

  • 只传递步数和总步数,它完成了计算完成百分比的艰巨工作。
  • 仅使用 60 个字符空间,将它们分成 240 个“刻度”,以提供从 0.25 % 到 100% 的更精细的显示。
  • 支持添加标题。
  • 可选百分比在行尾完成。
  • 可变长度进度条,默认为 60 个字符或 240 个“记号”。

如何调用进度显示

调用进度显示非常简单。对于上面的示例.gif,该函数是使用以下方法调用的:

percent_complete(step, total_steps, title="Convert Markdown")

在 CSV 格式的 Stack Exchange 数据转储中,len(rows)total_steps 约为 2,500。 step 是当前行号,因为每个 Stack Exchange Markdown Q&A 都转换为 Kramdown(用于 GitHub Pages)。

Python 代码

代码很简单,但比其他答案长一点:

def percent_complete(step, total_steps, bar_width=60, title="", print_perc=True):
    import sys

    fill = "▒"                      # Fill up to bar_width
    utf_8s = ["▉", "▎", "▌", "▊"]   # UTF-8 left blocks: 7/8, 1/4, 1/2, 3/4
    perc = 100 * float(step) / float(total_steps)
    max_ticks = bar_width * 4
    num_ticks = int(round(perc / 100 * max_ticks))
    full_ticks = num_ticks / 4      # Number of full blocks
    part_ticks = num_ticks % 4      # Size of partial block (array index)

    disp = bar = ""                 # Blank out variables
    bar += utf_8s[0] * full_ticks   # Add full blocks into Progress Bar

    # If part_ticks is zero, then no partial block, else append part char
    if part_ticks > 0:
        bar += utf_8s[part_ticks]

    # Pad Progress Bar with fill character
    bar += fill * int((max_ticks/4 - float(num_ticks)/4.0))

    if len(title) > 0:
        disp = title + ": "         # Optional title to progress display

    disp += bar                     # Progress bar to progress display
    if print_perc:
        # If requested, append percentage complete to progress display
        if perc > 100.0:
            perc = 100.0            # Fix "100.04 %" rounding error
        disp += " {:6.2f}".format(perc) + " %"

    # Output to terminal repetitively over the same line using '\r'.
    sys.stdout.write("\r" + disp)
    sys.stdout.flush()

Python 代码注释

几点:

  • [ .... ] 方括号占位符不是必需的,因为存在用于相同目的的填充字符。这样可以节省两个额外字符以使进度条更宽。
  • bar_width 关键字参数可以根据屏幕宽度使用。 60 的默认值似乎适合大多数用途。
  • print_perc=True 关键字参数默认值可以通过在调用函数时传递print_perc=False 来覆盖。这将允许更长的进度条。
  • title="" 关键字参数默认为无标题。如果您希望使用一次,title="My Title": 将自动添加到其中。
  • 当您的程序完成后,记得调用sys.stdout.write("\r") 后跟sys.stdout.flush() 以清除进度显示行。

总结

这个答案比其他答案长一点,但重要的是要注意它是一个完整的解决方案,而不是您需要添加更多代码的解决方案的一部分。

另一点是这个解决方案没有依赖项,也没有额外的安装。 Python 支持 UTF-8 字符集,gnome-terminal 不需要额外的设置。如果您使用的是 Python 2.7,则可能需要在 shebang 之后使用# -*- coding: utf-8 -*-。 IE 作为程序的第二行。

该函数可以转换为具有单独 initupdatepause(用于将调试内容打印到屏幕)、resumeclose 方法的类。

此函数是从 bash 脚本转换而来的。每当电视音量发生变化时,bash 脚本都会以libnotify-bin(弹出气泡消息)显示索尼电视音量。如果您对 bash 版本感兴趣,请在下面发表评论。

【讨论】:

    【解决方案2】:
    import progressbar
    import time
    
    # Function to create  
    def animated_marker():
        widgets = ['Loading: ', progressbar.Bar('=', '[', ']', '-'), progressbar.Percentage()]
        bar = progressbar.ProgressBar(max_value=200,widgets=widgets).start() 
          
        for i in range(200): 
            time.sleep(0.1)
            bar.update(i+1)
        bar.finish()
    
    # Driver's code 
    animated_marker()
    

    【讨论】:

      【解决方案3】:

      做纯python,不做系统调用:

      from time import sleep
      
      for i in range(21):
          spaces = " " * (20 - i)
          percentage = 5*i
          print(f"\r[{'='*i}{spaces}]{percentage}%", flush=True, end="")
          sleep(0.25)
      

      【讨论】:

        【解决方案4】:

        对于 Python 3.6,我可以通过以下方式更新输出 inline

        for current_epoch in range(10):
            for current_step) in range(100):
                print("Train epoch %s: Step %s" % (current_epoch, current_step), end="\r")
        print()
        

        【讨论】:

          【解决方案5】:

          根据 Steven C. Howell 对 Mark Rushakoff's answer 的评论

          j = (i + 1) / n
          stdout.write('\r')
          stdout.write('[%-20s] %d%%' % ('='*int(20*j), 100*j))
          stdout.flush()
          

          其中i 是当前项目,n 是项目总数

          【讨论】:

            【解决方案6】:
            def printProgressBar(value,label):
                n_bar = 40 #size of progress bar
                max = 100
                j= value/max
                sys.stdout.write('\r')
                bar = '█' * int(n_bar * j)
                bar = bar + '-' * int(n_bar * (1-j))
            
                sys.stdout.write(f"{label.ljust(10)} | [{bar:{n_bar}s}] {int(100 * j)}% ")
                sys.stdout.flush()
            

            呼叫:

            printProgressBar(30,"IP")
            

            知识产权 | [████████████----------------------------] 30%

            【讨论】:

              【解决方案7】:

              这是我使用@Mark-Rushakoff 的解决方案所做的。自适应调整终端宽度。

              from time import sleep
              import os
              import sys
              from math import ceil
              
              l = list(map(int,os.popen('stty size','r').read().split()))
              col = l[1]
              col = col - 6
              
              for i in range(col):
                  sys.stdout.write('\r')
                  getStr = "[%s " % ('='*i)
                  sys.stdout.write(getStr.ljust(col)+"]"+"%d%%" % (ceil((100/col)*i)))
                  sys.stdout.flush()
                  sleep(0.25)
              print("")
              

              【讨论】:

                【解决方案8】:

                进一步改进,使用函数作为:

                import sys
                
                def printProgressBar(i,max,postText):
                    n_bar =10 #size of progress bar
                    j= i/max
                    sys.stdout.write('\r')
                    sys.stdout.write(f"[{'=' * int(n_bar * j):{n_bar}s}] {int(100 * j)}%  {postText}")
                    sys.stdout.flush()
                

                调用示例:

                total=33
                for i in range(total):
                    printProgressBar(i,total,"blah")
                    sleep(0.05)  
                

                输出:

                [================================================  ] 96%  blah  
                

                【讨论】:

                • 完美!并且不需要任何模块
                【解决方案9】:

                Mark Rushakoff's solution 中所述,您可以输出回车符sys.stdout.write('\r'),将光标重置到行首。为了推广该解决方案,同时实现Python 3's f-Strings,您可以使用

                from time import sleep
                import sys
                
                n_bar = 50
                iterable = range(33)  # for demo purposes
                n_iter = len(iterable)
                for i, item in enumerate(iterable):
                    j = (i + 1) / n_iter
                
                    sys.stdout.write('\r')
                    sys.stdout.write(f"[{'=' * int(n_bar * j):{n_bar}s}] {int(100 * j)}%")
                    sys.stdout.flush()
                
                    sleep(0.05)  
                    # do something with <item> here
                

                【讨论】:

                  【解决方案10】:

                  使用@Mark-Rushakoff 答案,我制定了一种更简单的方法,无需调用 sys 库。它适用于 Python 3。在 Windows 中测试:

                  from time import sleep
                  for i in range(21):
                      # the exact output you're looking for:
                      print ("\r[%-20s] %d%%" % ('='*i, 5*i), end='')
                      sleep(0.25)
                  

                  【讨论】:

                  • the answer to a different question 中所述,print 是一个瘦包装器,用于格式化输入并调用给定对象的写入函数。默认情况下,此对象为sys.stdout
                  【解决方案11】:

                  这里你可以使用以下代码作为函数:

                  def drawProgressBar(percent, barLen = 20):
                      sys.stdout.write("\r")
                      progress = ""
                      for i in range(barLen):
                          if i < int(barLen * percent):
                              progress += "="
                          else:
                              progress += " "
                      sys.stdout.write("[ %s ] %.2f%%" % (progress, percent * 100))
                      sys.stdout.flush()
                  

                  使用 .format:

                  def drawProgressBar(percent, barLen = 20):
                      # percent float from 0 to 1. 
                      sys.stdout.write("\r")
                      sys.stdout.write("[{:<{}}] {:.0f}%".format("=" * int(barLen * percent), barLen, percent * 100))
                      sys.stdout.flush()
                  

                  【讨论】:

                  • 对我来说,第一行不会被光标修改 - 只有第二行会。因此,进度条获得了多行,例如] percent * 100 %
                  • 第二个功能很好,但我必须补充:if percent == 1: print('')
                  【解决方案12】:

                  最简单的还是

                  import sys
                  total_records = 1000
                  for i in range (total_records):
                      sys.stdout.write('\rUpdated record: ' + str(i) + ' of ' + str(total_records))
                      sys.stdout.flush()
                  

                  关键是将整数类型转换为字符串。

                  【讨论】:

                    【解决方案13】:

                    试试 PyProg。 PyProg 是一个用于 Python 的开源库,用于创建超级可定制的进度指示器和条形图。

                    目前是 1.0.2 版本;它托管在 Github 上并在 PyPI 上可用(链接如下)。它与 Python 3 & 2 兼容,也可以与 Qt 控制台一起使用。

                    它真的很容易使用。以下代码:

                    import pyprog
                    from time import sleep
                    
                    # Create Object
                    prog = pyprog.ProgressBar(" ", " ", total=34, bar_length=26, complete_symbol="=", not_complete_symbol=" ", wrap_bar_prefix=" [", wrap_bar_suffix="] ", progress_explain="", progress_loc=pyprog.ProgressBar.PROGRESS_LOC_END)
                    # Update Progress Bar
                    prog.update()
                    
                    for i in range(34):
                        # Do something
                        sleep(0.1)
                        # Set current status
                        prog.set_stat(i + 1)
                        # Update Progress Bar again
                        prog.update()
                    
                    # Make the Progress Bar final
                    prog.end()
                    

                    会产生你想要的(甚至是条的长度!):

                    [===========               ] 45%
                    [===============           ] 60%
                    [==========================] 100%
                    

                    有关自定义进度条的更多选项,请转到本网站的 Github 页面。

                    我实际上制作了 PyProg,因为我需要一个简单但超级可定制的进度条库。您可以通过以下方式轻松安装它:pip install pyprog

                    PyProg Github:https://github.com/Bill13579/pyprog
                    PyPI:https://pypi.python.org/pypi/pyprog/

                    【讨论】:

                      【解决方案14】:

                      如果你正在开发命令行界面,我建议你看一下click,非常不错:

                      import click
                      import time
                      
                      for filename in range(3):
                          with click.progressbar(range(100), fill_char='=', empty_char=' ') as bar:
                              for user in bar:
                                  time.sleep(0.01)
                      

                      这是你得到的输出:

                      $ python test.py
                        [====================================]  100%
                        [====================================]  100%
                        [=========                           ]   27%
                      

                      【讨论】:

                        【解决方案15】:

                        您可以从PyPI 获得一个名为progressbar 的Python 模块,该模块实现了此类功能。如果您不介意添加依赖项,这是一个很好的解决方案。否则,请选择其他答案之一。

                        一个简单的使用例子:

                        import progressbar
                        from time import sleep
                        bar = progressbar.ProgressBar(maxval=20, \
                            widgets=[progressbar.Bar('=', '[', ']'), ' ', progressbar.Percentage()])
                        bar.start()
                        for i in xrange(20):
                            bar.update(i+1)
                            sleep(0.1)
                        bar.finish()
                        

                        要安装它,你可以使用easy_install progressbar,如果你喜欢pip,你可以使用pip install progressbar

                        【讨论】:

                        • 所有答案都很棒,但是,我最喜欢模块。谢谢大家。
                        • 遗憾的是,progressbarfish 似乎都不适用于 python3。
                        • 此模块已超过 2 年未更新。不要将它用于新软件!
                        • 安装:sudo -H pip install progressbar2.
                        【解决方案16】:

                        这是一种非常简单的方法,可用于任何循环。

                        #!/usr/bin/python
                        for i in range(100001):
                            s =  ((i/5000)*'#')+str(i)+(' %')
                            print ('\r'+s),
                        

                        【讨论】:

                        • “#”有什么作用?
                        • @unseen_rider 这里的 '#' 只是一个符号,会随着循环迭代的增加而重复。
                        【解决方案17】:

                        我今天遇到了这个帖子,并在尝试了 Mark Rushakoff 的这个解决方案之后

                        from time import sleep
                        import sys
                        
                        for i in range(21):
                        sys.stdout.write('\r')
                        # the exact output you're looking for:
                        sys.stdout.write("[%-20s] %d%%" % ('='*i, 5*i))
                        sys.stdout.flush()
                        sleep(0.25)
                        

                        我可以说这在带有 python 3.4.3 64 位的 W7-64 上运行良好,但只能在本机控制台中运行。但是,当使用 spyder 3.0.0dev 的内置控制台时,换行符仍然/再次存在。由于这花了我一些时间来弄清楚,我想在这里报告这个观察结果。

                        【讨论】:

                          【解决方案18】:

                          我找到了有用的库 tqdm(https://github.com/tqdm/tqdm/,以前:https://github.com/noamraph/tqdm)。它会自动估计完成时间,并可用作迭代器。

                          用法:

                          import tqdm
                          import time
                          
                          for i in tqdm.tqdm(range(1000)):
                              time.sleep(0.01)
                              # or other long operations
                          

                          结果:

                          |####------| 450/1000  45% [elapsed: 00:04 left: 00:05, 99.15 iters/sec]
                          

                          tqdm 可以包装任何可迭代对象。

                          【讨论】:

                          • 哇,这个 tqdm 真是太棒了,而且很容易使用:)。
                          • 这是最简单的解决方案
                          • 它也与 Anaconda 发行版捆绑在一起! (至少对于python 3.5及以上)
                          • 谢谢,太棒了,简单有效
                          【解决方案19】:

                          基于此处和其他地方的一些答案,我编写了这个简单的函数,它显示一个进度条和经过/估计的剩余时间。应该可以在大多数基于 unix 的机器上工作。

                          import time
                          import sys
                          
                          percent = 50.0
                          start = time.time()
                          draw_progress_bar(percent, start)
                          
                          
                          def draw_progress_bar(percent, start, barLen=20):
                          sys.stdout.write("\r")
                          progress = ""
                          for i in range(barLen):
                              if i < int(barLen * percent):
                                  progress += "="
                              else:
                                  progress += " "
                          
                          elapsedTime = time.time() - start;
                          estimatedRemaining = int(elapsedTime * (1.0/percent) - elapsedTime)
                          
                          if (percent == 1.0):
                              sys.stdout.write("[ %s ] %.1f%% Elapsed: %im %02is ETA: Done!\n" % 
                                  (progress, percent * 100, int(elapsedTime)/60, int(elapsedTime)%60))
                              sys.stdout.flush()
                              return
                          else:
                              sys.stdout.write("[ %s ] %.1f%% Elapsed: %im %02is ETA: %im%02is " % 
                                  (progress, percent * 100, int(elapsedTime)/60, int(elapsedTime)%60,
                                   estimatedRemaining/60, estimatedRemaining%60))
                              sys.stdout.flush()
                              return
                          

                          【讨论】:

                            【解决方案20】:

                            基于上述答案和其他关于 CLI 进度条的类似问题,我想我对所有这些问题都有一个普遍的共同答案。在https://stackoverflow.com/a/15860757/2254146查看它

                            这是该函数的副本,但已根据您的风格进行了修改:

                            import time, sys
                            
                            # update_progress() : Displays or updates a console progress bar
                            ## Accepts a float between 0 and 1. Any int will be converted to a float.
                            ## A value under 0 represents a 'halt'.
                            ## A value at 1 or bigger represents 100%
                            def update_progress(progress):
                                barLength = 20 # Modify this to change the length of the progress bar
                                status = ""
                                if isinstance(progress, int):
                                    progress = float(progress)
                                if not isinstance(progress, float):
                                    progress = 0
                                    status = "error: progress var must be float\r\n"
                                if progress < 0:
                                    progress = 0
                                    status = "Halt...\r\n"
                                if progress >= 1:
                                    progress = 1
                                    status = "Done...\r\n"
                                block = int(round(barLength*progress))
                                text = "\rPercent: [{0}] {1}% {2}".format( "="*block + " "*(barLength-block), progress*100, status)
                                sys.stdout.write(text)
                                sys.stdout.flush()
                            

                            看起来像

                            百分比:[====================] 99.0%

                            【讨论】:

                            • 我没有显示进度条
                            • 我认为它不需要那么多小数位,即round(progress*100,2)
                            【解决方案21】:

                            您可以使用\r (carriage return)。演示:

                            import sys
                            total = 10000000
                            point = total / 100
                            increment = total / 20
                            for i in xrange(total):
                                if(i % (5 * point) == 0):
                                    sys.stdout.write("\r[" + "=" * (i / increment) +  " " * ((total - i)/ increment) + "]" +  str(i / point) + "%")
                                    sys.stdout.flush()
                            

                            【讨论】:

                            • 尝试total10,然后您会收到错误消息ZeroDivisionError: long division or modulo by zero
                            【解决方案22】:

                            '\r' 字符(回车)将光标重置到行首,并允许您改写该行之前的内容。

                            from time import sleep
                            import sys
                            
                            for i in range(21):
                                sys.stdout.write('\r')
                                # the exact output you're looking for:
                                sys.stdout.write("[%-20s] %d%%" % ('='*i, 5*i))
                                sys.stdout.flush()
                                sleep(0.25)
                            

                            我不能 100% 确定这是否可以在所有系统上完全移植,但它至少可以在 Linux 和 OSX 上运行。

                            【讨论】:

                            • 这比标记的答案更好,因为它也适用于 Python 3。
                            • 如所写,代码并没有明确说明如何使其适应您正在迭代的任何大小(不是 21)。我会让n=21,用range(n) 替换range(21),然后在循环中添加j = (i + 1) / n,并用这个轻微的修改替换write 语句:sys.stdout.write("[%-20s] %d%%" % ('='*int(20*j), 100*j))。现在你需要做的唯一改变是在循环之前n=21(更可能是n=len(iterable)),然后枚举可迭代对象。我推荐了这个编辑,但被拒绝了;显然功能“偏离了帖子的初衷”。
                            • 自适应任意大小的 n,sys.stdout.write("[{:{}}] {:.1f}%".format("="*i, n-1, (100/(n-1)*i))),仅限 Python 3
                            • 什么是%-20s
                            • @StevenC.Howell 为什么不将其作为引用马克的答案发布?这是另一个版本,准备好非常有帮助
                            猜你喜欢
                            • 1970-01-01
                            • 1970-01-01
                            • 2014-12-12
                            • 2011-07-15
                            • 2015-04-15
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            • 1970-01-01
                            相关资源
                            最近更新 更多