Search code examples
pythonpython-3.xformatprogress-bartransfer

How to display mb/s instead of b/s in progressbar and Python?


I am trying to change the way progressbar (version 2) displays the transferred/total file size in Python 3. Here's the code:

import progressbar
import requests

url = 'http://download.openbricks.org/sample/H264/big_buck_bunny_1080p_H264_AAC_25fps_7200K_short.MP4'

request = requests.get(url, stream=True)

file         = open('test.mp4', 'wb')
file_size    = int(request.headers['Content-Length'])
file_size_mb = round(file_size / 1024 / 1024,2)

chunk_sz = 512

widgets = [progressbar.Bar(marker="#",left="[",right="]"),
           progressbar.Percentage()," | ",
           progressbar.FileTransferSpeed()," | ",
           progressbar.SimpleProgress()," | ",
           progressbar.ETA()]

bar = progressbar.ProgressBar(widgets=widgets, maxval=file_size).start()

i = 0

for chunk in request.iter_content(chunk_size=chunk_sz):
    file.write(chunk)
    i += len(chunk)
    bar.update(i)

bar.finish()
print('File size: {0} MB'.format(file_size_mb))

file.close()

The output:

[#########################] 91% | 741.3 KiB/s | 3397632 of 3714474 | Time: 00:00:08

File size: 3.54 MB

I want to be able to make the "3397632 of 3714474" be displayed in the MB format (like in the file_size_mb variable), not in Bytes as it is being displayed by progressbar by default.

I've read the docs in Progress Bar’s documentation but I couldn't find the answer to my question in any of the examples given there.


Solution

  • you can obtain this kind of display relatively easily with DataSize widget:

    [######                                 ] 17% | 256.0 KiB/s | 631.0 KiB /   3.5 MiB | ETA: 0:00:11
    

    you can do this by replacing the SimpleProgress widgets by two DataSize widgets, as shown below:

    widgets = [progressbar.Bar(marker="#",left="[",right="]"),
               progressbar.Percentage(), " | ",
               progressbar.FileTransferSpeed(), " | ",
               progressbar.DataSize(), " / ",
               progressbar.DataSize(variable="max_value"), " | ",
               progressbar.ETA()]
    

    By default, the DataSize widget displays the current value, but you can specify that you want it to display the max value by passing it "max_value" as variable parameter.

    The problem is that it is not documented and you have to inspect the code to find this information. So this could be changed one day and no more work with a later version of the module.

    Considering the DataSize doc, it seems you can alter the default format by passing a custom value to format parameter. The default format is

    format='%(scaled)5.1f %(prefix)s%(unit)s'
    

    I think you could get two decimal numbers by specifying this format:

    format='%(scaled)5.2f %(prefix)s%(unit)s'
    

    Concerning the displayed unit, the doc mentions the prefixes parameter which defaults to

    prefixes=('', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi', 'Yi')
    

    You could probably pass another tuple with the prefixes you want. But consider this article discussing about binary prefixes before doing that. If you use powers of 1024 to convert your units, and if you want to be exact, then you probably wants to use the IEC binary prefixes with the "i" character.


    An other, but longer, way to achieve what you want would be to derive the SimpleProgress class to make one which fits you needs.