Search code examples
pythontkinterwidgetpython-imaging-libraryurllib

Python 3 tkinter | Image from URL not displaying


First of all, my code is based on this StackOverflow answer.

Due to circumstance, I am trying to have most of the code packed into a function that I call ImgFromUrl()

import io
import tkinter as tk
import urllib.request
from PIL import Image, ImageTk

def ImgFromUrl(root, url):
    with urllib.request.urlopen(url) as connection:
        raw_data = connection.read()
    im = Image.open(io.BytesIO(raw_data))
    image = ImageTk.PhotoImage(im)
    return tk.Label(root, image=image)

root = tk.Tk()
url = "http://imgs.xkcd.com/comics/python.png"

widget = ImgFromUrl(root,url)
widget.grid(row=0,column=0)

root.mainloop()

And for some reason, the image does not show up (though the Tkinter window was automatically resized to the size of the image).

However, this works:

# same imports

root = tk.Tk()
url = "http://imgs.xkcd.com/comics/python.png"

with urllib.request.urlopen(url) as connection:
    raw_data = connection.read()
im = Image.open(io.BytesIO(raw_data))
image = ImageTk.PhotoImage(im)

widget = tk.Label(root, image=image)
widget.grid(row=0,column=0)

root.mainloop()

Solution

  • So your issue is because image is deleted as soon as the function end and tkinter needs the image to be saved for a reference somewhere.

    We can do this with global or by returning the image to a variable defined in the global.

    Option 1 global:

    import tkinter as tk
    import urllib.request
    from PIL import Image, ImageTk
    import io
    
    
    def ImgFromUrl(url):
        global image
        with urllib.request.urlopen(url) as connection:
            raw_data = connection.read()
        im = Image.open(io.BytesIO(raw_data))
        image = ImageTk.PhotoImage(im)
        return image
    
    root = tk.Tk()
    url = "http://imgs.xkcd.com/comics/python.png"
    widget = tk.Label(root, image=ImgFromUrl(url))
    widget.grid(row=0, column=0)
    
    root.mainloop()
    

    Option 2 returning the image object to a variable defined in the global namespace:

    import tkinter as tk
    import urllib.request
    from PIL import Image, ImageTk
    import io
    
    
    def ImgFromUrl(url):
        with urllib.request.urlopen(url) as connection:
            raw_data = connection.read()
        im = Image.open(io.BytesIO(raw_data))
        image = ImageTk.PhotoImage(im)
        return image
    
    root = tk.Tk()
    url = "http://imgs.xkcd.com/comics/python.png"
    image = ImgFromUrl(url)
    widget = tk.Label(root, image=image)
    widget.grid(row=0, column=0)
    
    root.mainloop()