Tkinter: Progressbar indicating the status of the uploaded file - python

Hi I'd like to have in my tkinter GUI a progress bar that gives visual cue about the status of the loading of a file. This is my code, but when I launch it the app just get stuck and does not work (I have to close it by force).
def start():
file = filedialog.askopenfilename(filetypes =(("Text File", "*.txt"),("All Files","*.*")), title = "Select")
filesize = os.path.getsize(file)
with open(file, "r", encoding='utf-8') as f:
for i in range(os.path.getsize(file)):
progressbar["value"] = 5
progressbar["maximum"] = filesize
label.config(text=str(progressbar["value"]) + "%")
root.update_idletasks()
time.sleep(1)
label = ttk.Label(root, text="00%")
label.pack()
button_prg = Button(root, text="progress", command=start)
button_prg.pack()
progressbar = ttk.Progressbar(root, orient="horizontal", length=100, mode='determinate')
progressbar.pack()
Also is there a way to resize the height of the bar? since I'd like to put it in the status bar at the bottom of my frame. Thank you all!

well what you want, i didn't understand clearly..
but here's what you shall have:
import time
import os
from tkinter import * #importing * is a very bad habit!
from tkinter import ttk
from tkinter import filedialog
def start():
file = filedialog.askopenfilename(filetypes =(("Text File", "*.txt"),("All Files","*.*")), title = "Select")
filesize = os.path.getsize(file)
with open(file, "r", encoding='utf-8') as f:
for i in range(os.path.getsize(file)):
progressbar["value"] = progressbar["value"]+1
progressbar["maximum"] = filesize
label.config(text=str(int(progressbar["value"]/os.path.getsize(file)*100)) + "%")
root.update_idletasks()
#time.sleep(1)
root = Tk()
label = ttk.Label(root, text="00%")
label.pack()
button_prg = Button(root, text="progress", command=start)
button_prg.pack()
progressbar = ttk.Progressbar(root, orient="horizontal", length=100, mode='determinate')
progressbar.pack()
root.mainloop()
btw, there's a limit in the file size.. that is 400kb.. i am just unable to load any of my files larger than 400kb in size!

Related

Python tkinter freezing and doesn't update when some function takes a long time - tkinter & pytube

I am building an app using tkinter and pytube, but I'm struggling with a feature: the progress bar.
The problem is, when I am using tkinter, because of the mainloop function, I can't use things like while, time.sleep... When the pytube function "download" is executed, the app freezes until the download is complete. For this reason, the progress bar doesn't load, just jumps to 100% complete when the app unfreezes at the end of download.
I've tried a threading function, to try to run the download function at the same time as the screen main loop updates. I've tried using the after function on tkinter, or some alternatives for mainloop. I also tried to run the download on another tk screen. Nothing works, the code is still running during the download, the progress is printed by a function in terminal, but as the tk screen freezes, I can't show it on screen, just in terminal.
How can I fix it?
ps: this is not the final version of this code, so it is not yet in OOP, but it is possible to understand
import sys
import threading
import time
from tkinter import *
from tkinter import filedialog, ttk, messagebox
from PIL import ImageTk, Image
from urllib.request import urlopen
from PIL.Image import Resampling
from pytube import YouTube
from io import BytesIO
import pyperclip
download_path = None
# -------------------- COMMANDS -------------------- #
def get_thumnail(url):
image_url = urlopen(url)
image_raw = image_url.read()
image_url.close()
image = Image.open(BytesIO(image_raw))
thumbnail = image.resize((300, 200), resample=Resampling.LANCZOS)
image_tk = ImageTk.PhotoImage(thumbnail)
return image_tk
def paste_url():
clipboard = pyperclip.paste()
video_url_entry.delete(0, END)
video_url_entry.insert(0, clipboard)
def select_path():
global download_path
download_path = filedialog.askdirectory()
destination_path_label_entry.config(text=download_path)
def show_progress_bar(s, chunk, bytes_remaining):
download_percent = int((s.filesize - bytes_remaining) / s.filesize * 100)
progressbar.after(200, progressbar.config(value=download_percent))
#progressbar.config(value=download_percent)
root.title(f'\rProgress: {download_percent} %')
print(f'\rProgress: {download_percent} %')
#threading.Thread(target=after).start()
def complete(s, file_path):
print(f'Download complete: {file_path}')
def download_video():
link = video_url_entry.get()
path = download_path
yt = YouTube(link)
threading.Thread(target=yt.register_on_progress_callback(show_progress_bar)).start()
new_icon = get_thumnail(yt.thumbnail_url)
main_image.configure(image=new_icon)
main_image.image = new_icon
is_ok = messagebox.askokcancel(title=yt.title,
message=f'Title: {yt.title}\n'
f'Length: {yt.length//60}m{yt.length&60}s\n'
f'Views: {yt.views}\n'
f'Author: {yt.author}')
if is_ok:
resolution = current_var.get()
if resolution == 'mp3':
video_stream = yt.streams.get_audio_only()
elif resolution == 'Highest':
video_stream = yt.streams.get_highest_resolution()
else:
video_stream = yt.streams.get_by_resolution(resolution)
print('start')
#threading.Thread(target=video_stream.download(path, filename_prefix=f'({resolution}) ')).start()
video_stream.download(path, filename_prefix=f'({resolution}) ')
print('done')
else:
main_image.configure(image=icon)
main_image.image = icon
# Screen settings
root = Tk()
icon = PhotoImage(file='logo.png')
root.iconphoto(False, icon)
root.title('Youtube Video Downloader')
root.config(padx=50, pady=50)
# Main Canvas
main_image = Label(root, image=icon, width=350, height=200)
main_image.grid(row=1, column=1, columnspan=3)
# Labels
video_url_label = Label(text='Video URL:')
video_url_label.grid(row=2, column=1)
done_label = Label(text='', width=47, fg='green')
done_label.grid(row=6, column=2, columnspan=2)
destination_path_label_entry = Label(width=40, bg='white')
destination_path_label_entry.grid(row=3, column=1, columnspan=2)
# Entry's
video_url_entry = Entry(width=35)
video_url_entry.focus()
video_url_entry.grid(row=2, column=2)
# Buttons
search_button = Button(text='Paste', width=15, command=paste_url)
search_button.grid(row=2, column=3)
destination_path_button = Button(text='Select Path', width=15, command=select_path)
destination_path_button.grid(row=3, column=3)
current_var = StringVar(value='Resolution')
combobox = ttk.Combobox(textvariable=current_var, state='readonly', values=('mp3', '144p', '240p', '360p', '480p', '720p', '1080p', 'Highest'), width=9)
combobox.grid(row=4, column=1)
download_button = Button(text='Download as MP4', width=46, command=download_video)
download_button.grid(row=4, column=2, columnspan=2)
# Progress bar
progressbar = ttk.Progressbar(root, orient=HORIZONTAL, length=410, mode='determinate')
progressbar.grid(row=5, column=1, columnspan=3)
root.mainloop()
Problem of your solution is, that you're calling video_stream.download from within download_video function.
Once video_stream.download is called, it is blocking GUI, so nothing got updated, until video download is finished. You had commented line in your code threading.Thread(target=video_stream.download(path, filename_prefix=f'({resolution}) ')).start(), so you were trying to call download in the Thread, but that would yield the same result as target=video_stream.download(path, filename_prefix=f'({resolution}) ') would first download the video and only then start the Thread itself. You don't want that.
Have a look at Thread documentation, where you must use callable object (function or method) as a target without any arguments. Those must be passed to the Thread by using args and kwargs.
The rest of your code is fine and you were properly using .after as well to avoid calling GUI from different Thread, which you should never do.
Here is your modified code, now properly calling download Thread and updating progress bar as well:
import threading
from io import BytesIO
from tkinter import *
from tkinter import filedialog, ttk, messagebox
from urllib.request import urlopen
import pyperclip
from PIL import ImageTk, Image
from PIL.Image import Resampling
from pytube import YouTube
download_path = None
# -------------------- COMMANDS -------------------- #
def get_thumnail(url):
image_url = urlopen(url)
image_raw = image_url.read()
image_url.close()
image = Image.open(BytesIO(image_raw))
thumbnail = image.resize((300, 200), resample=Resampling.LANCZOS)
image_tk = ImageTk.PhotoImage(thumbnail)
return image_tk
def paste_url():
clipboard = pyperclip.paste()
video_url_entry.delete(0, END)
video_url_entry.insert(0, clipboard)
def select_path():
global download_path
download_path = filedialog.askdirectory()
destination_path_label_entry.config(text=download_path)
def show_progress_bar(s, chunk, bytes_remaining):
download_percent = int((s.filesize - bytes_remaining) / s.filesize * 100)
root.after(0, update_gui(download_percent))
print(f'\rProgress: {download_percent} %')
def update_gui(download_percent):
progressbar.config(value=download_percent)
root.title(f'\rProgress: {download_percent} %')
def complete(s, file_path):
print(f'Download complete: {file_path}')
def download_video():
link = video_url_entry.get()
path = download_path
yt = YouTube(link)
# Don't put callback into Thread
yt.register_on_progress_callback(show_progress_bar)
yt.register_on_complete_callback(complete)
new_icon = get_thumnail(yt.thumbnail_url)
main_image.configure(image=new_icon)
main_image.image = new_icon
is_ok = messagebox.askokcancel(title=yt.title,
message=f'Title: {yt.title}\n'
f'Length: {yt.length // 60}m{yt.length & 60}s\n'
f'Views: {yt.views}\n'
f'Author: {yt.author}')
if is_ok:
resolution = current_var.get()
if resolution == 'mp3':
video_stream = yt.streams.get_audio_only()
elif resolution == 'Highest':
video_stream = yt.streams.get_highest_resolution()
else:
video_stream = yt.streams.get_by_resolution(resolution)
# You cant just call video_stream.download() with params, because it will execute and block outside Thread
threading.Thread(target=video_stream.download,
kwargs={"output_path": path, "filename_prefix": f'({resolution}) '}).start()
else:
main_image.configure(image=icon)
main_image.image = icon
# Screen settings
root = Tk()
icon = PhotoImage(file='logo.png')
root.iconphoto(False, icon)
root.title('Youtube Video Downloader')
root.config(padx=50, pady=50)
# Main Canvas
main_image = Label(root, image=icon, width=350, height=200)
main_image.grid(row=1, column=1, columnspan=3)
# Labels
video_url_label = Label(text='Video URL:')
video_url_label.grid(row=2, column=1)
done_label = Label(text='', width=47, fg='green')
done_label.grid(row=6, column=2, columnspan=2)
destination_path_label_entry = Label(width=40, bg='white')
destination_path_label_entry.grid(row=3, column=1, columnspan=2)
# Entry's
video_url_entry = Entry(width=35)
video_url_entry.focus()
video_url_entry.grid(row=2, column=2)
# Buttons
search_button = Button(text='Paste', width=15, command=paste_url)
search_button.grid(row=2, column=3)
destination_path_button = Button(text='Select Path', width=15, command=select_path)
destination_path_button.grid(row=3, column=3)
current_var = StringVar(value='Resolution')
combobox = ttk.Combobox(textvariable=current_var, state='readonly',
values=('mp3', '144p', '240p', '360p', '480p', '720p', '1080p', 'Highest'), width=9)
combobox.grid(row=4, column=1)
download_button = Button(text='Download as MP4', width=46, command=download_video)
download_button.grid(row=4, column=2, columnspan=2)
# Progress bar
progressbar = ttk.Progressbar(root, orient=HORIZONTAL, length=410, mode='determinate')
progressbar.grid(row=5, column=1, columnspan=3)
root.mainloop()

Convert multiple images to pdf

I wanted to create a pdf converter app with python which converts images to pdf.
This is my code but it is only converting one image into pdf I tried many ways to fix it but none of them worked can anyone please help me because I want to convert multiple images into pdf but it is not working besides using for loop. I tried img2pdf but it giving alpha channel error and I am not able to solve that.
import PIL.Image
from tkinter import *
from tkinter import filedialog as fd
import PyPDF2
import img2pdf
from tkinter import ttk
root = Tk()
root.geometry('500x500')
root.resizable(0, 0)
filename = StringVar()
entry = Entry(root, width=50, textvariable=filename).place(x=115, y=250)
def Select_images():
global files
files = fd.askopenfilenames()
def select_dir():
global dir_name
dir_name = fd.askdirectory()
def submit():
global file_name
file_name = filename.get()
def create_pdf():
myfile=open(f'{dir_name}/{file_name}.pdf', 'wb+')
for image in files:
img=PIL.Image.open(image).convert('RGBA')
im1=img.convert('RGB')
im1.save(r'{}\{}.pdf'.format(dir_name,file_name))
myfile.close()
button = Button(root, text='Sumbit PDF Name', command=submit).place(x=200, y=300)
label = Label(root, text='Write PDF Name').place(x=210, y=215)
button1 = Button(root, text='Create File', command=create_pdf).place(x=215, y=335)
button2 = Button(root, text='Select Directory To Save File',command=select_dir).place(x=200, y=50)
button3 = Button(root, text='select Images', command=Select_images).place(x=235, y=100)
root.mainloop()
See if this helps you:
from tkinter import Tk, messagebox as mb, filedialog as fd
from tkinter.constants import DISABLED, NORMAL
from tkinter.ttk import Button, Label
from PIL import Image
import os
root = Tk()
root.title("Image to PDF converter")
root.geometry("500x500")
imglist = [] # For creating a list of images
fimgl = [] # List for storing multiple image names
png = True # If the image chosen is a .png file
def askfile():
global files, fimg, order, imglist, tm, png
files = fd.askopenfilenames(title="Choose images", filetypes=(("PNGs", "*.png"), ("JPGs", "*.jpg"), ("All Files", "*.*")))
for i in files:
fimgl.append(i)
if files:
for j in fimgl:
if j.endswith(".png"): # If the image is a PNG:
png = True
fnl = Label(root, text=j)
fnl.pack()
img = Image.open(j)
fimg = img.convert('RGB')
imglist.append(fimg)
p.config(state=NORMAL)
def convert_pdf():
global png
try:
if png:
imglist.pop()
saveloc = fd.asksaveasfilename(title="Save the PDF file")
if saveloc:
if saveloc.endswith(".pdf"):
pass
else:
saveloc = saveloc + ".pdf"
if os.path.exists(saveloc):
yn = mb.askyesno("Confirm Save As", f"{os.path.basename(saveloc)} already exists.\nDo you want to replace it?")
if yn:
os.remove(saveloc)
else:
convert_pdf()
fimg.save(saveloc, save_all=True, append_images=imglist)
mb.showinfo("Done!", "PDF file saved! Click 'OK' to open it")
os.startfile(saveloc)
root.quit()
except Exception as err:
mb.showerror("Error", err)
root.quit()
cb = Button(root, text="Add Files", command=askfile)
cb.pack(pady=20)
p = Button(root, text="Convert", command=convert_pdf, state=DISABLED)
p.pack(pady=20)
root.mainloop()

changing extensions of all images in a folder with tkinter

I want to convert the image formats of all images in a folder using tkinter. all extensions I want in a combobox but I don't know why this code doesn't work .no error is displayed.
from tkinter import filedialog, StringVar
from tkinter import ttk
root=tkinter.Tk()
root.geometry("800x600")
#defining functions
def get_folder():
global folder_path
folder_path = filedialog.askdirectory(initialdir='./', title="Select Folder")
print(folder_path)
def get_extension():
change_to = com.get()
change_from = "py"
files=os.listdir(folder_path)
for file in files:
if (".%s"%change_from) in file:
newfile=file.replace((".%s"%change_from),".%s"%change_to)
os.rename((folder_path+'/'+file),(folder_path+'/'+newfile))
#defining widgets for frames
folder_label = tkinter.Label(from_frame)
browse_button = tkinter.Button(from_frame, text="Browse", command=get_folder)
change_button = tkinter.Button(button_frame, text="Change Extension", command=get_extension)
change_button.pack()
#defining combobox
com = StringVar()
list_combo = ['.png','.jpg','.jpeg', '.svg', '.tif','.bmp','.gif','ppm']
combox = ttk.Combobox(root, width = 25, font = 'arial 19 bold', value = list_combo, state = 'r', textvariable = com)
combox.place(x= 190, y= 190)
combox.set('select type')
root.configure(bg = 'coral1')
root.mainloop()```
Is this what You want:
from tkinter import Tk, Button, Label, StringVar, Frame
from tkinter.ttk import Combobox
from tkinter.filedialog import askdirectory
import os
def convert_extensions():
path = dir_lbl.cget('text')
from_ = from_var.get()
to_ = to_var.get()
files_from = [f'{path}/{file}' for file in os.listdir(path) if file.split('.')[-1] == from_]
files_to = ['.'.join(file.split('.')[:-1]) + '.' + file.split('.')[-1].replace(from_, to_) for file in files_from]
for from_file, to_file in zip(files_from, files_to):
os.rename(from_file, to_file)
print(f'{"-" * 100}\n'
f'{"From:": <10} {from_file}\n'
f'{"To:": <10} {to_file}\n')
root = Tk()
root.geometry('500x200')
Button(root, text='Choose Directory', command=lambda: dir_lbl.config(text=askdirectory())).pack(pady=5)
dir_lbl = Label(root, text='')
dir_lbl.pack(pady=5)
options_frame = Frame(root)
options_frame.pack(pady=5)
from_var = StringVar(value='Convert from')
list_combo = ['png', 'jpg', 'jpeg', 'svg', 'tif', 'bmp', 'gif', 'ppm']
Combobox(options_frame, value=list_combo,
textvariable=from_var, state='r').pack(side='left', padx=5, pady=5)
to_var = StringVar(value='Convert to')
Combobox(options_frame, value=list_combo,
textvariable=to_var, state='r').pack(side='left', padx=5, pady=5)
Button(root, text='Convert', command=convert_extensions).pack(pady=5)
root.mainloop()
This however allows more control in that You can choose both the extension to convert and to which one convert.
This is pretty basic so if You have any questions, ask. I will probably add a detailed explanation later but for now this is all I can do. Oh and btw that three line print function is not necessary at all and it probably should be made to display that in tkinter window or sth but otherwise it can be removed.

How can I call my second function after my first function?

I have two functions and two GUI.
I cannot get them to work after each other.
first, I want to open my browse GUI then editing GUI.
help me out, please.
from tkinter import *
from tkinter import filedialog
window = Tk()
def fileme():
root = Tk()
root.withdraw()
file_path = filedialog.askopenfilenames(filetypes=[("Text file","*.txt")])
print(file_path)
window.withdraw()
with open(file_path[0]) as f:
f_contents = f.read()
b=(f_contents)
print(b)
window.title('Edit and save text files by Ali')
frame = Frame(window)
btn = Button(frame, text = 'Browse', command= fileme)
btn.pack(side = RIGHT , padx =55)
frame.pack(padx=100,pady = 30)
root=Tk()
x=filedialog.askopenfilename(filetypes=[("Text file","*.txt")])
T=Text(root,state='normal', height=20, width=70)
T.pack()
T.insert(END, open(x).read())
def save():
b = T.get('1.0', END)
f = open(x, 'wt')
f.write(b)
f.close()
btn= Button(root, text='Save', command=save)
btn.pack(side = RIGHT , padx =55)
window.mainloop()
root.mainloop()
I figured it out
Here is an answer to this type of questions.
hope to be useful for anyone who is learning programming.
from tkinter import *
from tkinter import filedialog
window=Tk()
def x():
x=filedialog.askopenfilename(filetypes=[("Text file","*.txt")])
T = Text(window, state='normal', height=20, width=70)
T.pack()
T.insert(END, open(x).read())
def save():
b = T.get('1.0', END)
f = open(x, 'wt')
f.write(b)
f.close()
btn1 = Button(window, text='Save', command=save)
btn1.pack(side=RIGHT, padx=55)
window.title('Edit and save text files by Ali')
frame = Frame(window)
btn = Button(frame, text = 'Browse', command= x)
btn.pack(side = RIGHT , padx =55)
frame.pack(padx=100,pady = 30)
window.mainloop()
#Epiphnac In your solution both the browser window and the text editor open in the same window. I think my solution is a little bit better than that.
I have put the editor inside a new window using Toplevel.
Like this:
import tkinter as tk
from tkinter import filedialog
window = tk.Tk()
def editor():
x = filedialog.askopenfilename(filetypes=[("Text file", "*.txt")])
new_window = tk.Toplevel()
t = tk.Text(new_window, state='normal', height=20, width=70)
t.pack()
t.insert(tk.END, open(x).read())
def save():
b = t.get('1.0', tk.END)
f = open(x, 'wt')
f.write(b)
f.close()
btn1 = tk.Button(new_window, text='Save', command=save)
btn1.pack(side=tk.RIGHT, padx=55)
window.title('Edit and save text files by Ali')
frame = tk.Frame(window)
btn = tk.Button(frame, text='Browse', command=editor)
btn.pack(side=tk.RIGHT, padx=55)
frame.pack(padx=100, pady=30)
window.mainloop()

How to make buttons call functions

After defining my word count method thanks to another member, I am now creating a GUI to go along with it. I have created three buttons, one for browsing for a file, one for counting the words and lines in a file, and one for exiting.
My question is, how do I make these buttons do things? I am trying to make the "Browse for a file" run the filename = fileopenbox() line, and the "Count" button run the word_count() method.
Here is what the code looks like:
from tkinter import *
from easygui import fileopenbox
root = Tk()
root.title("Word Counter")
root.geometry("500x500")
app = Frame(root)
app.grid()
button1 = Button(app, text = "Browse for a file")
button1.grid()
button2 = Button(app)
button2.grid()
button2.configure(text ="Count the file")
button3 = Button(app)
button3.grid()
button3["text"] = "Exit"
root.mainloop()
def word_count(filename):
filename = fileopenbox()
if not filename.endswith(('.txt', '.py', '.java')):
print('Are you trying to annoy me? How about giving me a TEXT or SOURCE CODE file, genius?')
return
with open(filename) as f:
n_lines = 0
n_words = 0
for line in f:
n_lines += 1
n_words += len(line.split())
print('Your file has {} lines, and {} words'.format(n_lines, n_words))
You have to pass the reference to the function you want to execute as the command option. Since you are divinding the task in two steps (one button to ask for the file name, and another one to count the lines and the words), I'd suggest you to create a class to wrap everything.
Besides, I suggest you to remove the dependency of easygui - since it is a project that is not longer maintained - and replace it with filedialog.askopenfilename, which is part of the standard library:
from tkinter import *
from tkinter.filedialog import askopenfilename
from tkinter.messagebox import showwarning, showinfo
class App(Frame):
def __init__(self, master):
Frame.__init__(self, master)
self.filename = None
button1 = Button(self, text="Browse for a file", command=self.askfilename)
button2 = Button(self, text ="Count the file", command=self.word_count)
button3 = Button(self, text="Exit", command=master.destroy)
button1.grid()
button2.grid()
button3.grid()
self.grid()
def askfilename(self):
filename = askopenfilename()
if not filename.endswith(('.txt', '.py', '.java')):
showwarning('Are you trying to annoy me?', 'How about giving me a TEXT or SOURCE CODE file, genius?')
else:
self.filename = filename
def word_count(self):
if self.filename:
with open(self.filename) as f:
n_lines = 0
n_words = 0
for line in f:
n_lines += 1
n_words += len(line.split())
showinfo('Result', 'Your file has {} lines, and {} words'.format(n_lines, n_words))
else:
showwarning('No file selected', 'Select a file first')
root = Tk()
root.title("Word Counter")
root.geometry("500x500")
app = App(root)
root.mainloop()

Categories

Resources