Display Download Information¶
At the moment we are able to download a file. But we have no information on how fast our download is and if it is completed or there is some error.
Before we start, here is the tiny program we made previously if you need to refresh your mind:
import bitpit
#will download this
url = 'https://www.python.org/static/img/python-logo.png'
#this is our downloader
dl = bitpit.Downloader(url)
#start downloading and tell user download has started.
dl.start()
print('Download has started.')
#end of the main thread
Now it is time to make it better.
Display the file size¶
If we are downloading a file, we probably want to know the file size. bitpit is written in an event driven style. It is a little similar to GTK library if you have used it before. We need to do 2 steps to show the file size. First, we need to define a function that will be called when the file size is known:
def on_size_changed(downloader):
print(downloader.size)
This function takes 1 argument: downloader
which is the Downloader
instance that we just knew its file size. In the function, we print the
Downloader.size
property, which is just the file size in bytes.
Next, we need to tell the downloader to call this function as soon as it knows
the file size. You probably want to do this just before you start the download.
This is done using Downloader.listen()
method:
dl.listen('size-changed', on_size_changed)
The Downloader.listen()
takes at least 2 arguments. The first is the signal to
listen to. Here we listened to the size-changed
signal which is emitted
whenever the downloader gets to know the size of the file being downloaded. The
second argument is the function to call when the signal is emitted. Here we put
the function we defined above.
After this call to Downloader.listen()
, our function will be called as soon
as the file size is known. Our full program now becomes as follows:
import bitpit
def on_size_changed(downloader):
print('The file size is', downloader.size)
#will download this
url = 'https://www.python.org/static/img/python-logo.png'
#this is our downloader
dl = bitpit.Downloader(url)
#listen to signals
#print size as soon as it is known
dl.listen('size-changed', on_size_changed)
#start downloading and tell user download has started.
dl.start()
print('Download has started.')
#end of the main thread
If you notice, the size is expressed in bytes. Showing the size in bytes gives
us a very big number that is difficult for humans to read. It would be easier
for us if we could display the size in Kilobytes or Megabytes. This can be done
by modifying the callback function on_size_changed()
to be as follows:
def on_size_changed(downloader):
print('The file size is', *downloader.human_size)
We just replaced Downloader.size
property with Downloader.human_size
property. Downloader.human_size
property gives us a 2-element tuple. The
first element is a float representing the size and the second element is a
string suffix with the value KB for kilobytes or MB for megabytes and so on.
In our call to print()
function, we unpacked the tuple arguments using
python * operator. If you are not familiar with this, check it out in the python
here.
When I tried the new callback function, I got the following message printed:
The file size is 9.865234375 KB
We can use python string formatting to make it look better but we will leave it for later.
Display the download speed¶
Other than the size, we want to know the download speed. Similar to the size, we
define a callback function and listen to a signal. The function we will define
will print the speed just like the size. The property we will use is
Downloader.speed
. Also like the size, there is a Downloader.human_speed
.
We will use Downloader.human_speed
:
def on_speed_changed(downloader):
print('The speed is', *downloader.human_speed)
The signal we want to listen to this time is speed-changed
:
dl.listen('speed-changed', on_speed_changed)
The behaviour of speed-changed
signal is a little bit different than
size-changed
. When the download starts, the signal is emitted every 1 second
. It will keep being emitted periodically as long as the download is running. In
our program, the signal will not work very well because the file size is very
small. Try to download linux mint and you will see the signal working
properly.
There are other things we can do to improve our program regarding
speed-changed
signal. For example, we can show how much we have downloaded
so far in the callback function because we probably have downloaded something
since the last time the signal was emitted. We can check
Downloader.downloaded
and Downloader.human_downloaded
to know that.
Furthermore, our callback will be printing a message every second which makes
the terminal full of confusing text. We can make our output better. However, we
will leave it to the end of the tutorial. For now we will stick to what we have
done so far.
Now our program has become as follows:
import bitpit
def on_size_changed(downloader):
print('The file size is', downloader.size)
def on_speed_changed(downloader):
print('The speed is', *downloader.human_speed)
#will download this
url = 'https://www.python.org/static/img/python-logo.png'
#this is our downloader
dl = bitpit.Downloader(url)
#listen to signals
#print size as soon as it is known
dl.listen('size-changed', on_size_changed)
#print speed periodically
dl.listen('speed-changed', on_speed_changed)
#start downloading and tell user download has started.
dl.start()
print('Download has started.')
#end of the main thread
Just as a final note in this section, you can change the time between
speed-changed
signal emissions in Downloader.__init__()
when you create
the downloader instance by passing the desired number of seconds in the
update_period
argument. Check the class documentation for more details.
Display the download state¶
Another useful information we need in our download is its state. For example, did it start or not? Is it completed or still in progress? Did it stop normally or because of an error? This is what we are going to do.
Similar to the size and speed, we define a callback function and listen to a signal:
def on_state_changed(downloader, old_state):
print('The state changed to:', downloader.state)
dl.listen('state-changed', on_state_changed)
Notice that state-changed
signal takes at least 2 positional argumetns.
The Downloader
that changed state and the old state the downloader was on.
The state-changed
signal is emitted whenever the download is started,
stopped, or completed. To know the new state, check the Downloader.state
property. It can be one of the following:
* pause
: The download is not started or started then stopped by a calling
Downloader.stop()
method.
* start
: The download just started but is not download anything yet.
* download
: The download is running and in progress.
* error
: The download stopped bacause of an error.
* complete
: The download completed.
Our program now has become like this:
import bitpit
def on_size_changed(downloader):
print('The file size is', downloader.size)
def on_speed_changed(downloader):
print('The speed is', *downloader.human_speed)
def on_state_changed(downloader, old_state):
print('The state changed to:', downloader.state)
#will download this
url = 'https://www.python.org/static/img/python-logo.png'
#this is our downloader
dl = bitpit.Downloader(url)
#listen to signals
#print size as soon as it is known
dl.listen('size-changed', on_size_changed)
#print speed periodically
dl.listen('speed-changed', on_speed_changed)
#print state
dl.listen('state-changed', on_state_changed)
#start downloading and tell user download has started.
dl.start()
print('Download has started.')
#end of the main thread
In Automatic Restart, we will make our downloader automatically resume the download when the download is interrupted due to an error.