非常棒的进度条

在ftp项目编写上传下载文件时候会用到进度条功能,以便客户机直观的获取文件传输状态信息。python已经由第三方tqdm库供我们调用了,但是这么一个简单的功能调用一个200多k的第三方模块让我觉得相当浪费,不如自己写一个吧!

需求:

require1>>>文件传输中,每当文件大小状态获得更新时,我需要在屏幕的进度条上得到实时反馈

require2>>>我希望我的进度条能在一行展示,而不是铺满整个屏幕

solve1:

第一个需求让我想到生产者消费者模型,只是这里只有一个生产者和一个消费者

solve2:

print方法默认结束符end='\n',即下次打印会换行:

import time


for i in range(5):
    time.sleep(0.2)
    print(i)
# 如下图,这种打印方式显然不符合要求

而sys.stdout.write没有end一说,下次打印不会换行,更符合需求

import sys
import time


for i in range(10):
    time.sleep(0.2)
    sys.stdout.write(str(i))
# 如下图,这种方式能实现单行打印,但是为了
提高效率,sys.stdout.write方法会等所有数据缓存满了再一次性输出,

 

解决办法:sys.stdout.flush(),每来一次数据就强制输出一次,而不是默认所有数据一起输出

import sys
import time


for i in range(10):
    time.sleep(0.2)
    sys.stdout.write(str(i))
    sys.stdout.flush()
# 还是有问题:每次打印我只要从开始位置显示当前数据,之前数据不显示

解决办法:在每次打印数据前加 '\r' 表示从当前行起始位置打印

import sys
import time


for i in range(10):
    time.sleep(0.2)
    sys.stdout.write('\r' + str(i))
    sys.stdout.flush()
# 如下图,这样就比较完美了

 

最终实现代码

code1:

# -*- coding:utf-8 -*-
# Author: Tarantiner
# @Time :2019/3/26 11:53

import time
import sys
import queue
from threading import Thread

total_size = 36842  # 模拟文件总大小
rec_size = 0  # 模拟接收到文件大小
MAX = 50  # 进度条中显示最大方块数量


def fetcher():
    # 实现进度条打印功能
    while True:
        k = q.get()
        if k is None:
            print(' Done!')
            break
        sys.stdout.write('\r' + '|' + '▇' * k + '|' + str(int(k*100/MAX)) + '%')


def producer(q):
    global rec_size
    count = 0
    while rec_size < total_size:
        time.sleep(0.1)
        rec_size += 1024  # 模拟获取数据
        count = int(rec_size * MAX / total_size)
        q.put(count)
    q.put(count)
    q.put(None) # 发送结束信号


if __name__ == '__main__':
    q = queue.Queue()
    f = Thread(target=fetcher)
    f.start()
    producer(q)

输出:

想想code1中为单个生产者消费者开辟一个子线程,有点不值,于是又想到了生成器,实现单个线程下程序级别的切换,开销更小,于是就有了code2

code2

# -*- coding:utf-8 -*-
# Author: Tarantiner
# @Time :2019/3/26 12:25

import time


total_size = 100
rec_size = 0
count = 0


def func():
    while True:
        k = yield
        # sys.stdout.write只是print的一种特定格式,那么print当然能实现sys.stdout.write功能
        print('\r' + '|' + '▇'*k + '|' + str(k) + '%', end='', flush=True)


f = func()
next(f)
while rec_size < total_size:
    f.send(count)
    time.sleep(0.2)
    rec_size += 2
    count = int(rec_size * 50 / total_size)
f.send(count)

至此,进度条问题就完美解决了,当然我会推荐code2使用生成器方式。

posted on 2019-03-26 14:24  Tarantino  阅读(169)  评论(0编辑  收藏  举报

导航