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.
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:
- Open the image with
PIL.Image.open(...)— this gives you a PIL image object. - Wrap it with
PIL.ImageTk.PhotoImage(...)— this converts it to a tkinter-compatible image. - Pass the result to a
tk.Labelas theimage=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()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, ImageTkThis 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.
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 errorsThe 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.
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.