I just made a simple project with Tkinter GUI, but when I launch it and enter username, its window stops responding 'til the requests
and instaloader
processes are done, then it will be ok. Can I make a 'please wait' thing to avoid not responding? or does it get better if I migrate to another GUI?
from tkinter import *
import instaloader
from PIL import ImageTk, Image
import requests
from io import BytesIO
def insta(username):
L = instaloader.Instaloader()
profile = instaloader.Profile.from_username(L.context, username)
label2.config(text=profile.full_name)
label3.config(text=profile.biography)
url=profile.get_profile_pic_url()
response = requests.get(url)
img_data = response.content
img = ImageTk.PhotoImage(Image.open(BytesIO(img_data)))
panel = Label(image=img)
panel.place(x=150,y=100)
label4.config(text="Done")
window = Tk()
window.geometry("600x600")
window.maxsize(600, 600)
window.minsize(600, 600)
window.title("Instagram Profile Downloader")
# label
label = Label(window, text="Enter UserName to Download Profile Image:",
fg="black", bg="#f4b265")
label.place(x=180, y=20)
label2 = Label(window, text="")
label2.place(x=100, y=70)
label3 = Label(window, text="")
label3.place(x=100, y=100)
label4 = Label(window, text="", fg="red")
label4.place(x=380, y=50)
# button
def butt():
if input.get() == "":
label4.config(text="Please Enter Username")
return
else:
insta(input.get())
button = Button(window, text="Download", fg="white",
bg="#095e95", command=butt)
button.place(x=310, y=47)
# input
input = Entry(window)
input.place(x=180, y=50)
window.mainloop()
CodePudding user response:
Tkinter stuff is running in the main thread and so when you call something in the main thread that takes time the GUI will be blocked while that function is running. To solve this you need to use threads to make sure the call to insta is running separate from the main thread where tkinter is running but also need to make sure that you dont call tkinter functions on that other thread because tkinter only works in the main thread. Here's an exemple of how you could acheive it:
import threading
class InstaThread:
def __init__(self, username):
self.profile = None
self.img_data = None
# Disable the button while instaloader is running
button["text"] = "Loading..."
button["state"] = DISABLED
self.thread = threading.Thread(target=self.insta, args=(username,))
self.thread.start()
self.check_thread()
# Check periodically if the function has alread run
def check_thread(self):
if self.thread.is_alive():
window.after(100, self.check_thread)
else:
label2.config(text=self.profile.full_name)
label3.config(text=self.profile.biography)
panel = Label(window)
img = ImageTk.PhotoImage(Image.open(BytesIO(self.img_data)))
# Also updated this so that the image would really show in the panel
panel.image = img
panel.config(image = img)
panel.place(x=150,y=100)
label4.config(text="Done")
# Reset button
button["text"] = "Download"
button["state"] = NORMAL
# Function that will be running in other thread and updating the profile and img_data varibles of this class
def insta(self, username):
L = instaloader.Instaloader()
self.profile = instaloader.Profile.from_username(L.context, username)
url=self.profile.get_profile_pic_url()
response = requests.get(url)
self.img_data = response.content
Now in the Butt function you just need to call InstaThread(input.get())
instead of insta(input.get())
and like this when you click the button it will disable and say Loading while the function is running and the rest of the GUI will continue working fine
Another suggestion I have is for you to make a class for your GUI so that you have it all in one place and can access it anywhere inside the class so that you dont have to be having global variables for buttons and labels.