python模拟缓冲区进度条
实验步骤
点击【桌面连接】进入实验环境。
1 开发准备
1.1 打开编程IDE-Spyder
-
双击桌面上的【LXTerminal】打开终端
-
切换到root用户
1 | sudo su |
紧接着粘贴student用户的密码,方法参见4.1小节。
- 敲入如下命令启动Spyder:
1 | spyder |
- 将Spyder的工作目录设置为/root(默认为/root),如下图所示:
图1 Spyder工作目录
1.2 再打开一个新的终端
我们编写程序是在IDE(比如Spyder)下实现,但是我们运行程序一般都是在终端运行的,即命令行模式下运行。原因很简单,把程序部署在服务器后,程序总不能在Spyder里面跑,一定是在命令行模式下跑。
在本实验中,编写代码在Spyder环境下,运行程序在终端环境下。
再打开一个终端的步骤如下:
-
双击桌面上的【LXTerminal】打开终端
-
切换到root用户
1 | sudo su |
紧接着粘贴student用户的密码。
- 进入到代码所在目录
1 | cd /root |
后面我们称这个终端叫新打开的终端。
2 原理探究
我们都知道电影的原理,连续的画面是由一帧帧单独的图片构成的。动态进度条同理,即在同一行内连续播放不同的图案。要想在同一行内连续播放不同的图案,就必须知道如何在同一行内输出时覆盖掉之前存在的字符。下面我们一起来探究这个问题。
2.1 多行输出
使用Spyder新建一个Python文件,命名为example1.py。
print()函数是最常见的输出方式。
每隔0.3秒便print()一行数字,代码如下:
1 | import time |
按Ctrl+s组合键保存代码。
在新打开的终端中输入下面命令执行程序:
1 | python example1.py |
观察运行效果,可以下载附件中的1.mp4,查看运行效果是否一致。附件下载方式参考4.3小节。
代码输出了很多行。但是对于进度条,应该在1行输出。
5.2.2 改进–在同一行输出
使用Spyder新建一个Python文件,命名为example2.py。
print()函数的参数end控制着是否换行。
end的值默认是\n。替换为空字符即可不换行。
1 | import time |
按Ctrl+s组合键保存代码。
在新打开的终端中输入下面命令执行程序:
1 | python example2.py |
观察运行效果,可以下载附件中的2.mp4,查看运行效果是否一致。
现在所有的数字输出到了一行。
但我们很明显的发现,5个字符串是一次性同时输出的,和进度条的效果是不匹配的。
2.3 改进–打破一次性同时输出
使用Spyder新建一个Python文件,命名为example3.py。
因为Python中的标准输入输出默认是使用行缓冲的。而对于行缓冲来说,缓冲区读取到换行符 \n 或回车符 \r 时才会刷新缓冲区。所以,我们需要使用 sys.stdout.flush() 函数在每次迭代时刷新缓冲区。
1 | import time |
按Ctrl+s组合键保存代码。
在新打开的终端中输入下面命令执行程序:
1 | python example3.py |
观察运行效果,可以下载附件中的3.mp4,查看运行效果是否一致。
我们发现,现在已经不是一次性同时输出了。但是仍然有一个问题:字符串是在末尾追加,而不是把前面的字符覆盖。
2.4 改进–覆盖前面字符
使用Spyder新建一个Python文件,命名为example4.py。
显示进度条时,应该是让新输出的字符覆盖已存在的字符。为了解决这个问题,我们只需要使用一个字符:回车符 \r。回车符的作用是回到行首,这时我们新输出的字符将会覆盖同一行内已存在的字符。
1 | import time |
按Ctrl+s组合键保存代码。
在新打开的终端中输入下面命令执行程序:
1 | python example4.py |
观察运行效果,可以下载附件中的4.mp4,查看运行效果是否一致。
2.5 改进–简化代码
使用Spyder新建一个Python文件,命名为example5.py。
函数print()默认end=’\n’,而sys.stdout.write()默认sep=’ '。使用sys.stdout.write()更简洁一些。
1 | import time |
按Ctrl+s组合键保存代码。
在新打开的终端中输入下面命令执行程序:
1 | python example5.py |
输出结果和5.2.4一模一样。
2.6 简单的进度条
使用Spyder新建一个Python文件,命名为example6.py。
下面将运用上面的知识实现一个最简单的进度条,以此来深入理解进度条的工作原理。
1 | import time |
按Ctrl+s组合键保存代码。
在新打开的终端中输入下面命令执行程序:
1 | python example6.py |
3 编写进度条函数
使用Spyder新建一个Python文件,命名为progressbar.py。
为了让代码具有通用性,我们编写一个函数,叫show_process()。
1 | # -*- coding: utf-8 -*- |
接下来定义3个全局变量:
1)进度条的长度。进度条太长了不好看,太短了不好看。在这里我们定义50个字符的长度
2)已完成的进度符号。已完成的进度用#表示
3)未完成的进度符号。未完成的进度用-表示
这3个全局变量决定了进度条的效果如下图所示:
图17 进度条效果
定义3个全局变量的代码如下:
1 | COMPLETED_CHAR = '#' # 进度条已完成的符号 |
下面是进度条最难的一步:计算应该显示几个#,计算应该显示几个-,计算进度百分比。代码如下:
1 | num_completed_char = int(current_step * MAX_SYMBOL / max_step) #计算显示多少个COMPLETED_CHAR |
使用sys.stdout.write()函数将内容显示出来,代码如下:
1 | sys.stdout.write('\r') |
1 | sys.stdout.write('\r') |
如果完成了,输出一个标识,代码如下:
1 | if current_step >= max_step: |
进度条的函数完成后,我们来模拟一个场景。
下载150张图片;每下载一张图片需要0.2秒钟。
代码如下:
1 | # 场景:下载150张图片;每下载一张图片需要0.2秒钟。 |
按Ctrl+s组合键保存代码。
在新打开的终端中输入下面命令执行程序:
1 | python progressbar.py |
progressbar.py的完整代码如下:
1 | # -*- coding: utf-8 -*- |
4 实验总结
本实验掌握了命令行动态进度条的原理,并加深了对输出缓冲区、Python函数的理解。