I'm trying to build an app that downloads a file and updates a wx.Gauge in real time with the download progress. Additionally, I would like to show the current downloaded size / total size and a real time updating download speed.
In my minimal code example, the progress variable, which would have a value between 0 and 100, is set to perc_value. The size and current speed text is set to progress_text.
I tried using tqdm, but their examples shows their own implementation of progress bar and speed. I couldn't find a variable/function from their library to give me those info.
Can you help me? Thank you!
import wx
import requests
from threading import Thread
class DownloadThread(Thread):
def __init__(self, parent, url):
Thread.__init__(self)
self.parent = parent
self.url = url
self.start()
def run(self):
perc_value = 0
progress_text = ''
with open('file', 'wb') as f:
r = requests.get(self.url, stream=True)
for chunk in r.iter_content(4096):
f.write(chunk)
wx.CallAfter(self.parent.update_gauge, perc_value, progress_text)
class Main(wx.Frame):
def __init__(self, parent):
super().__init__(parent)
sizer = wx.BoxSizer(wx.VERTICAL)
self.gauge = wx.Gauge(self, -1)
self.progress_t = wx.StaticText(self, -1, "Testing")
sizer.Add(self.gauge, flag=wx.ALL, border=10)
sizer.Add(self.progress_t, flag=wx.ALL, border=10)
self.SetSizerAndFit(sizer)
self.start_download()
def start_download(self):
url = '.../file.zip'
DownloadThread(self, url)
def update_gauge(self, perc_value, progress_text):
self.gauge.SetValue(perc_value)
self.progress_t.SetLabel(progress_text)
app = wx.App()
Main(None).Show()
app.MainLoop()
Edit: I have made some improvements. I found the best solution is not using tqdm. My only problem now is the download speed, which is inaccurate.
Edit2: Solved the problem. The corrected version is below!
import wx
import requests
from threading import Thread
class DownloadThread(Thread):
def __init__(self, parent, url):
Thread.__init__(self)
self.parent = parent
self.url = url
self.start()
def run(self):
perc_temp = 0
r = requests.get(self.url, stream=True)
self.total_length = r.headers.get('content-length')
with open('file.data', 'wb') as f:
if self.total_length is None: # no content length header
f.write(r.content)
else:
self.dl = 0
self.dl_temp = 0
self.total_length = int(self.total_length)
self.total_size = self.get_downloaded_value(self.total_length)
self.total_unit = self.get_unit(self.total_length)
print(self.total_length)
for data in r.iter_content(chunk_size=4096):
self.dl += len(data)
self.dl_temp += len(data)
f.write(data)
value = int(100 * self.dl / self.total_length)
if perc_temp != value:
perc_temp = value
wx.CallAfter(self.parent.update_gauge, value)
def get_progress_text(self):
downloaded = self.get_downloaded_value(self.dl)
unit_downloaded = self.get_unit(self.dl)
speed = self.get_downloaded_value(self.dl_temp)
speed_unit = self.get_unit(self.dl_temp)
text = f"{downloaded:.2f} {unit_downloaded} / {self.total_size:.2f} {self.total_unit}\t\t{speed:.2f} {speed_unit}/s"
self.dl_temp = 0
return text
def get_unit(self, size):
unit = ''
if size < 1024:
unit = 'B'
elif size < 1048576:
unit = 'KB'
else:
unit = 'MB'
return unit
def get_downloaded_value(self, size):
if size < 1024:
return size
elif size < 104876:
return size / 1024
else:
return size / 1048576
class Main(wx.Frame):
def __init__(self, parent):
super().__init__(parent)
sizer = wx.BoxSizer(wx.VERTICAL)
self.timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.OnTimer)
self.gauge = wx.Gauge(self, -1)
self.progress_t = wx.StaticText(self, -1, "Testing", size=(300, 23))
sizer.Add(self.gauge, flag=wx.ALL | wx.EXPAND, border=10)
sizer.Add(self.progress_t, flag=wx.ALL | wx.EXPAND, border=10)
self.SetSizerAndFit(sizer)
self.start_download()
def start_download(self):
url = 'https://portal.inmet.gov.br/uploads/dadoshistoricos/2008.zip'
self.thread = DownloadThread(self, url)
self.timer.Start(1000)
def update_gauge(self, perc_value):
self.gauge.SetValue(perc_value)
def OnTimer(self, event):
self.progress_t.SetLabel(self.thread.get_progress_text())
app = wx.App()
Main(None).Show()
app.MainLoop()