Related
how to stop opening the same window multiple time when the button clicked for below code. in this I'm using multiprocessing.
on button click event I'm getting issue like screenshot
github repo
root = Tk()
root.title("PDF Reader")
root.geometry("500x300+500+300")
root.maxsize(width=None, height=None)
root.resizable(False, False)
def dostuff():
if (__name__ == "__main__"):
try:
folder = folderPath.get()
if folder != "":
if (os.path.exists(folder)):
run_btn['state'] = DISABLED
msg = "Please wait... Process being done.\n Dont close the app."
label = Label(root, text=msg, foreground="red")
label.grid(row=5, column=0, padx=20)
# label.pack()
label.update()
#root.protocol("WM_DELETE_WINDOW", on_exit)
root.protocol("WM_DELETE_WINDOW", disable_event)
pdfs_list = get_listof_files(folder, "pdf")
with multiprocessing.Manager() as manager:
failed = manager.list()
if pdfs_list:
p = multiprocessing.Pool(5)
p.map(partial(get_total_txt, failed),
pdfs_list)
# p.close()
# Process.join(p)
# print(failed)
label.destroy()
suss_files_count = len(pdfs_list)-len(failed)
print("Out of {} files {} Succeeded and {} files found without data {} Files Without Grid Numbers and {} loaded.".format(
len(pdfs_list), suss_files_count, len(
failed), len(No_grid_number),
str(round(suss_files_count/(len(pdfs_list))*100, 2))+"%"))
msg = "\nProcess has been Completed Sucessfully..!\n Out of {} files {} Succeeded and {} files found without data.\n Please check Log for More info.".format(
len(pdfs_list), suss_files_count, len(
failed), len(No_grid_number))
label = Label(root, text="", foreground="green")
label.grid(row=5, column=0, padx=20)
# label.pack()
label.update()
label = Label(root, text=msg, foreground="green")
label.grid(row=5, column=0, padx=20)
# label.pack()
label.update()
time.sleep(5)
label.destroy()
end_time = datetime.now()
print('Duration: {} Minutes'.format(end_time - start_time))
run_btn['state'] = NORMAL
else:
messagebox.showerror(
"Path Not Found", "Selected path or directory doesn't exists: "+folder)
else:
messagebox.showwarning("Warning", "Please Select path")
# print(folder)
except AttributeError as attbe:
messagebox.showerror("AttributeError", str(attbe))
except FileNotFoundError as fne:
messagebox.showerror("TypeError", str(fne))
run_btn['state'] = NORMAL
#print(fne, folder)
except TypeError as TError:
messagebox.showerror("TypeError", str(TError))
run_btn['state'] = NORMAL
folderPath = StringVar()
a = Label(root, text="\nChoose Path:", font=('Arial'))
a.grid(row=0, column=0)
e = Entry(root, textvariable=folderPath, width=50)
e.grid(row=3, column=0, padx=35)
btn_browse = ttk.Button(root, text="Browse", command=getFolderPath, padding=5)
btn_browse.grid(row=3, column=1)
""""whenever the button is clicked, a new thread will be created and started """
run_btn = Button(root, text="Run", command=lambda: Thread(target=dostuff).start(),
width=30, height=2, padx=10)
run_btn.grid(row=4, column=0)
# root.protocol("WM_DELETE_WINDOW", on_exit)
# root.protocol("WM_DELETE_WINDOW", lambda: (
# run_btn.config(state="disabled"), root.destroy()))
root.lift()
root.mainloop()
Here's the kind of structure you need. This still doesn't work, because I don't have the functions in your module and there are some errors in your code, but this shows the structure:
import os
import glob
import multiprocessing
from tkinter import *
def get_total_txt(f,pdf):
f.append(pdf)
def get_listof_files(folder,ext):
return glob.glob( folder+"/*."+ext)
def getFolderPath():
folder_selected = filedialog.askdirectory(initialdir="")
folderPath.set(folder_selected)
def on_exit():
"""When you click to exit, this function is called"""
if run_btn['state'] == DISABLED:
if messagebox.askyesno("Exit", "Are you want to quit the application? Currently process being in running state."):
root.destroy()
sys.exit(0)
else:
if messagebox.askyesno("Exit", "Are you want to quit the application?"):
root.destroy()
def disable_event():
messagebox.showinfo( "Processing", "Please wait until process being done.")
def dostuff():
folder = folderPath.get()
if folder != "":
if os.path.exists(folder):
run_btn['state'] = DISABLED
msg = "Please wait... Process being done.\n Dont close the app."
label = Label(root, text=msg, foreground="red")
label.grid(row=5, column=0, padx=20)
label.update()
#root.protocol("WM_DELETE_WINDOW", on_exit)
root.protocol("WM_DELETE_WINDOW", disable_event)
pdfs_list = get_listof_files(folder, "pdf")
with multiprocessing.Manager() as manager:
failed = manager.list()
if pdfs_list:
p = multiprocessing.Pool(5)
p.map(partial(get_total_txt, failed),
pdfs_list)
label.destroy()
suss_files_count = len(pdfs_list)-len(failed)
print("Out of {} files {} Succeeded and {} files found without data {} Files Without Grid Numbers and {} loaded.".format(
len(pdfs_list), suss_files_count, len(
failed), len(No_grid_number),
str(round(suss_files_count/(len(pdfs_list))*100, 2))+"%"))
msg = "\nProcess has been Completed Sucessfully..!\n Out of {} files {} Succeeded and {} files found without data.\n Please check Log for More info.".format(
len(pdfs_list), suss_files_count, len(
failed), len(No_grid_number))
label = Label(root, text="", foreground="green")
label.grid(row=5, column=0, padx=20)
# label.pack()
label.update()
label = Label(root, text=msg, foreground="green")
label.grid(row=5, column=0, padx=20)
# label.pack()
label.update()
time.sleep(5)
label.destroy()
end_time = datetime.now()
print('Duration: {} Minutes'.format(end_time - start_time))
run_btn['state'] = NORMAL
else:
messagebox.showerror(
"Path Not Found", "Selected path or directory doesn't exists: "+folder)
else:
messagebox.showwarning("Warning", "Please Select path")
def main():
global root
global folderPath
global run_btn
root = Tk()
root.title("PDF Reader")
root.geometry("500x300+500+300")
root.maxsize(width=None, height=None)
root.resizable(False, False)
folderPath = StringVar()
a = Label(root, text="\nChoose Path:", font=('Arial'))
a.grid(row=0, column=0)
e = Entry(root, textvariable=folderPath, width=50)
e.grid(row=3, column=0, padx=35)
#btn_browse = ttk.Button(root, text="Browse", command=getFolderPath, padding=5)
btn_browse = Button(root, text="Browse", command=getFolderPath)
btn_browse.grid(row=3, column=1)
""""whenever the button is clicked, a new thread will be created and started """
run_btn = Button(root, text="Run", command=dostuff, width=30, height=2, padx=10)
run_btn.grid(row=4, column=0)
# root.protocol("WM_DELETE_WINDOW", on_exit)
# root.protocol("WM_DELETE_WINDOW", lambda: (
# run_btn.config(state="disabled"), root.destroy()))
root.lift()
root.mainloop()
if __name__ == "__main__":
main()
I saw this video: https://www.youtube.com/watch?v=W0l0r0PekvM&t=20s
The code could display data but it display by many Grid together so i can not create a Scrollbar.
you can see the function display data in CRUD() -> creategrid(n)
sorry for my bad English ^^
my code:
from tkinter import *
import tkinter as tk
from tkinter import ttk
import pymongo
from tkinter import messagebox
from tkinter import filedialog
from PIL import Image, ImageTk
import pybase64 as base64
import gridfs
myclient = pymongo.MongoClient("mongodb://localhost:27017/")
mydb = myclient["enrollmentsystem"] #database file
mycol = mydb["employees"]
mycol2 = mydb["fs.files"]
def CRUD():
lst=[["ID","Name","Age","Departure","Gender","Skill","Working (Y)","Trained (Y)","Salary"]]
window=tk.Toplevel(w1)
window.title("UPDATE PROFILE")
window.geometry('300x400')
window.configure(bg="bisque2")
window3=tk.Toplevel(window)
window3.title("UPDATE PROFILE")
window3.geometry('+%d+%d' % (100,100))
window3.configure(bg="bisque2")
def callback(event):
global lstindex
li=[]
li=event.widget._values
lstindex=li[1]
cid.set(lst[li[1]][0])
cname.set(lst[li[1]][1])
cage.set(lst[li[1]][2])
cdept.set(lst[li[1]][3])
cgender.set(lst[li[1]][4])
cskill.set(lst[li[1]][5])
cwyear.set(lst[li[1]][6])
ctyear.set(lst[li[1]][7])
csalary.set(lst[li[1]][8])
def creategrid(n):
lst.clear()
lst.append(["ID","Name","Age","Departure","Gender","Skill","Working (Y)","Trained (Y)","Salary"])
cursor = mycol.find({})
for text_fromDB in cursor:
empid=str(text_fromDB["empid"])
empname=str(text_fromDB["empname"].encode('utf-8').decode('utf-8'))
empage=str(text_fromDB["empage"].encode('utf-8').decode('utf-8'))
empdept=str(text_fromDB["empdept"].encode('utf-8').decode('utf-8'))
empgender=str(text_fromDB["empgender"].encode('utf-8').decode('utf-8'))
empskill=str(text_fromDB["empskill"].encode('utf-8').decode('utf-8'))
empwyear=int(text_fromDB["empwyear"].encode('utf-8').decode('utf-8'))
emptyear=int(text_fromDB["emptyear"].encode('utf-8').decode('utf-8'))
empsalary=float(text_fromDB["empsalary"].encode('utf-8').decode('utf-8'))
lst.append([empid,empname,empage,empdept,empgender,empskill,empwyear,emptyear,empsalary])
for i in range(len(lst)):
for j in range(len(lst[0])):
mgrid=tk.Entry(window3,width=10)
mgrid.insert(tk.END, lst[i][j])
mgrid._values =mgrid.get(),i
mgrid.grid(row=i+12,column=j+11)
mgrid.bind("<Button-1>",callback)
if n==1:
for label in window.grid_slaves():
if int(label.grid_info()["row"]) >11:
label.grid_forget()
def msgbox(msg,titlebar):
result = messagebox.askokcancel(title=titlebar, message=msg)
return result
def save():
r=msgbox("save record?","record")
if r==True:
newid = mycol.count_documents({})
if newid!=0:
newid = mycol.find_one(sort=[("empid", -1)])["empid"]
id=newid+1
cid.set(id)
mydict = {"empid": int(custid.get()),"empname":custname.get(), "empage":custage.get(),
"empdept":custdept.get(),"empgender":custgender.get(),"empskill":custskill.get(),
"empwyear":custwyear.get(),"emptyear":custtyear.get(),"empsalary":custsalary.get()}
x = mycol.insert_one(mydict)
messagebox.showinfo("info", "save completed, please add photo")
creategrid(1)
creategrid(0)
def delete():
r=msgbox("Delete?", "record")
if r ==True:
myquery = {"empid":int(custid.get())}
mycol.delete_one(myquery)
creategrid(1)
creategrid(0)
def update():
r=msgbox("Update?","record")
if r ==True:
myquery={"empid":int(custid.get())}
newvalues={"$set":{"empname":custname.get()}}
mycol.update_one(myquery,newvalues)
newvalues={"$set":{"empage":custage.get()}}
mycol.update_one(myquery,newvalues)
newvalues={"$set":{"empdept":custdept.get()}}
mycol.update_one(myquery,newvalues)
newvalues={"$set":{"empgender":custgender.get()}}
mycol.update_one(myquery,newvalues)
newvalues={"$set":{"empskill":custskill.get()}}
mycol.update_one(myquery,newvalues)
newvalues={"$set":{"empwyear":custwyear.get()}}
mycol.update_one(myquery,newvalues)
newvalues={"$set":{"emptyear":custtyear.get()}}
mycol.update_one(myquery,newvalues)
newvalues={"$set":{"empsalary":custsalary.get()}}
mycol.update_one(myquery,newvalues)
creategrid(1)
creategrid(0)
def upload_file(): # Image upload and display
global filename,img, fs
f_types =[('Png files','*.png'),('Gif Files', '*.gif'),('PMG files','*.pmg'),('jpg files','*.jpg')]
filename = filedialog.askopenfilename(filetypes=f_types,defaultextension='.png')
img=Image.open(filename)
img=img.resize((150,150)) # new width & height
img=ImageTk.PhotoImage(img)
e1 =tk.Label(picker)
e1.grid(row=15,column=1)
e1.image = img
e1['image']=img
def saveimage():
global filename
r=msgbox("save image?","image")
if r==True:
# encode image to binary text
with open(filename, "rb") as image:
# read the image as text and convert it to binary
image_string = base64.b64encode(image.read())
# create Gridfs instance
fs = gridfs.GridFS(mydb)
# add the image to database
def alreadyExists():
return bool(mycol2.find_one({'filename' : custid.get()}))
if alreadyExists() == True:
messagebox.showerror("Error", "ID exist")
else:
put_image = fs.put(image_string, filename = custid.get())
picker = tk.Frame(window,width=150, height=150)
picker.place(x=1, y=250)
b2 = tk.Button(window, text='Upload photo',
command = lambda:upload_file()).place(x=180, y=310)
b3 = tk.Button(window, text='Save photo',
command = lambda:saveimage()).place(x=180, y=340)
label =tk.Label(window, text="EMPLOYEE ENLISTMENT FORM", width=25, height=1, bg="cornflower blue",anchor="center")
label.grid(column=2, row=1)
label = tk.Label(window, text="Employee ID:", width=20, height=1, bg="cadet blue")
label.grid(column=1, row=2)
cid = tk.StringVar()
custid = tk.Entry(window,width=33, textvariable=cid)
custid.grid(column=2, row=2)
custid.configure(state=tk.DISABLED)
label = tk.Label(window, text="Employee Name:", width=20, height=1, bg="cadet blue")
label.grid(column=1, row=3)
cname = tk.StringVar()
custname = tk.Entry(window,width=33, textvariable=cname)
custname.grid(column=2, row=3)
age = []
for i in range(1960,2002):
age.append(i)
label=tk.Label(window, text="Employee BirthYear:", width=20, height=1, bg="cadet blue")
label.grid(row=4,column=1)
cage = tk.IntVar()
custage = ttk.Combobox(window,width=30, values=age)
custage.grid(row=4,column=2)
label=tk.Label(window, text="Employee Departure:", width=20, height=1, bg="cadet blue")
label.grid(row=5,column=1)
cdept = tk.StringVar()
dept=['Sale','Marketing']
custdept = ttk.Combobox(window,width=30, values=dept)
custdept.grid(row=5,column=2)
label=tk.Label(window, text="Employee Gender:", width=20, height=1, bg="cadet blue")
label.grid(row=6,column=1)
gender=['Male','Female']
cgender = tk.StringVar()
custgender = ttk.Combobox(window,width=30, values=gender)
custgender.grid(row=6,column=2)
label=tk.Label(window, text="Employee Skill:", width=20, height=1, bg="cadet blue")
label.grid(row=7,column=1)
cskill = tk.StringVar()
skill=['SQL, C#, Python','SQL, C#, C++','SQL, Python, C++','SQL, C++','SQL, Python','SQL, C#']
custskill = ttk.Combobox(window,width=30, values=skill)
custskill.grid(row=7,column=2)
label = tk.Label(window, text="Employee WorkingYear:", width=20, height=1, bg="cadet blue")
label.grid(column=1, row=8)
cwyear = tk.IntVar()
custwyear = tk.Entry(window,width=33, textvariable=cwyear)
custwyear.grid(column=2, row=8)
label = tk.Label(window, text="Employee Trained (Y):", width=20, height=1, bg="cadet blue")
label.grid(column=1, row=9)
ctyear = tk.IntVar()
custtyear = tk.Entry(window,width=33, textvariable=ctyear)
custtyear.grid(column=2, row=9)
label = tk.Label(window, text="Employee Salary:", width=20, height=1, bg="cadet blue")
label.grid(column=1, row=10)
csalary = tk.DoubleVar()
custsalary = tk.Entry(window,width=33, textvariable=csalary)
custsalary.grid(column=2, row=10)
creategrid(1) #create textfield grid vi o tren chay tu grid 1
savebtn = tk.Button(window,text="Save", command=save).place(x=180, y=275)
savebtn = tk.Button(window,text="Delete", command=delete).place(x=220, y=275)
savebtn = tk.Button(window,text="Update", command=update).place(x=270, y=275)
btnClose=tk.Button(window, text="Close me", command=window.destroy).place(x=180, y=370)
# Tim kiem anh dua tren ID
def searchImage():
window2=tk.Toplevel(w1)
window2.title("BROWSE")
window2.geometry('+%d+%d' % (100,100))
picker2 = tk.Frame(window2,width=30, height=1)
picker2.grid(row=1, column=3,rowspan = 10)
a = tk.Entry(picker2,width=33)
a.grid(column=2, row=11)
def checkid():
def alreadyExists():
return bool(mycol2.find_one({'filename' : a.get()}))
if alreadyExists() == True:
messagebox.showinfo("info", "ID exist")
fs = gridfs.GridFS(mydb)
data = mycol2.find_one({'filename' : a.get()})
my_id = data['_id']
outputdata =fs.get(my_id).read()
outputdata=outputdata.decode()
outputdata = base64.b64decode((outputdata))
dowloadlocation = "image.jpg"
output = open(dowloadlocation, "wb")
output.write(outputdata)
output.close()
bgetdisplay.config(state='active')
else:
messagebox.showerror("Error", "ID doesn't exist, please retype")
bgetdisplay.config(state='disabled')
def display():
root = Toplevel(window2)
path = "image.jpg"
img = Image.open(path)
img=img.resize((300,300))
img = ImageTk.PhotoImage(img)
panel = tk.Label(root, image=img)
panel.photo = img
panel.grid(column=1,row=1)
bgetdisplay = tk.Button(window2,state ='disabled', text='display image',
width=10,height=2,command = lambda:display())
bgetdisplay.grid(column = 5, row = 2)
bgetid = tk.Button(window2, text='checkID',
width=10,height=2,command = lambda:checkid()).grid(column = 4 ,row = 2)
label = tk.Label(picker2, text="browse photo(input id):", width=20, height=1, bg="cornflower blue")
label.grid(column=1, row=11)
w1=Tk()
btnOpen=tk.Button(w1,text ="Create, update, Delete",width=20,height=4, command=CRUD).place(x=120, y=20)
btnOpen=tk.Button(w1,text ="Search photo",width=20,height=4, command=searchImage).place(x=120, y=100)
w1.geometry('400x200')
w1.title("HUMAN RESOURES")
w1.mainloop()
I want to display data in a treeview to create a scrollbar.
Can anyone help to to change this display code
I am making this program using tkinter and urllib which is supposed to be like a download manager. After I was nearly done with the program I realized that I had not defined a cancel button for the downloads. After looking into it more I found out about multiprocessing (before that I just used threading) and apparently it is a better module than threading for running functions in parallel and it also has a terminate function. However, no matter what I do I can't seem to understand this module. It is much more complicated than threading and I get absurd errors all the time. In my program I constantly get this error:
Exception in Tkinter callback
Traceback (most recent call last):
File "C:\Users\Family\AppData\Local\Programs\Python\Python38-32\lib\tkinter\__init__.py", line 1883, in __call__
return self.func(*args)
File "C:/Users/Family/PycharmProjects/8-bit Downloader/test.py", line 258, in start_download
Process(target=download_files,
File "C:\Users\Family\AppData\Local\Programs\Python\Python38-32\lib\multiprocessing\process.py", line 121, in start
self._popen = self._Popen(self)
File "C:\Users\Family\AppData\Local\Programs\Python\Python38-32\lib\multiprocessing\context.py", line 224, in _Popen
return _default_context.get_context().Process._Popen(process_obj)
File "C:\Users\Family\AppData\Local\Programs\Python\Python38-32\lib\multiprocessing\context.py", line 327, in _Popen
return Popen(process_obj)
File "C:\Users\Family\AppData\Local\Programs\Python\Python38-32\lib\multiprocessing\popen_spawn_win32.py", line 93, in __init__
reduction.dump(process_obj, to_child)
File "C:\Users\Family\AppData\Local\Programs\Python\Python38-32\lib\multiprocessing\reduction.py", line 60, in dump
ForkingPickler(file, protocol).dump(obj)
TypeError: cannot pickle '_tkinter.tkapp' object
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "C:\Users\Family\AppData\Local\Programs\Python\Python38-32\lib\multiprocessing\spawn.py", line 116, in spawn_main
exitcode = _main(fd, parent_sentinel)
File "C:\Users\Family\AppData\Local\Programs\Python\Python38-32\lib\multiprocessing\spawn.py", line 126, in _main
self = reduction.pickle.load(from_parent)
EOFError: Ran out of input
I have absolutely no idea what has caused this error and how to fix it. It seems like there is a problem with the start_download() function. My code (I know that it has some flaws and that I can make it cleaner, I just wanted to do that after finishing the fundamentals and if there are some parts that you think is irrelevant please inform me. ):
import os
import sqlite3
import time
import urllib.request
from multiprocessing import Process
from pathlib import Path
from tkinter import *
from tkinter import filedialog
from tkinter import font
from tkinter import messagebox
from tkinter import ttk
import numpy as np
import requests
import win10toast
from PIL import Image, ImageTk
files_downloading, times_clicked, dir_files = 0, 0, {}
info_window, customize = None, None
# The problem I believe is with the two functions below and the start function some of the code is related to the downloads tab
# which is irrelevant and some other were just functions that were unrelated so if you see a button without a function that
# exists or it is 'None' it is just deleted for the purpose of this question and also ignore the image variables!
def get_info(path, new_fullname, url_entry):
global times_clicked
lbl_await.pack_forget()
prg_br.pack_forget()
file_size = int(requests.head(str(url_entry.get()),
headers={'accept-encoding': ''}).headers['Content-Length']) / 1000000
print(file_size)
file_dir = Path(str(path).replace(" \ ".strip(), "/") + "/" + new_fullname)
size_eachsec = np.array([0.0, ])
my_scrollbar.pack(side=RIGHT, fill=Y)
lbl_fr = ttk.LabelFrame(second_frame, text=new_fullname, padding=5)
if len(new_fullname) > 20:
lbl_fr.config(text=new_fullname[:21] + "...")
download_prg = ttk.Progressbar(lbl_fr, orient=HORIZONTAL, length=365, mode='determinate', cursor="wait")
lbl_speed = Label(lbl_fr, text=None)
lbl_crnt_size = Label(lbl_fr, text=None)
lbl_file_size = Label(lbl_fr, text=f"File size: {round(file_size, 3)} MB")
lbl_percent = Label(lbl_fr, text="0 %")
lbl_cancel_btn = ttk.Button(lbl_fr, image=cancel_btn_icon)
lbl_see_more = Label(second_frame, text="See more", fg="grey", font=("Arial", 10))
download_prg.grid(row=0, column=0, columnspan=2)
lbl_speed.grid(row=0, column=2, padx=5)
lbl_crnt_size.grid(row=1, column=1)
lbl_file_size.grid(row=1, column=0)
lbl_percent.grid(row=1, column=2)
lbl_cancel_btn.grid(row=0, column=3, rowspan=2)
lbl_fr.grid(row=times_clicked, column=0, padx=5, pady=5)
lbl_see_more.grid(row=times_clicked, column=1, padx=3)
times_clicked += 1
start_time = time.time()
while True:
if file_dir.exists():
time.sleep(0.5)
crnt_size = file_dir.stat().st_size / 1000000
size_eachsec = np.append(size_eachsec, crnt_size)
percent = (crnt_size / file_size) * 100
crnt_speed = size_eachsec[1] - size_eachsec[0]
size_eachsec = np.delete(size_eachsec, 0)
lbl_speed.config(text=f"{round(crnt_speed, 2)} MB/s")
lbl_crnt_size.config(text=f"Downloaded: {round(crnt_size, 3)} MB")
lbl_percent.config(text=f"{round(percent, 2)} %")
download_prg["value"] = percent
# print( f"Current Size: {crnt_size}, Current speed: {crnt_speed}, File size: {file_size},
# Percentage: {percent}% " f", Progress bar: {download_prg['value']}%")
if crnt_size == file_size:
break
end_time = time.time()
time_elapsed = end_time - start_time
print(f"Start time: {start_time}, End time: {end_time}, Time took: {time_elapsed}")
download_prg.config(cursor="arrow")
lbl_speed.config(text="Completed!")
def show_info(event):
global info_window
if info_window is not None and info_window.winfo_exists():
info_window.focus_set()
lbl_see_more.config(fg="purple")
else:
lbl_see_more.config(fg="purple")
info_window = Toplevel()
info_window.resizable(0, 0)
lbl_time_took = Label(info_window, text=f"Time elapsed: {round(time_elapsed, 2)}")
lbl_avg_speed = Label(info_window, text=f"Average speed: {round(file_size / time_elapsed, 3)} MB/s")
lbl_time_took.pack(pady=20)
lbl_avg_speed.pack(pady=20)
lbl_see_more.bind("<Button-1>", show_info)
new_font = font.Font(lbl_see_more, lbl_see_more.cget("font"))
new_font.configure(size=10, underline=True)
lbl_see_more.config(font=new_font, fg="blue", cursor="hand2")
print("Download successful! ")
def download_files(status_bar, url_entry, output_entry, name_entry, format_entry, num, chum, var):
global files_downloading
files_downloading += 1
status_bar.config(text=f"Downloading {files_downloading} file(s)...")
url = str(url_entry.get())
if num.get() == 1:
name = url.split("/")[-1].split(".")[0]
else:
name = str(name_entry.get())
formatname = str(format_entry.get())
if var.get() == 1:
operator = str(url_entry.get())
formatname = '.' + operator[-3] + operator[-2] + operator[-1]
else:
pass
static_fullname = str(name) + formatname
new_fullname = static_fullname
path = (str(output_entry.get()) + "/").replace(" \ ".strip(), "/")
if chum.get() == 1:
conn = sqlite3.connect("DEF_PATH.db")
c = conn.cursor()
c.execute("SELECT * FROM DIRECTORY_LIST WHERE SELECTED_DEF = 1")
crnt_default_path = c.fetchall()
# print(crnt_default_path)
path = str(crnt_default_path[0][0] + "/").replace(" \ ".strip(), "/")
conn.commit()
conn.close()
else:
pass
if path + static_fullname not in dir_files.keys():
dir_files[path + static_fullname] = 0
all_files_dir = os.listdir(path)
# if fullname in all_files_dir:
while new_fullname in all_files_dir:
dir_files[path + static_fullname] += 1
if num.get() == 1:
name = url.split("/")[-1].split(".")[0] + f" ({dir_files.get(path + static_fullname)})"
else:
name = str(name_entry.get()) + f" ({dir_files.get(path + static_fullname)})"
new_fullname = name + formatname
else:
pass
print(dir_files)
Process(target=get_info, args=(path, new_fullname, url_entry)).start()
urllib.request.urlretrieve(url, path + new_fullname)
if len(new_fullname) > 20:
toast.show_toast(title="8-bit Downloader",
msg=f"{new_fullname[:21] + '...'} was successfully downloaded. to '{path}'"
, duration=6, threaded=True)
else:
toast.show_toast(title="8-bit Downloader", msg=f"{new_fullname} was successfully downloaded. to '{path}'"
, duration=6, threaded=True)
files_downloading -= 1
status_bar.config(text=f"Downloading {files_downloading} file(s)...")
if files_downloading == 0:
status_bar.config(text="Download(s) successful!")
if __name__ == '__main__':
root = Tk()
tabs = ttk.Notebook()
# the top menu
num = IntVar()
chum = IntVar()
var = IntVar()
toast = win10toast.ToastNotifier()
def start_download():
Process(target=download_files,
args=(status_bar, url_entry, output_entry, name_entry, format_entry, num, chum, var),
daemon=True).start()
# the status bar
status_bar = Label(main_window, text="Awaiting download...", bd=1, relief=SUNKEN, anchor=W)
status_bar.pack(side=BOTTOM, fill=X)
# the download frame
body_frame = Frame(main_window, bg="light blue")
download_button = ttk.Button(body_frame, text="Download! ", command=start_download, padding=40, style='my.TButton')
download_button_tip = CreateToolTip(download_button, "Initiates the downloading process. ")
download_button.pack(side=LEFT, pady=5, padx=5)
body_frame.pack(side=LEFT, fill=Y)
def clear_entry(entryname, variable=None):
if variable is not None:
variable.set(0)
entryname.config(state=NORMAL)
entryname.delete(0, END)
if entryname is format_entry:
entryname.insert(0, '.')
# the main interaction menu
inter_frame = Frame(main_window)
trash_icon = ImageTk.PhotoImage(Image.open("icons/Asset 6.png"))
settings_icon = ImageTk.PhotoImage(Image.open("icons/Settings-icon.png"))
add_icon = ImageTk.PhotoImage(Image.open("icons/Plus-icon-8.png"))
minus_icon = ImageTk.PhotoImage(Image.open("icons/Minus-icon-8.png"))
change_path_icon = ImageTk.PhotoImage(Image.open("icons/Change-icon-8.png"))
url_entry = ttk.Entry(inter_frame, width=30)
label = ttk.Label(inter_frame, text="Enter the image URL: ")
clr_url = ttk.Button(inter_frame, image=trash_icon, command=lambda: clear_entry(url_entry))
file_format = ttk.Label(inter_frame, text="Choose your file format: ")
format_entry = ttk.Entry(inter_frame, width=30)
clr_format = ttk.Button(inter_frame, image=trash_icon, command=lambda: clear_entry(format_entry, var))
file_name = ttk.Label(inter_frame, text="File's name: ")
name_entry = ttk.Entry(inter_frame, width=30)
clr_name = ttk.Button(inter_frame, image=trash_icon, command=lambda: clear_entry(name_entry, num))
check_name_manual = ttk.Radiobutton(inter_frame, text="Enter name manually", variable=num,
value=0, command=lambda: name_entry.config(state=NORMAL))
check_name_auto = ttk.Radiobutton(inter_frame, text="Download with default name", variable=num,
value=1, command=lambda: name_entry.config(state=DISABLED))
check_format_manual = ttk.Radiobutton(inter_frame, text="Enter format manually", variable=var,
value=0, command=lambda: format_entry.config(state=NORMAL))
check_format_auto = ttk.Radiobutton(inter_frame, text="Download with default format", variable=var,
value=1, command=lambda: format_entry.config(state=DISABLED))
output_path = ttk.Label(inter_frame, text="Choose output path: ")
output_entry = ttk.Entry(inter_frame, width=30)
def set_path():
directory = filedialog.askdirectory(initialdir="/Downloads", title="Choose path")
if directory:
output_entry.delete(0, END)
output_entry.insert(0, directory)
select_path_btn = ttk.Button(inter_frame, width=3, text="...", command=set_path)
select_path_btn_tip = CreateToolTip(select_path_btn, "Pops up a filedialog to change directory. ")
default_path_btn = ttk.Button(inter_frame, image=settings_icon, command=None)
check_default_manual = ttk.Radiobutton(inter_frame, text="Enter path manually", variable=chum,
value=0, command=lambda: output_entry.config(state=NORMAL))
check_default_auto = ttk.Radiobutton(inter_frame, text="Download to default path", variable=chum,
value=1, command=lambda: output_entry.config(state=DISABLED))
file_name.grid(row=0, column=0, pady=(15, 10))
name_entry.grid(row=0, column=1, pady=(15, 10))
clr_name.grid(row=0, column=2, pady=(5, 0))
check_name_manual.grid(row=1, column=0, padx=(10, 0))
check_name_auto.grid(row=1, column=1, padx=(10, 0))
label.grid(row=2, column=0, pady=10, padx=(10, 0))
url_entry.grid(row=2, column=1, pady=10, padx=(10, 0))
clr_url.grid(row=2, column=2)
file_format.grid(row=3, column=0, pady=10, padx=(10, 0))
format_entry.grid(row=3, column=1, pady=10, padx=(10, 0))
format_entry.insert(0, '.')
clr_format.grid(row=3, column=2)
check_format_manual.grid(row=4, column=0, padx=(10, 0))
check_format_auto.grid(row=4, column=1, padx=(10, 0))
output_path.grid(row=5, column=0, pady=10, padx=(10, 0))
output_entry.grid(row=5, column=1, padx=(10, 5))
select_path_btn.grid(row=5, column=2)
check_default_manual.grid(row=6, column=0, pady=(0, 10), padx=(10, 0))
check_default_auto.grid(row=6, column=1, pady=(0, 10), padx=(10, 0))
default_path_btn.grid(row=6, column=2, pady=(0, 5))
inter_frame.pack(expand=1)
main_window.pack(fill="both", expand=1)
# The Downloads tab (this part is irrelevant so I wouldn't bother checking this part out just put it there in case)
lbl_await = ttk.Label(download_fr, text="Awaiting Download...", font=("Helvetica", 24))
prg_br = ttk.Progressbar(download_fr, orient=HORIZONTAL, length=250, mode='indeterminate')
my_canvas = Canvas(download_fr)
my_scrollbar = ttk.Scrollbar(download_fr, orient=VERTICAL, command=my_canvas.yview)
my_canvas.configure(yscrollcommand=my_scrollbar.set)
lbl_await.pack(pady=(85, 8))
prg_br.pack()
prg_br.start(6)
my_canvas.pack(side=LEFT, fill=BOTH, expand=1)
download_fr.pack(fill="both", expand=1)
second_frame = Frame(my_canvas)
# Add that new frame to a window in the canvas
my_canvas.create_window(0, 0, window=second_frame, anchor='nw')
# update canvas scrollregion whenever the size of second_frame is changed
second_frame.bind('<Configure>', lambda e: my_canvas.configure(scrollregion=my_canvas.bbox('all')))
tabs.pack(fill="both")
tabs.add(main_window, text="Main Window")
tabs.add(download_fr, text="Downloads")
root.mainloop()
# the end!
I think your main problem is this:
TypeError: cannot pickle '_tkinter.tkapp' object
It looks like you are trying to pass Tk objects between the processes. I doubt that will ever work.
I am new to python and I am creating this app in python using tkinter and urllib. Basically, the program gets the URL, name, format and the path of the file then downloads it whenever the user hits the "Download" button. The problem is that without threading the program freezes until the download is finished and I don't want that to happen. So after a bit of research, I found out that threading can help solve this problem. After using threads, I found out that I can start a thread only once meaning that the user can only download one file.So I figured out that in order for the user to download multiple files and at a time, whenever the user hits the download button the program should create a new thread for that download and this even gives the user the ability to download multiple files at a time. The problem is that I have no clue how to write this algorithm. I would really appreciate it if someone helped me.
My code without threading:
from tkinter import *
from tkinter import font as tkFont
import random
import urllib.request
import requests
import wget
def printsth():
print("Yay it works! ")
def main_menu():
root = Tk()
root.title('8-bit downloader ')
root.iconbitmap(r"C:\Users\rayanravesh\PycharmProjects\GUI_Calculator\icon.ico")
root.geometry("600x300")
# the top menu
num = IntVar()
chum = IntVar()
var = IntVar()
menu = Menu(root)
root.config(menu=menu)
submenu = Menu(menu)
menu.add_cascade(label="Settings", menu=submenu)
def custom_op():
custom = Toplevel()
custom.iconbitmap(r"C:\Users\rayanravesh\PycharmProjects\GUI_Calculator\icon.ico")
submenu.add_command(label="Customization ", command=custom_op)
def settings_op():
global gps
set_win = Toplevel()
set_win.iconbitmap(r"C:\Users\rayanravesh\PycharmProjects\GUI_Calculator\icon.ico")
path_label = Label(set_win, text="Current default download path: ")
path_entry = Entry(set_win, width=30)
file_read = open('Data.txt', 'r')
data_base = file_read.read()
path_entry.insert(0, data_base)
file_read.close()
def default_output():
global location
file_read2 = open('Data.txt', 'r+')
file_read2.truncate(0)
file_read2.close()
write_file2 = open('Data.txt', 'w')
write_file2.write(path_entry.get())
write_file2.close()
location = path_entry.get() + "\\"
default_location = location.replace("\\", "\\\\")
path_btn = Button(set_win, text="Submit ", command=default_output)
path_label.pack(anchor=CENTER, expand=1)
path_entry.pack(anchor=CENTER, expand=1)
path_btn.pack(anchor=CENTER, expand=1)
submenu.add_command(label="Settings ", command=settings_op)
submenu.add_separator()
submenu.add_command(label="Exit", command=root.destroy)
# the section menu
editmenu = Menu(menu)
menu.add_cascade(label="Sections(soon)", menu=editmenu)
editmenu.add_command(label="Downloader", command=printsth)
editmenu.add_command(label="Converter", command=printsth)
editmenu.add_command(label="Media Player", command=printsth)
editmenu.add_command(label="Editor", command=printsth)
# the tool bar
toolbar = Frame(root, bg="light gray")
insert_button = Button(toolbar, text="Insert an image", command=printsth)
insert_button.pack(side=LEFT, padx=2, pady=2)
print_button = Button(toolbar, text="Print", command=printsth)
print_button.pack(side=LEFT, padx=2, pady=2)
toolbar.pack(side=TOP, fill=X)
# the download function
def download_image():
global formatname
if num.get() == 1:
name = random.randrange(1, 1000000)
else:
name = str(name_entry.get())
formatname = str(format_entry.get())
if var.get() == 1:
operator = str(url_entry.get())
formatname = '.' + operator[-3] + operator[-2] + operator[-1]
else:
pass
fullname = str(name) + formatname
url = str(url_entry.get())
fw = open('file-size.txt', 'w')
file_size = int(requests.head(url, headers={'accept-encoding': ''}).headers['Content-Length'])
fw.write(str(file_size))
fw.close()
path = str(output_entry.get()) + "\\"
if chum.get() == 1:
filee = open('Data.txt', 'r')
destination = filee.read()
path = destination + "\\"
output_entry.insert(0, destination)
filee.close()
else:
output_entry.delete(0, END)
urllib.request.urlretrieve(url, path.replace("\\", "\\\\") + fullname)
# the status bar
status_bar = Label(root, text="Downloading...", bd=1, relief=SUNKEN, anchor=W)
status_bar.pack(side=BOTTOM, fill=X)
# the download frame
body_frame = Frame(root, bg="light blue")
download_button = Button(body_frame, text="Download! ", command=download_image, border=3, width=20, height=5)
download_design = tkFont.Font(size=12, slant='italic')
download_button['font'] = download_design
download_button.pack(side=LEFT, pady=5, padx=5)
body_frame.pack(side=LEFT, fill=Y)
# the main interaction menu
inter_frame = Frame(root)
url_entry = Entry(inter_frame, width=30)
label = Label(inter_frame, text="Enter the image URL: ")
file_format = Label(inter_frame, text="Choose your file format: ")
format_entry = Entry(inter_frame, width=30)
file_name = Label(inter_frame, text="File's name: ")
name_entry = Entry(inter_frame, width=30)
check_name = Checkbutton(inter_frame, text="Give a random name", variable=num)
check_format = Checkbutton(inter_frame, text="Download with default format", variable=var)
check_default = Checkbutton(inter_frame, text="Download to default path", variable=chum)
check_default.deselect()
output_path = Label(inter_frame, text="Choose output path: ")
output_entry = Entry(inter_frame, width=30)
file_name.pack(anchor=CENTER, expand=1)
name_entry.pack(anchor=CENTER, expand=1)
check_name.pack(anchor=CENTER, expand=1)
label.pack(anchor=CENTER, expand=1)
url_entry.pack(anchor=CENTER, expand=1)
file_format.pack(anchor=CENTER, expand=1)
format_entry.pack(anchor=CENTER, expand=1)
format_entry.insert(0, '.')
check_format.pack(anchor=CENTER)
output_path.pack(anchor=CENTER, expand=1)
output_entry.pack(anchor=CENTER, expand=1)
check_default.pack(anchor=CENTER, expand=1)
inter_frame.pack(expand=1)
root.mainloop()
# the end!
main_menu()
Based on your code, you can simply change the following line in download_image() function:
urllib.request.urlretrieve(url, path.replace("\\", "\\\\") + fullname)
to:
threading.Thread(target=urllib.request.urlretrieve,
args=(url, path.replace("\\", "\\\\")+fullname), daemon=True).start()
Of course, you need to import threading.
UPDATE: to make download_image() run in thread, add a function:
def start_download():
threading.Thread(target=download_image, daemon=True).start()
and set the command option of download_button to this new function:
download_button = Button(body_frame, text="Download!",
command=start_download, border=3, width=20, height=5)
I am new to python and I am developing this simple downloader app that gets a URL form the user and downloads it to the place they specify. I want the download button to create a thread each time the user hits the button so that each thread would be responsible for one download so the user can download multiple items at a time. As a result, the program wouldn't freeze with each download but I have no idea how to do such thing. I have tried many ways but none of them seem to be working. I could really use some help.
Here is my code without threads:
from tkinter import *
from tkinter import font as tkFont
import random
import urllib.request
import requests
import wget
def printsth():
print("Yay it works! ")
def main_menu():
root = Tk()
root.title('8-bit downloader ')
root.iconbitmap(r"C:\Users\rayanravesh\PycharmProjects\GUI_Calculator\icon.ico")
root.geometry("600x300")
# the top menu
num = IntVar()
chum = IntVar()
var = IntVar()
menu = Menu(root)
root.config(menu=menu)
submenu = Menu(menu)
menu.add_cascade(label="Settings", menu=submenu)
def custom_op():
custom = Toplevel()
custom.iconbitmap(r"C:\Users\rayanravesh\PycharmProjects\GUI_Calculator\icon.ico")
submenu.add_command(label="Customization ", command=custom_op)
def settings_op():
global gps
set_win = Toplevel()
set_win.iconbitmap(r"C:\Users\rayanravesh\PycharmProjects\GUI_Calculator\icon.ico")
path_label = Label(set_win, text="Current default download path: ")
path_entry = Entry(set_win, width=30)
file_read = open('Data.txt', 'r')
data_base = file_read.read()
path_entry.insert(0, data_base)
file_read.close()
def default_output():
global location
file_read2 = open('Data.txt', 'r+')
file_read2.truncate(0)
file_read2.close()
write_file2 = open('Data.txt', 'w')
write_file2.write(path_entry.get())
write_file2.close()
location = path_entry.get() + "\\"
default_location = location.replace("\\", "\\\\")
path_btn = Button(set_win, text="Submit ", command=default_output)
path_label.pack(anchor=CENTER, expand=1)
path_entry.pack(anchor=CENTER, expand=1)
path_btn.pack(anchor=CENTER, expand=1)
submenu.add_command(label="Settings ", command=settings_op)
submenu.add_separator()
submenu.add_command(label="Exit", command=root.destroy)
# the section menu
editmenu = Menu(menu)
menu.add_cascade(label="Sections(soon)", menu=editmenu)
editmenu.add_command(label="Downloader", command=printsth)
editmenu.add_command(label="Converter", command=printsth)
editmenu.add_command(label="Media Player", command=printsth)
editmenu.add_command(label="Editor", command=printsth)
# the tool bar
toolbar = Frame(root, bg="light gray")
insert_button = Button(toolbar, text="Insert an image", command=printsth)
insert_button.pack(side=LEFT, padx=2, pady=2)
print_button = Button(toolbar, text="Print", command=printsth)
print_button.pack(side=LEFT, padx=2, pady=2)
toolbar.pack(side=TOP, fill=X)
# the download function
def download_image():
global formatname
if num.get() == 1:
name = random.randrange(1, 1000000)
else:
name = str(name_entry.get())
formatname = str(format_entry.get())
if var.get() == 1:
operator = str(url_entry.get())
formatname = '.' + operator[-3] + operator[-2] + operator[-1]
else:
pass
fullname = str(name) + formatname
url = str(url_entry.get())
fw = open('file-size.txt', 'w')
file_size = int(requests.head(url, headers={'accept-encoding': ''}).headers['Content-Length'])
fw.write(str(file_size))
fw.close()
path = str(output_entry.get()) + "\\"
if chum.get() == 1:
filee = open('Data.txt', 'r')
destination = filee.read()
path = destination + "\\"
output_entry.insert(0, destination)
filee.close()
else:
output_entry.delete(0, END)
urllib.request.urlretrieve(url, path.replace("\\", "\\\\") + fullname)
# the status bar
status_bar = Label(root, text="Downloading...", bd=1, relief=SUNKEN, anchor=W)
status_bar.pack(side=BOTTOM, fill=X)
# the download frame
body_frame = Frame(root, bg="light blue")
download_button = Button(body_frame, text="Download! ", command=download_image, border=3, width=20, height=5)
download_design = tkFont.Font(size=12, slant='italic')
download_button['font'] = download_design
download_button.pack(side=LEFT, pady=5, padx=5)
body_frame.pack(side=LEFT, fill=Y)
# the main interaction menu
inter_frame = Frame(root)
url_entry = Entry(inter_frame, width=30)
label = Label(inter_frame, text="Enter the image URL: ")
file_format = Label(inter_frame, text="Choose your file format: ")
format_entry = Entry(inter_frame, width=30)
file_name = Label(inter_frame, text="File's name: ")
name_entry = Entry(inter_frame, width=30)
check_name = Checkbutton(inter_frame, text="Give a random name", variable=num)
check_format = Checkbutton(inter_frame, text="Download with default format", variable=var)
check_default = Checkbutton(inter_frame, text="Download to default path", variable=chum)
check_default.deselect()
output_path = Label(inter_frame, text="Choose output path: ")
output_entry = Entry(inter_frame, width=30)
file_name.pack(anchor=CENTER, expand=1)
name_entry.pack(anchor=CENTER, expand=1)
check_name.pack(anchor=CENTER, expand=1)
label.pack(anchor=CENTER, expand=1)
url_entry.pack(anchor=CENTER, expand=1)
file_format.pack(anchor=CENTER, expand=1)
format_entry.pack(anchor=CENTER, expand=1)
format_entry.insert(0, '.')
check_format.pack(anchor=CENTER)
output_path.pack(anchor=CENTER, expand=1)
output_entry.pack(anchor=CENTER, expand=1)
check_default.pack(anchor=CENTER, expand=1)
inter_frame.pack(expand=1)
root.mainloop()
# the end!
main_menu()
Here is a code snippet that I used for something similar in combination with Tk:
import threading
def join_thread(thread):
thread.join(0)
if thread.is_alive():
# the thread has not finished yet -> handle it (e.g. check again after
# a certain time)
pass
def start_threaded(root, timeout):
thread = threading.Thread(
target= ..., # put your downloading function here
args=... # put your arguments here, e.g.: (URL, targetPath)
)
thread.start()
root.after(timeout, join_thread, thread)
Bind start_threaded to the according button and pass the following parameters: the root of your Tk application (we need it for checking if the thread has finished with root.after) and a timeout value in milliseconds after which we will check if the download has finished.
Note that I am not sure if Tk is thread-safe. So I would not access any widgets directly from the download threads but pass every information that is needed as an argument when creating the thread.