Download progressbar for Python 3 - python

I need a progress to show during file download for Python 3.
I have seen a few topics on Stackoverflow, but considering that I'm a noob at programming and nobody posted a complete example, just fractions of it, or the one that I can make work on Python 3, none are good for me...
additional info:
ok, so i have this:
from urllib.request import urlopen
import configparser
#checks for files which need to be downloaded
print(' Downloading...')
file = urlopen(file_url)
#progress bar here
output = open('downloaded_file.py','wb')
output.write(file.read())
output.close()
os.system('downloaded_file.py')
script is run through python command line

There is urlretrieve() that downloads an url to a file and allows to specify a reporthook callback to report progess:
#!/usr/bin/env python3
import sys
from urllib.request import urlretrieve
def reporthook(blocknum, blocksize, totalsize):
readsofar = blocknum * blocksize
if totalsize > 0:
percent = readsofar * 1e2 / totalsize
s = "\r%5.1f%% %*d / %d" % (
percent, len(str(totalsize)), readsofar, totalsize)
sys.stderr.write(s)
if readsofar >= totalsize: # near the end
sys.stderr.write("\n")
else: # total size is unknown
sys.stderr.write("read %d\n" % (readsofar,))
urlretrieve(url, 'downloaded_file.py', reporthook)
Here's a GUI progress bar:
import sys
from threading import Event, Thread
from tkinter import Tk, ttk
from urllib.request import urlretrieve
def download(url, filename):
root = progressbar = quit_id = None
ready = Event()
def reporthook(blocknum, blocksize, totalsize):
nonlocal quit_id
if blocknum == 0: # started downloading
def guiloop():
nonlocal root, progressbar
root = Tk()
root.withdraw() # hide
progressbar = ttk.Progressbar(root, length=400)
progressbar.grid()
# show progress bar if the download takes more than .5 seconds
root.after(500, root.deiconify)
ready.set() # gui is ready
root.mainloop()
Thread(target=guiloop).start()
ready.wait(1) # wait until gui is ready
percent = blocknum * blocksize * 1e2 / totalsize # assume totalsize > 0
if quit_id is None:
root.title('%%%.0f %s' % (percent, filename,))
progressbar['value'] = percent # report progress
if percent >= 100: # finishing download
quit_id = root.after(0, root.destroy) # close GUI
return urlretrieve(url, filename, reporthook)
download(url, 'downloaded_file.py')
On Python 3.3 urlretrieve() has different reporthook interface (see issue 16409). To workaround it, you could access the previous interface via FancyURLopener:
from urllib.request import FancyURLopener
urlretrieve = FancyURLopener().retrieve
To update the progress bar within the same thread, you could inline urlretrieve() code:
from tkinter import Tk, ttk
from urllib.request import urlopen
def download2(url, filename):
response = urlopen(url)
totalsize = int(response.headers['Content-Length']) # assume correct header
outputfile = open(filename, 'wb')
def download_chunk(readsofar=0, chunksize=1 << 13):
# report progress
percent = readsofar * 1e2 / totalsize # assume totalsize > 0
root.title('%%%.0f %s' % (percent, filename,))
progressbar['value'] = percent
# download chunk
data = response.read(chunksize)
if not data: # finished downloading
outputfile.close()
root.destroy() # close GUI
else:
outputfile.write(data) # save to filename
# schedule to download the next chunk
root.after(0, download_chunk, readsofar + len(data), chunksize)
# setup GUI to show progress
root = Tk()
root.withdraw() # hide
progressbar = ttk.Progressbar(root, length=400)
progressbar.grid()
# show progress bar if the download takes more than .5 seconds
root.after(500, root.deiconify)
root.after(0, download_chunk)
root.mainloop()
download2(url, 'downloaded_file.py')

I think this piece of code can help you. I'm not quite sure it's exactly what you want. At least it should give you something to work on.
import tkinter
from tkinter import ttk
from urllib.request import urlopen
def download(event):
file = urlopen('http://www.python.org/')
output = open('downloaded_file.txt', 'wb')
lines= file.readlines()
i = len(lines)
for line in lines:
output.write(line)
pbar.step(100/i)
output.close()
file.close()
root = tkinter.Tk()
root.title('Download bar')
pbar = ttk.Progressbar(root, length=300)
pbar.pack(padx=5, pady=5)
btn = tkinter.Button(root, text="Download")
# bind to left mouse button click
btn.bind("<Button-1>", download)
btn.pack(pady=10)
root.mainloop()
This works, I've tried it.

Related

Tkinter application quits unexpectedly. error "Paattajahaku quit unexpectedly."

I'm relatively new to Python and this is my first application. The code works well when run though terminal and in Pycharm but when I packaged it using Py2app and tied launching it through the file created "Paattajahaku.app" I get the message "Paattajahaku quit unexpectedly." Does anyone know whats wrong?
Thanks!
import tkinter as tk
from tkinter import ttk
from tkinter import filedialog as fd
from bs4 import BeautifulSoup as bs
import requests
import pandas as pd
from time import sleep
from random import randint
root = tk.Tk()
root.title("Tkinter open file dialog")
root.resizable(False, False)
root.geometry("300x150")
titleslist = []
nameslist = []
fonrlist = []
df = pd.DataFrame(titleslist)
def select_file():
filetypes = (
("text files", "*txt",
"excel files", "*xlsx")
)
filename = fd.askopenfile(
title="open a file",
initialdir="/",
)
print("Please wait...")
for files in filename:
newfo_nr = files.strip().zfill(8)
fonrlist.append(newfo_nr)
sleep(randint(2, 4)) # sleep for a random time
url = f"https://www.asiakastieto.fi/yritykset/fi/{newfo_nr}/paattajat"
page = requests.get(url)
soup = bs(page.content, "html.parser")
names = soup.find_all(attrs={"data-title": "Nimi"})
titles = soup.find_all(attrs={"data-title": "Asema"})
for title in titles:
titleslist.append(title.text)
for name in names:
nameslist.append(name.text)
number_of_names = len(nameslist)
number_of_fo_nr = len(fonrlist)
sumfo = number_of_names - number_of_fo_nr
for i in range(sumfo):
if i < sumfo:
fonrlist.append(newfo_nr.strip())
if title == " ":
break
else:
continue
break
else:
continue
break
else:
continue
break
print("Files saved")
df = pd.DataFrame(list(zip(fonrlist, titleslist, nameslist))) # works
try:
# with block automatically closes file
with fd.asksaveasfile(mode='w', defaultextension=".xlsx") as file:
df.to_excel(file.name)
except AttributeError:
# if user cancels save, filedialog returns None rather than a file object, and the 'with' will raise an error
print("The user cancelled save")
def saveFile():
try:
# with block automatically closes file
with fd.asksaveasfile(mode='w', defaultextension=".xlsx") as file:
df.to_excel(file.name, index=False, header=False)
except AttributeError:
# if user cancels save, filedialog returns None rather than a file object, and the 'with' will raise an error
print("The user cancelled save")
open_button = ttk.Button(
root,
text="Open a file",
command=select_file
)
exit_button = ttk.Button(
root,
text="Exit",
command=root.destroy
)
open_button.pack(expand=True)
exit_button.pack(expand=True)
root.mainloop()

How can I make tkinter wait for a line of code to finish before continuing?

so I am making a program on tkinter that gets a response from a server and depending on the answer, it will change the background color, to either green for success or red for error, the problem is that I realized that when running the code, the windows.after() method doesn't wait till is done to continue and when I do the request for the server, it have to do it three times to check if the response is correct, and it is suppossed to change the window background color each time, but it is only doing it one time. And not only the background color changing fails, also I want to change a label's text when it is doing the request,but it does it really quick and I'm not able to diferentiate the changes, so the question is: how can I
How can I make the program wait until one line finishes running to go to the next one and not everything happens at the same time and so fast?
Here is a piece of my code, I removed the request part because I'm trying to solve this problem first:
# import gpiozero
# import picamera
import json
import requests
import tkinter as tk
with open("config.json") as file:
config = json.load(file)
ENDPOINT = config["ENDPOINT"]
USUARIO = config["USUARIO"]
ESTACION = config["ESTACION"]
TIEMPO_ESPERA = config["TIEMPO_ESPERA"]
PIN_RELE = config["PIN_RELE"]
PATH_SALIDA = ENDPOINT + "Salida.getTicket/" + ESTACION + "/" + USUARIO + "/"
barcode = ""
# RELAY = gpiozero.OutputDevice(PIN_RELE, active_high=True, initial_value=False)
# CAMERA = picamera.PiCamera()
def check_scan_barcode(event=None):
info_label.config(text = "Wait...")
barcode = barcode_entry.get()
barcode_entry.delete(0, "end")
for i in range(3):
response = get_request(ENDPOINT + barcode)
if response["data"] == "True":
success()
open_barrier()
else:
error()
info_label.config(text = "Scan barcode")
def get_request(url):
response = requests.get(url)
response.raise_for_status()
response = response.json()
return response
def normal():
window.configure(bg="white")
info_label.configure(bg="white")
def success():
window.configure(bg="green")
info_label.configure(bg="green")
window.after(1000, normal)
def error():
window.configure(bg="red")
info_label.configure(bg="red")
window.after(1000, normal)
def open_barrier(barcode):
# CAMERA.capture(f"/home/pi/Pictures{barcode}.jpg")
# RELAY.on()
# window.after(TIEMPO_ESPERA, RELAY.off)
pass
window = tk.Tk()
# window.attributes('-fullscreen', True)
info_label = tk.Label(window, text= "Scan barcode.", font=("Arial", 40))
info_label.pack()
barcode_entry = tk.Entry(window, width=50)
barcode_entry.bind('<Return>', check_scan_barcode)
barcode_entry.pack(expand=True)
barcode_entry.focus()
window.mainloop()

Python: Create new thread or process when button is clicked

In Python 3.6.4, what is a good approach for starting a new separate process or thread on every click of a button? I have written this code but it's not working the way I want it to.
from multiprocessing import process
import requests
import threading
from tkinter import *
def download():
name=entry2.get()
url=entry1.get()
r = requests.head(url)
if name:
file_name = name
else:
file_name = url.split('/')[-1]
try:
file_size = int(r.headers['content-length'])
part=file_size/4
start=0
end=part
except:
print ("Invalid URL")
return
print ('%s downloaded' % file_name)
def thread(url):
file_name=entry2.get()
r=requests.get(url)
data=r.content
with open('file_name','rb+')as fp:
data1=fp.read()
with open('file_name',"wb+") as fp:
data1=fp.write(data)
print("its working3")
if __name__=='__main__':
p=process(target=download,args=())
p.start()
p.join()
root=Tk()
frame=Frame(root,width=500,height=450,bg="lightpink")
url1=Label(frame,text="enter url here")
name=Label(frame,text="enter the name of the file")
url1.grid(row=0,sticky=E)
name.grid(row=1,sticky=E)
entry1=Entry(frame)
entry2=Entry(frame)
entry1.grid(row=0,column=1)
entry2.grid(row=1,column=1)
button1=Button(frame,text="download" ,command=download)
button1.grid(row=2,column=0)
button3=Button(frame,text="quit",command=frame.quit)
button3.grid(row=2,column=1)
frame.grid()
print("its working4")
root.mainloop()
Does this do the job? It uses the threading module rather than multiprocessing:
#from multiprocessing import process
from threading import Thread as process
import requests
import threading
from tkinter import *
def download():
name=entry2.get()
url=entry1.get()
r = requests.head(url)
if name:
file_name = name
else:
file_name = url.split('/')[-1]
try:
file_size = int(r.headers['content-length'])
part=file_size/4
start=0
end=part
except:
print ("Invalid URL")
return
print ('%s downloaded' % file_name)
def thread(url):
file_name=entry2.get()
r=requests.get(url)
data=r.content
with open('file_name','rb+')as fp:
data1=fp.read()
with open('file_name',"wb+") as fp:
data1=fp.write(data)
print("its working3")
root=Tk()
frame=Frame(root,width=500,height=450,bg="lightpink")
url1=Label(frame,text="enter url here")
name=Label(frame,text="enter the name of the file")
url1.grid(row=0,sticky=E)
name.grid(row=1,sticky=E)
entry1=Entry(frame)
entry2=Entry(frame)
entry1.grid(row=0,column=1)
entry2.grid(row=1,column=1)
button1=Button(frame,text="download" ,command=lambda: process (target = download).start ())
button1.grid(row=2,column=0)
button3=Button(frame,text="quit",command=root.destroy)
button3.grid(row=2,column=1)
frame.grid()
print("its working4")
root.mainloop()

How to add progress bar?

Is there a way to add progress bar in pytube? I don't know how to use the following method:
pytube.Stream().on_progress(chunk, file_handler, bytes_remaining)
My code:
from pytube import YouTube
# from pytube import Stream
from general import append_to_file
def downloader(video_link, down_dir=None):
try:
tube = YouTube(video_link)
title = tube.title
print("Now downloading, " + str(title))
video = tube.streams.filter(progressive=True, file_extension='mp4').first()
print('FileSize : ' + str(round(video.filesize/(1024*1024))) + 'MB')
# print(tube.streams.filter(progressive=True, file_extension='mp4').first())
# Stream(video).on_progress()
if down_dir is not None:
video.download(down_dir)
else:
video.download()
print("Download complete, " + str(title))
caption = tube.captions.get_by_language_code('en')
if caption is not None:
subtitle = caption.generate_srt_captions()
open(title + '.srt', 'w').write(subtitle)
except Exception as e:
print("ErrorDownloadVideo | " + str(video_link))
append_to_file('debug', format(e))
# FILESIZE print(tube.streams.filter(progressive=True, file_extension='mp4').first().filesize/(1024*1024))
You can also do like this without writing your own function.
code:
from pytube import YouTube
from pytube.cli import on_progress #this module contains the built in progress bar.
link=input('enter url:')
yt=YouTube(link,on_progress_callback=on_progress)
videos=yt.streams.first()
videos.download()
print("(:")
Call your progress function inside the Youtube class
yt = YouTube(video_link, on_progress_callback=progress_function)
This is your progress function
def progress_function(self,stream, chunk,file_handle, bytes_remaining):
size = stream.filesize
p = 0
while p <= 100:
progress = p
print str(p)+'%'
p = percent(bytes_remaining, size)
This computes the percentage converting the file size and the bytes remaining
def percent(self, tem, total):
perc = (float(tem) / float(total)) * float(100)
return perc
The callback function takes three arguments, not four: stream, chunk and bytes_remaining.
I know this is already answered, but I came across this and for me, the progress was counting down from 100 to 0. Since I wanted to fill a progress bar with the percentage value, I couldn't use this.
So I came up with this solution:
def progress_func(self, stream, chunk, file_handle,bytes_remaining):
size = self.video.filesize
progress = (float(abs(bytes_remaining-size)/size))*float(100))
self.loadbar.setValue(progress)
The loadbar is my Progress Bar from PyQt5.
Hope this helps someone.
This is something interesting!
We can emulate the download animation of linux with the following code:
def progress_function(chunk, file_handle, bytes_remaining):
global filesize
current = ((filesize - bytes_remaining)/filesize)
percent = ('{0:.1f}').format(current*100)
progress = int(50*current)
status = '█' * progress + '-' * (50 - progress)
sys.stdout.write(' ↳ |{bar}| {percent}%\r'.format(bar=status, percent=percent))
sys.stdout.flush()
yt_obj = YouTube('<<some youtube video URL>>', on_progress_callback=progress_function)
Output looks like:
↳ |██████████████████████████████████----------------| 68.4%
Have fun!!
Somewhat shorter option:
yt = YouTube(video_link, on_progress_callback=progress_function)
video = yt.streams.first() # or whatever
# Prints something like "15.555% done..."
def progress_function(stream, chunk, file_handle, bytes_remaining):
print(round((1-bytes_remaining/video.filesize)*100, 3), '% done...')
You can, of course, limit the progress output, for instance, to values like 10, 20, 30%... - just surround the print statement with the required if-clause.
Here is a bit advanced version
def on_progress(vid, chunk, bytes_remaining):
total_size = vid.filesize
bytes_downloaded = total_size - bytes_remaining
percentage_of_completion = bytes_downloaded / total_size * 100
totalsz = (total_size/1024)/1024
totalsz = round(totalsz,1)
remain = (bytes_remaining / 1024) / 1024
remain = round(remain, 1)
dwnd = (bytes_downloaded / 1024) / 1024
dwnd = round(dwnd, 1)
percentage_of_completion = round(percentage_of_completion,2)
#print(f'Total Size: {totalsz} MB')
print(f'Download Progress: {percentage_of_completion}%, Total Size:{totalsz} MB, Downloaded: {dwnd} MB, Remaining:{remain} MB')
yt.register_on_progress_callback(on_progress)
from pytube import Playlist
from pytube import YouTube
previousprogress = 0
def on_progress(stream, chunk, bytes_remaining):
global previousprogress
total_size = stream.filesize
bytes_downloaded = total_size - bytes_remaining
liveprogress = (int)(bytes_downloaded / total_size * 100)
if liveprogress > previousprogress:
previousprogress = liveprogress
print(liveprogress)
yt = YouTube('https://www.youtube.com/watch?v=4zqKJBxRyuo&ab_channel=SleepEasyRelax-KeithSmith')
yt.register_on_progress_callback(on_progress)
yt.streams.filter(only_audio=True).first().download()
You can add progress bar like this. ignore silly type error (if any)
pytube.request.default_range_size = 1048576 # this is for chunck size, 1MB size
yt = YouTube(url)
video = yt.streams.first()
video.download(<whatever>)
def progress_callback(stream, chunk, bytes_remaining):
size = video.filesize
progress = int(((size - bytes_remaining) / size) * 100)
print(progress)
# do call progress bar from GUI here
def complete_callback(stream, file_handle):
print("downloading finished")
# progress bar stop call from GUI here
yt.register_on_progress_callback(progress_callback)
yt.register_on_complete_callback(complete_callback)

Sending values from one python script to another to another

so i've got some values coming in from an Arduino to my Raspberry Pi via an RF transceiver (NRF24L01) and i can display the integers when the program runs (Python). Now i want to display those integer values in my GUI that i've written in a seperate python script. I'm having trouble doing so. I've tried importing them from the GUI but it isn't working and i couldn't figure out why..
So now i've gone with the option of writing the value into a text file in the transmission script and then attempting to read the value from the text file in the GUI script but it still dosent work completely.
Can anyone help me update the text file from the transmission script and read it from the GUI script? Can you write to a text file from one script and read the text file from another script at the same time?
ANY help will be greatly appreciated. Thanks!
ps. If iv'e missed out on anything that you need to know just ask. It's a little hard to explain everything!
GUI CODE
# -*- coding: utf-8 -*-
"""
Created on Sat Aug 6 20:05:30 2016
#author: s
"""
import sys
if sys.version_info[0] < 3:
import Tkinter as tk
else:
import tkinter as tk
def clear():
pass
def exit_():
root.quit()
root.withdraw()
#water_amount = 0
water_cost = 0
total_water_amount = 0
total_water_cost = 0
def show_data():
while True:
text_file = open("Output.txt", "r")
water_amount = text_file.readlines()
text_file.close()
tk.Label(root, text='Water Amount: ' + str(water_amount)).pack()
tk.Label(root, text='Water Cost: ' + str(water_cost)).pack()
separator = tk.Frame(height=2, bd=10, relief=tk.SUNKEN)
separator.pack(fill=tk.X, padx=5, pady=5)
tk.Label(root, text='Total Water Amount: ' + str(total_water_amount)).pack()
tk.Label(root, text='Total Water Cost: ' + str(total_water_cost)).pack()
separator = tk.Frame(height=2, bd=10, relief=tk.SUNKEN)
separator.pack(fill=tk.X, padx=5, pady=5)
#show_data()
def get_rate():
import random
for i in range(100):
flow_rate.append(random.randint(20, 60))
# print(flow_rate)
# def draw_plot(flow_rate):
# import matplotlib.pyplot as plt
# conda install matplotlib
# print(flow_rate)
# plt.plot(flow_rate, label='Flow rate ml/sec')
# plt.xlabel('Time(sec)')
# plt.ylabel('Flow Rate(ml)')
# plt.title("Flow Rate Chart")
#
# plt.legend()
# plt.show()
root = tk.Tk(className='Water')
flow_rate = []
get_rate()
show_data()
tk.Button(root, text='Clear', command=clear).pack(side='left')
tk.Button(root, text='Exit', command=exit_).pack(side='left')
#tk.Button(root, text='Draw', command=draw_plot(flow_rate)).pack_forget()
root.mainloop()
CODE RECEIVING VALUES
import RPi.GPIO as GPIO
from lib_nrf24 import NRF24
import time
import spidev
GPIO.setmode(GPIO.BCM)
pipes = [[0xE8, 0xE8, 0xF0, 0xF0, 0xE1], [0xF0, 0xF0, 0xF0, 0xF0, 0xE1]]
radio = NRF24(GPIO, spidev.SpiDev())
radio.begin(0,17)
radio.setPayloadSize(32) #can have maximum 32
radio.setChannel(0x76)
radio.setDataRate(NRF24.BR_1MBPS) #Slower since it is secure
radio.setPALevel(NRF24.PA_MIN) # Minimum to save battery
radio.setAutoAck(True)
radio.enableDynamicPayloads()
radio.enableAckPayload() #Acknowledgement Payload : Can verify if data received
radio.openReadingPipe(1, pipes[1])
radio.printDetails()
radio.startListening()
while True:
# Waits to recieve data, if no data is recieved then goes into sleep mode
while not radio.available(0):
time.sleep(1/100)
receivedMessage = []
#Populates the message
radio.read(receivedMessage, radio.getDynamicPayloadSize())
#-------------------------------------------------------
raw = int(receivedMessage[1]) * 256
total = raw + int(receivedMessage[0])
print ("total equals:" + str(int(total)))
text_file = open("Output.txt", "w")
text_file.write("%s" % total)
text_file.close()
You should try to combine the data-receiving code and data-displaying code with threading library.
In the while True loop in the data-receiving script, it should check for new result and notify the GUI thread somehow (eg. store it in a global variable and use the threading.Condition object), or change the GUI directly.
For example:
from tkinter import *
import threading
tk=Tk()
result=StringVar()
Label(tk,textvariable=result).pack()
def update_result():
import RPi.GPIO as GPIO
from lib_nrf24 import NRF24
import time
import spidev
GPIO.setmode(GPIO.BCM)
pipes = [[0xE8, 0xE8, 0xF0, 0xF0, 0xE1], [0xF0, 0xF0, 0xF0, 0xF0, 0xE1]]
radio = NRF24(GPIO, spidev.SpiDev())
radio.begin(0,17)
radio.setPayloadSize(32) #can have maximum 32
radio.setChannel(0x76)
radio.setDataRate(NRF24.BR_1MBPS) #Slower since it is secure
radio.setPALevel(NRF24.PA_MIN) # Minimum to save battery
radio.setAutoAck(True)
radio.enableDynamicPayloads()
radio.enableAckPayload() #Acknowledgement Payload : Can verify if data received
radio.openReadingPipe(1, pipes[1])
radio.printDetails()
radio.startListening()
while True:
while not radio.available(0):
time.sleep(1/100)
receivedMessage = []
#Populates the message
radio.read(receivedMessage, radio.getDynamicPayloadSize())
#-------------------------------------------------------
raw = int(receivedMessage[1]) * 256
total = raw + int(receivedMessage[0])
result.set(total)
threading.Thread(target=update_result).start()
mainloop()
(I didn't test this program because I don't have the environment, but I think that should work. Please comment if it's not working.)

Categories