进度条最主要的问题就是所有字符全部在同一行,而且可以修改。然而当执行print语句的时候,python会在打印完这个语句的同时,在结尾加上换行‘ ’,这就导致在控制台下一旦被print之后就无法修改了。
我们需要使用的是来自sys.stdout.write()函数,这个函数会在控制台输出这个字符串的同时不加上任何结尾,这就意味着这个输出还没有完全结束。通过sys.stdout.flush()函数可以把输出暂时打印在控制台中(造成print的假象)。那么我们用‘ ’来回到行首。一切看起来那么简单了。
也就是说:打印字符串的时候,没有加上' ',同时让光标回到行首,再把当前缓冲区显示出来,也就好像是print了一样,但是这时候光标还在原来位置。
举个例子:
import sys, time for i in range(10): sys.stdout.write('{0}/10 '.format(i + 1)) sys.stdout.flush() time.sleep(1)
在终端下执行这段代码就会得到简单的进度效果。
接下来需要解决两个问题:
一、清空缓冲区:
当新的字符串比之前短的时候会出现问题,如下:
import sys, time for i in range(10): sys.stdout.write(str(i) * (10 - i) + ' ') sys.stdout.flush() time.sleep(1)
运行后会发现,结果跟期望的不一样。
其实是因为已经被flush出去的字符并不会主动清空,所以只有新写入的被修改了。针对这点,我目前的解决方案是先输出一波空格,把之前的字符串冲掉,然后重写:
import sys, time for i in range(10): sys.stdout.write(str(i) * (10 - i) + ' ') sys.stdout.flush() time.sleep(1)
二、固定底边输出:
有时候我们希望在进度条加载的同时,还有一些其他输出。不妨在刷新掉上一次输出之后输出所需输出的字符串,然后在假输出进度条。如下:
import sys, time for i in range(10): sys.stdout.write(' ' * 10 + ' ') sys.stdout.flush() print(i) sys.stdout.write(str(i) * (10 - i) + ' ') sys.stdout.flush() time.sleep(1)
以后也可以给出一个自己实现的类来打印进度条:
class Process(object): def __init__(self, id, wide=20): self.id = id self.wide = wide def log(self, line): info = self.id*line + '{0}%'.format(int(line/self.wide*100)) + ' ' sys.stdout.write(info) sys.stdout.flush() time.sleep(0.5) obj = Process('>>') for i in range(20): obj.log(i)
1 import sys, time 2 3 4 class ProgressBar: 5 def __init__(self, count=0, total=0, width=50): 6 self.count = count 7 self.total = total 8 self.width = width 9 10 def move(self): 11 self.count += 1 12 13 def log(self, s): 14 sys.stdout.write(' ' * (self.width + 9) + ' ') 15 sys.stdout.flush() 16 print(s) 17 progress = self.width * self.count / self.total 18 sys.stdout.write('{0:3}/{1:3}: '.format(self.count, self.total)) 19 sys.stdout.write('#' * int(progress) + '-' * int(self.width - progress) + ' ') 20 if progress == self. 21 sys.stdout.write(' ') 22 sys.stdout.flush() 23 24 25 bar = ProgressBar(total=10) 26 for i in range(10): 27 bar.move() 28 bar.log('We have arrived at: ' + str(i + 1)) 29 time.sleep(1)