Alright, I am thoroughly confused.
So I recently started to use the Steam API and I decided to start out something simple, displaying the avatar image of a profile.
Thing is, the program runs with no errors except that it does not display the image.
Here is the code where it shows the image:
def displayImage():
global window
global STEAM_USER
response = urllib2.urlopen('http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=' + STEAM_API_KEY + '&steamids=' + STEAM_USER + '&format=xml')
htmlSource = response.read()
soup = BeautifulSoup(htmlSource)
avatar = soup.find('avatarfull').getText()
print avatar
image_bytes = urllib2.urlopen(avatar).read()
data_stream = io.BytesIO(image_bytes)
pil_image = Image.open(data_stream)
tk_image = ImageTk.PhotoImage(pil_image)
label = Label(window, image=tk_image)
label.pack(padx=5, pady=5)
And here is the rest of the code:
import urllib2
from Tkinter import *
from PIL import Image, ImageTk
from bs4 import BeautifulSoup
import io
STEAM_API_KEY = 'XXXX'
global window
window = Tk()
window.title('Steam Avatar Viewer')
window.geometry("215x215")
def newUser():
global window
global entry
entry = Entry(window)
button = Button(window, text='Search', width=10, command=getUser)
entry.pack()
button.pack()
def getUser():
global STEAM_USER
global entry
steamUser = entry.get()
steamConverterURL = 'http://www.steamidconverter.com/' + steamUser
steamIDURL = urllib2.urlopen(steamConverterURL)
steamIDSource = steamIDURL.read()
a = BeautifulSoup(steamIDSource)
for hit in a.findAll(attrs={'id':'steamID64'}):
STEAM_USER = hit.contents[0]
print STEAM_USER
displayImage()
def displayImage():
global window
global STEAM_USER
response = urllib2.urlopen('http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=' + STEAM_API_KEY + '&steamids=' + STEAM_USER + '&format=xml')
htmlSource = response.read()
soup = BeautifulSoup(htmlSource)
avatar = soup.find('avatarfull').getText()
print avatar
image_bytes = urllib2.urlopen(avatar).read()
data_stream = io.BytesIO(image_bytes)
pil_image = Image.open(data_stream)
tk_image = ImageTk.PhotoImage(pil_image)
label = Label(window, image=tk_image)
label.pack(padx=5, pady=5)
newUser()
window.mainloop()
I believe it is something very simple but I can't figure out what's causing the image not to display.
When you add a PhotoImage or other Image object to a Tkinter widget, you must keep your own reference to the image object. If you don’t, the image won’t always show up.
Here is essentially what I'm trying to say:
photo = PhotoImage(...)
label = Label(image=photo)
label.image = photo # keep a reference!
label.pack()
You can refer to this.
As has already been said, you must ensure you keep a reference to the image around, or else it will be removed by Python's garbage collector. In addition to the above method, the way I first learnt to overcome the issue was to simply append the image to a list, such as:
photo_list=[]
photo = PhotoImage(...)
photo_list.append(photo)
I liked this method at the time because it was obvious what the code was doing ( i.e. storing the picture to prevent it's deletion).
Either way, you must simply ensure your picture is kept around! Good luck :)
Related
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()
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.
Code:
import tkinter, urllib.request, json, io
from PIL import Image, ImageTk
main = tkinter.Tk()
main.geometry('500x500+800+300')
dogapi = urllib.request.urlopen(f'https://dog.ceo/api/breeds/image/random')
dogjson = dogapi.read()
dogdict = json.loads(dogjson)
url=dogdict['message']
m = urllib.request.urlopen(url)
mp = io.BytesIO(m.read())
mpi = Image.open(mp)
tkimg = ImageTk.PhotoImage(mpi)
l = tkinter.Label(main, image=tkimg)
b = tkinter.Button(main, text='Next Dog', command='do something to refresh the dog photo')
l.pack()
main.mainloop()
I have this code that gets a random dog photo and loads it in to a window, as well as a button.
This works fine but the "Next Dog" button doesn't actually do anything, and the dog photo almost never matches up with the window. How could I add functionality to the button, and make the dog photo size consistent?
You can put the fetching of the dog image in a function and update the image of the label inside the function. Then assign this function to the command option of the button.
import tkinter, urllib.request, json, io
from PIL import Image, ImageTk
main = tkinter.Tk()
main.geometry('500x500+800+300')
# maximum size of image
W, H = 500, 460
# resize image but keeping the aspect ratio
def resize_image(img):
ratio = min(W/img.width, H/img.height)
return img.resize((int(img.width*ratio), int(img.height*ratio)), Image.ANTIALIAS)
def fetch_image():
dogapi = urllib.request.urlopen(f'https://dog.ceo/api/breeds/image/random')
dogjson = dogapi.read()
dogdict = json.loads(dogjson)
url = dogdict['message']
m = urllib.request.urlopen(url)
mpi = resize_image(Image.open(m))
tkimg = ImageTk.PhotoImage(mpi)
l.config(image=tkimg) # show the image
l.image = tkimg # save a reference of the image to avoid garbage collection
# label to show the image
l = tkinter.Label(main, image=tkinter.PhotoImage(), width=W, height=H)
b = tkinter.Button(main, text='Next Dog', command=fetch_image)
l.pack()
b.pack()
fetch_image() # fetch first image
main.mainloop()
I am trying to iterate around a list of urls of images and display those images in tkinter. My code is:
root = tk.Tk()
for imgTag in outputS:
url = imgTag["src"]
with urllib.request.urlopen(url) as u:
raw_data = u.read()
im = Image.open(BytesIO(raw_data))
image = ImageTk.PhotoImage(im)
label = tk.Label(image=image)
label.pack( side = tk.BOTTOM)
root.mainloop()
However this displays only one of the images, but also seems to leave enough room for the other images. I am pretty sure the list of urls are correct as I have output them to the terminal and seen them.
Is the image that appears the last one? Is so, the problem is that you rebind im each loop and only keep the last one. All previous get garbage collected. See this. The solution is to keep a reference to each Iamge with each ImageTk widget.
root = tk.Tk()
for imgTag in outputS:
url = imgTag["src"]
with urllib.request.urlopen(url) as u:
raw_data = u.read()
im = Image.open(BytesIO(raw_data))
image = ImageTk.PhotoImage(im)
image.im = im # <<< new line
label = tk.Label(image=image)
label.pack( side = tk.BOTTOM)
root.mainloop()
I want to create a gui sub window that could look like this, which lists a live feedback for the top teams and also giving them a numbering.
So I have done for loop function to number them and Label them out, which works fine when run with nothing but the code but I'm having trouble making it run on the new window that is opened from main window.
import io
import base64
import Tkinter as tk
from Tkinter import *
from re import findall
from urllib2 import urlopen
def epl_Window():
epl = tk.Toplevel()
epl.title('Top Ten EPL Team 2016')
url = "http://i.imgur.com/3znCRqH.gif"
image_byte1 = urlopen(url).read()
data_stream1 = io.BytesIO(image_byte1)
pil_image1 = Image.open(data_stream1)
w, h = pil_image.size
tk_image1 = ImageTk.PhotoImage(pil_image1)
label1 = tk.Label(epl, image=tk_image1, bg='black')
label1.pack(padx=5, pady=5)
epl.geometry("500x700")
for index, value in enumerate(EPL_10, start=1):
Label(epl, text=str(index)).grid(row=index, column=1, sticky='W')
Label(epl, text=value).grid(row=index, column=2, sticky='W')
epl.mainloop()
root = tk.Tk()
root.title("Top Ten Lists")
url = "http://i.imgur.com/H1sURuR.gif"
image_bytes = urlopen(url).read()
# internal data file
data_stream = io.BytesIO(image_bytes)
# open as a PIL image object
pil_image = Image.open(data_stream)
# optionally show image info
# get the size of the image
w, h = pil_image.size
### convert PIL image object to Tkinter PhotoImage object
tk_image = ImageTk.PhotoImage(pil_image)
# put the image on a typical widget
label = tk.Label(root, image=tk_image, bg='brown')
label.pack(padx=5, pady=5)
#BUTTON
EPL_B = Button(root, text='EPL Teams', width=10, command=epl_Window)
EPL_B.pack(anchor = "w", side = LEFT)
#EPL RANK
url = 'http://www.bbc.com/sport/football/premier-league/table'
EPL_contents = urlopen(url).read()
EPL_Ranking = findall("'>(\w* ?\w*)</a>", EPL_contents)
EPL_10= EPL_Ranking[:10]
root.mainloop()
I think there is problem with def new window toplevel() but not sure how to change it to load. Running it make program crash
So it crashes when you click on the button which opens the TopLevel?
Without a traceback this is very difficult to answer, but my guess is that the line
epl.mainloop()
causes the crash. Even if it doesn't, you don't need to call it. There is usually only one mainloop at a time on your root widget.
please help to fix the script.
import os, sys
import tkinter
from PIL import ImageTk, Image
DIR_IMGS = 'imgs'
DIR_THUMBS = 'thumbs'
imgfiles = os.listdir(DIR_IMGS)
thumbfiles = os.listdir(DIR_THUMBS)
root = tkinter.Tk()
root.geometry('900x700')
links = []
def showItem(imgfile):
print(imgfile)
pathImg = os.path.join(DIR_IMGS, imgfile)
print(pathImg)
renderImg = ImageTk.PhotoImage(file=pathImg)
popup = tkinter.Toplevel()
tkinter.Button(popup, image=renderImg).pack()
def createThumbs():
for imgfile in imgfiles:
pathImg1 = os.path.join(DIR_IMGS, imgfile)
pathImg2 = os.path.join(DIR_THUMBS, imgfile)
openImg = Image.open(pathImg1)
openImg.thumbnail((100, 100))
openImg.save('thumbs/' + imgfile)
def outputButtons():
for thumbfile in thumbfiles:
pathImg = os.path.join(DIR_THUMBS, thumbfile)
renderImg = ImageTk.PhotoImage(file=pathImg)
but = tkinter.Button(root, image=renderImg)
but.pack(side='left')
but.bind('<Button-1>', lambda event, thumbfile=thumbfile: showItem(thumbfile))
links.append(renderImg)
createThumbs()
outputButtons()
root.mainloop()
I wrote the script, just the example of the popular book "Mark Lutz. Programming Python". but for some strange reason, my script does not work.
no manifest error as the screen is not an error message. but in pop-up window is not displayed (showing blank popup)
The (somewhat large) image is garbage collected before the window has a chance to display it.
You'll need to keep a reference around to the image to show it. I've taken the solution from here, and your showItem function can look as follows:
def showItem(imgfile):
print(imgfile)
pathImg = os.path.join(DIR_IMGS, imgfile)
print(pathImg)
renderImg = ImageTk.PhotoImage(file=pathImg)
popup = tkinter.Toplevel()
button = tkinter.Button(popup, image=renderImg)
button.image = renderImg
button.pack()