Skip to content

Instantly share code, notes, and snippets.

@dandrake
Last active April 20, 2026 19:08
Show Gist options
  • Select an option

  • Save dandrake/cc466cb8f5c938a381cde3d880566c61 to your computer and use it in GitHub Desktop.

Select an option

Save dandrake/cc466cb8f5c938a381cde3d880566c61 to your computer and use it in GitHub Desktop.

Displaying Images in Tkinter: PNG vs JPG

The Simple Case: PNG Files with tk.PhotoImage

Tkinter has built-in support for a few image formats — PNG, GIF, and PPM/PGM — through tk.PhotoImage. For a PNG file, no third-party library is needed:

import tkinter as tk

def main():
    win = tk.Tk()
    win.geometry("400x400")

    tkpi = tk.PhotoImage(file="photo.png")

    img_widget = tk.Label(win, image=tkpi)
    img_widget.grid(row=0, column=0)

    win.mainloop()

if __name__ == "__main__":
    main()

This is the simplest possible approach and works without any additional packages.

Important: tk.PhotoImage does not support JPEG files. If you try to load a .jpg with it, you will get a cryptic error like couldn't recognize data in image file.

Loading JPG Files: You Need PIL/Pillow

To load a JPEG (or any other format tkinter doesn't natively support), you need the Pillow library (pip install pillow). Pillow provides PIL.ImageTk.PhotoImage, which wraps a PIL image into something tkinter can display.

The workflow is:

  1. Open the image with PIL.Image.open(...) — this gives you a PIL image object.
  2. Wrap it with PIL.ImageTk.PhotoImage(...) — this converts it to a tkinter-compatible image.
  3. Pass the result to a tk.Label as the image= argument, just like the PNG case.
import tkinter as tk
from PIL import Image, ImageTk

def main():
    win = tk.Tk()
    win.geometry("400x400")

    pil_image = Image.open("photo.jpg")
    tk_image = ImageTk.PhotoImage(pil_image)

    img_widget = tk.Label(win, image=tk_image)
    img_widget.grid(row=0, column=0)

    win.mainloop()

if __name__ == "__main__":
    main()

Why import PIL Doesn't Work (a Python Packaging Gotcha)

You might expect that after import PIL, you could write PIL.Image.open(...) and PIL.ImageTk.PhotoImage(...). This does not work:

import PIL                          # only loads PIL/__init__.py
pil_image = PIL.Image.open("x.jpg")  # AttributeError: module 'PIL' has no attribute 'Image'

The reason is that PIL is a package (a directory of submodules), and import PIL only runs PIL/__init__.py, which does not automatically import Image, ImageTk, or any other submodule. Those submodules only get loaded when you explicitly import them.

You have two options:

Option A — from PIL import ... (recommended)

from PIL import Image, ImageTk

This is the conventional and cleanest approach. After this, you write Image.open(...) and ImageTk.PhotoImage(...).

Option B — import PIL.Image and import PIL.ImageTk

import PIL.Image
import PIL.ImageTk

pil_image = PIL.Image.open("photo.jpg")
tk_image = PIL.ImageTk.PhotoImage(pil_image)

This makes the full dotted names available and can be useful when you want the code to be explicit about which Image you mean (e.g., when another library in scope also has something named Image). The downside is more verbose import lines.

The key rule: submodules must be explicitly imported; importing a package does not automatically load its submodules.

Another Pitfall: Only One tk.Tk() Per Program

Tkinter only supports one root window, created by tk.Tk(). If any imported module also calls tk.Tk() at import time (as a side effect of being imported), you will end up with two root windows, which causes confusing errors.

Watch out for this if you import a helper library that uses tkinter internally. For example, if you have a module that does this at the top level:

# inside some_helper.py
import tkinter as tk
root = tk.Tk()   # runs at import time!
root.withdraw()

Then in your main file:

from some_helper import *   # triggers tk.Tk() — now there are two!

win = tk.Tk()               # second tk.Tk() → weird errors

The fix is to not import that module if you don't need it, or to restructure the helper so it doesn't call tk.Tk() at import time.

Working Example (JPG, clean version)

import tkinter as tk
from PIL import Image, ImageTk

def main():
    win = tk.Tk()
    win.geometry("400x400")

    label = tk.Label(win, text="Here is an image.")
    label.grid(row=0, column=0)

    pil_image = Image.open("photo.jpg")
    tk_image = ImageTk.PhotoImage(pil_image)

    img_widget = tk.Label(win, image=tk_image)
    img_widget.grid(row=1, column=0)

    win.mainloop()

if __name__ == "__main__":
    main()

Replace "photo.jpg" with the path to your image file.


The current version of this document is available in this gist.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment