URL de una imagen en TKinter

Usar una imagen de una URL sin descargarla en TKinter (Python)

Respuesta corta

Si no debemos modificar la imagen:


"""
Image from wikipedia

Permission details
Commons:Deletion_requests/File:Python_logo.svg, https://www.python.org/psf/trademarks/
"""

import requests
import tkinter
from PIL import ImageTk

url = 'https://upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Python-logo-notext.svg/800px-Python-logo-notext.png'
response = requests.get(url)

window = tkinter.Tk()

img = ImageTk.PhotoImage(data=response.content)
tkinter.Label(window, image=img).pack()

window.mainloop()

Si necesitamos modificar la imagen:


"""
Image from wikipedia

Permission details
Commons:Deletion_requests/File:Python_logo.svg, https://www.python.org/psf/trademarks/
"""

import requests
import tkinter
from PIL import ImageTk, Image

url = 'https://upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Python-logo-notext.svg/800px-Python-logo-notext.png'
response = requests.get(url, stream=True)
image = Image.open(response.raw)

new_size = (50, 50)
resized_image = image.resize(new_size)

window = tkinter.Tk()

image_tk = ImageTk.PhotoImage(image=resized_image)
tkinter.Label(window, image=image_tk).pack()

window.mainloop()

Respuesta larga

Mostrar una imagen en la GUI de nuestra aplicación es muy sencillo con TKinter: instanciamos la subclase "tkinter.Image" y le pasamos como parámetro la ruta al archivo en cuestión; luego usamos el objeto en el Label correspondiente y volià!

img = PhotoImage(file='images/sasuke.png')
Label(image=img).pack()

Pero... ¿Y si solo tenemos la URL de la imagen?

Lo primero que se nos puede ocurrir es que podríamos descargar la imagen al PC en cuestión, y luego abrirlo de manerla normal. Para ello usaríamos la librería "requests". Como el archivo puede ser muy pesado, se recomienda hacer la descarga con el modo "stream" activado (mantiene la conexión hasta indicar el cierre) para así ir descargando el contenido de poco a poco con ".iter_content()".

response = requests.get(url, stream=True)
with open('image.jpg', 'wb') as file:
for chunk in response.iter_content():
file.write(chunk)
response.close()

Pero esto nos genera más problemas: ahora tenemos un archivo en el ordenador que no nos interesa. Podríamos borrarlo, pero sería agregar más librerías innecesarías. ¿No sería más lógico usar el archivo cargado solamente en la RAM?

Es aquí donde entra la magia de la libreria PILLOW que nos permite trabajar con imágenes en Python. Incluso tiene un módulo reservado para imágenes de TKinter, y es allí donde encontramos la solución: en su parámetro "data".

class PIL.ImageTk.PhotoImage(image=None, size=None, **kw)
A Tkinter-compatible photo image. This can be used everywhere Tkinter expects an image object.

Parameters
  • image – Either a PIL image, or a mode string. If a mode string is used, a size must also be given.
  • size – If the first argument is a mode string, this defines the size of the image.
  • file – A filename to load the image from (using Image.open(file)).
  • data – An 8-bit string containing image data (as loaded from an image file).

Cuando instanciamos la ImageTK, Pillow nos provee de la capacidad de generarlo directamente del contenido de la imágen en formato de texto plano, así que pasándole el contenido del "response" es suficiente.

import requests
import tkinter
from PIL import ImageTk

url = 'https://upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Python-logo-notext.svg/800px-Python-logo-notext.png'
response = requests.get(url)

window = tkinter.Tk()

img = ImageTk.PhotoImage(data=response.content)
tkinter.Label(window, image=img).pack()

window.mainloop()

Ahora ya tenemos la imagen lista para ser utilizada en el "Label" correspondiente. Pero tenemos un pequeño problema: la imagen puede ser demasiado grande y no podemos encogerla.

La subclase "ImageTk" que nos provee Pillow no tiene ningún método para poder modificar su tamaño, sin embargo la clase primaria "Image" contiene el el método ".resize()" que retorna una imagen con un tamaño diferente.

Por lo tanto, en el caso de que quieras usar en TKinter imágenes desde su URL pero que éstas sean demasiado grandes para tu aplicación deberías primero crear la imagen con la clase "Image", modificar su tamaño, y luego instanciar un nuevo objeto de clase "ImageTk". Pero "Image" no trabaja de la misma forma que "ImageTk".

Este otra clase no trabaja con ningún parámetro que lea directamente los datos del archivo, sino que deberás utilizar su método ".open()", donde deberías poner la ruta a la imagen de tu PC. Por suerte este método admite el contenido de la URL pero solo en bruto ("response.raw").

import requests
import tkinter
from PIL import ImageTk, Image

url = 'https://upload.wikimedia.org/wikipedia/commons/thumb/c/c3/Python-logo-notext.svg/800px-Python-logo-notext.png'
response = requests.get(url, stream=True)
image = Image.open(response.raw)

new_size = (50, 50)
resized_image = image.resize(new_size)

window = tkinter.Tk()

image_tk = ImageTk.PhotoImage(image=resized_image)
tkinter.Label(window, image=image_tk).pack()

window.mainloop()

Comentarios

Entradas populares de este blog

¿Terminal o Shell? Qué son y en qué se diferencian

Café con Bits 11 ☕ Un truco para aprender CUALQUIER cosa

Café con Bits 9 ☕ La línea que separa el arte de la ciencia