Tkinter: How to load, display and replace image on Label, Button or Canvas
Example images
At start few images which I will use in examples. You can download them to use in own examples.
Reading image
Tkinter uses PhotoImage to read PNG, GIF, PGM/PPM.
Older version didn't read PNG. All versions still can't read JPG or other formats.
img = tk.PhotoImage(file="smile-1.png")
It has to use named variable file=. It can't skip this name.
It may also use named variable data= to use base64-encoded string with PNG or GIF data.
img = tk.PhotoImage(data="iVBORw0KGgoAAAANSUhEUgAAACMAAAAjAQMAAAAkFyEaAAAABlBMVEX///8AAABVwtN+AAAAJ0lEQVQI12P4DwQPGCDkAQYGhgRSSDv+BjwkqabZ/2/AQ+LVi+QLAGveQwjt4H11AAAAAElFTkSuQmCC")
At the end of page you can see how to convert file with image PNG or GIF to base64-encoded string
To read JPG you have to use module pillow
from PIL import ImageTk
img = ImageTk.PhotoImage(file="image.jpg")
or
from PIL import ImageTk, Image
img = ImageTk.PhotoImage(Image.open("image.jpg"))
# or
img = ImageTk.PhotoImage(image=Image.open("image.jpg"))
or
from PIL import ImageTk, Image
img = ImageTk.PhotoImage(data=open("image.jpg", "rb").read())
Image can be also used to modify image before displaying - ie. resize, crop, rotate, flip, convert to grayscale, etc.
from PIL import ImageTk, Image
image = Image.open("image.jpg")
image = image.resize((200,100)
img = ImageTk.PhotoImage(image)
More functions to modify image in pillow
In tk.PhotoImage (but not in ImageTk.PhotoImage) you can change image using
img['data'] = "iVBORw0KGgoAAAANSUhEUgAAACMAAAAjAQMAAAAkFyEaAAAABlBMVEUAAADw0gCjrW2CAAAAI0lEQVQI12NgQAL2////byCFPPihHg9JqmkHGOrxkHj1IgEAZH9nDhQLxPMAAAAASUVORK5CYII="
# or
img.config(data="iVBORw0KGgoAAAANSUhEUgAAACMAAAAjAQMAAAAkFyEaAAAABlBMVEUAAADw0gCjrW2CAAAAI0lEQVQI12NgQAL2////byCFPPihHg9JqmkHGOrxkHj1IgEAZH9nDhQLxPMAAAAASUVORK5CYII=")
or
img['file'] = "image.png"
# or
img.config(file="image.png")
You can also check values
print(img['file'])
print(img['data'])
# or
print(img.cget('file'))
print(img.cget('data'))
In tk.PhotoImage you can put one image on another. If both have the same size then one image replace other image. If you paste smaller image then you will see partially old image.
img.paste(Image.open("image.jpg"))
Displaying with Label
Now it can be displayed with tk.Label(..., image=...)
import tkinter as tk root = tk.Tk() img = tk.PhotoImage(file="smile-1.png") label = tk.Label(root, image=img) label.pack() root.mainloop()
The same with base64 string
import tkinter as tk root = tk.Tk() img = tk.PhotoImage(data="iVBORw0KGgoAAAANSUhEUgAAACMAAAAjAQMAAAAkFyEaAAAABlBMVEX///8AAABVwtN+AAAAJ0lEQVQI12P4DwQPGCDkAQYGhgRSSDv+BjwkqabZ/2/AQ+LVi+QLAGveQwjt4H11AAAAAElFTkSuQmCC") label = tk.Label(root, image=img) label.pack() root.mainloop()
The same with pillow.
Image can be used even before tk.Tk() or after root.mainloop() but PhotoImage() has to be used only after tk.Tk()
import tkinter as tk from PIL import Image, ImageTk, image = Image.open("image.jpg") image = image.resize((200,100) root = tk.Tk() img = ImageTk.PhotoImage(image) label = tk.Label(root, image=img) label.pack() root.mainloop()
To display more images you can use for-loop
import tkinter as tk root = tk.Tk() img = tk.PhotoImage(data="iVBORw0KGgoAAAANSUhEUgAAACMAAAAjAQMAAAAkFyEaAAAABlBMVEX///8AAABVwtN+AAAAJ0lEQVQI12P4DwQPGCDkAQYGhgRSSDv+BjwkqabZ/2/AQ+LVi+QLAGveQwjt4H11AAAAAElFTkSuQmCC") for y in range(3): for x in range(3): label = tk.Label(root, image=img) label.grid(row=y, column=x) root.mainloop()
To change image in Label you can create new PhotoImage
label['image'] = ImageTk.PhotoImage(image)
# or
label.config(image=ImageTk.PhotoImage(image))
You can also use existing PhotoImage
img = ImageTk.PhotoImage(image)
# later
label['image'] = img
# or
label.config(image=img)
You can also replace data in PhotImage like in "Reading image"
Displaying with Button
To display with tk.Button(..., image=...)
import tkinter as tk # --- functions --- def on_click(): print('clicked') # --- main --- root = tk.Tk() img = tk.PhotoImage(file="smile-1.png") button = tk.Button(root, image=img, command=on_click) button.pack() root.mainloop()
The same with base64 string
import tkinter as tk # --- functions --- def on_click(): print('clicked') # --- main --- root = tk.Tk() img = tk.PhotoImage(data="iVBORw0KGgoAAAANSUhEUgAAACMAAAAjAQMAAAAkFyEaAAAABlBMVEX///8AAABVwtN+AAAAJ0lEQVQI12P4DwQPGCDkAQYGhgRSSDv+BjwkqabZ/2/AQ+LVi+QLAGveQwjt4H11AAAAAElFTkSuQmCC") button = tk.Button(root, image=img, command=on_click) button.pack() root.mainloop()
To display more images
import tkinter as tk # --- functions --- def on_click(): print('clicked') # --- main --- root = tk.Tk() img = tk.PhotoImage(data="iVBORw0KGgoAAAANSUhEUgAAACMAAAAjAQMAAAAkFyEaAAAABlBMVEX///8AAABVwtN+AAAAJ0lEQVQI12P4DwQPGCDkAQYGhgRSSDv+BjwkqabZ/2/AQ+LVi+QLAGveQwjt4H11AAAAAElFTkSuQmCC") for y in range(3): for x in range(3): button = tk.Button(root, image=img, command=on_click) button.grid(row=y, column=x) root.mainloop()
To change image in Button you can do
button['image'] = ImageTk.PhotoImage(image)
# or
button.config(image=ImageTk.PhotoImage(image))
In this example I has to button['command'] to use button as argument in function.
import tkinter as tk # --- functions --- def on_click(widget): print('clicked') widget['image'] = img2 # --- main --- root = tk.Tk() img1 = tk.PhotoImage(data="iVBORw0KGgoAAAANSUhEUgAAACMAAAAjAQMAAAAkFyEaAAAABlBMVEX///8AAABVwtN+AAAAJ0lEQVQI12P4DwQPGCDkAQYGhgRSSDv+BjwkqabZ/2/AQ+LVi+QLAGveQwjt4H11AAAAAElFTkSuQmCC") img2 = tk.PhotoImage(data="iVBORw0KGgoAAAANSUhEUgAAACMAAAAjAQMAAAAkFyEaAAAABlBMVEUAAADw0gCjrW2CAAAAI0lEQVQI12NgQAL2////byCFPPihHg9JqmkHGOrxkHj1IgEAZH9nDhQLxPMAAAAASUVORK5CYII=") for y in range(3): for x in range(3): button = tk.Button(root, image=img1) button['command'] = lambda arg=button:on_click(arg) button.grid(row=y, column=x) root.mainloop()
To change image in Button you can create new PhotoImage
button['image'] = ImageTk.PhotoImage(image)
# or
button.config(image=ImageTk.PhotoImage(image))
You can also use existing PhotoImage
img = ImageTk.PhotoImage(image)
# later
button['image'] = img
# or
button.config(image=img)
You can also replace data in PhotImage like in "Reading image"
Displaying with Canvas
To display on Canvas you have to use canvas.create_image((x,y), image=...), not pack()/grid()/place().
item_id = canvas.create_image((0,0), image=img)
It returns object's ID which later you can use to access this image (and move, remove, replace, etc.).
If you will not modify image then you don't have to assign to variable.
import tkinter as tk
root = tk.Tk()
img = tk.PhotoImage(data="iVBORw0KGgoAAAANSUhEUgAAACMAAAAjAQMAAAAkFyEaAAAABlBMVEUAAADw0gCjrW2CAAAAI0lEQVQI12NgQAL2////byCFPPihHg9JqmkHGOrxkHj1IgEAZH9nDhQLxPMAAAAASUVORK5CYII=")
canvas = tk.Canvas(root)
canvas.pack()
canvas.create_image((0, 0), image=img)
root.mainloop()
Image uses center point as anchor so image on screenshot above is visible only partially because center of image is in position (0, 0). To put top, left corner of image in position (0, 0) you have to use anchor='nw' ('nw' means North West, top left).
item_id = canvas.create_image((0,0), image=img, anchor='nw')
import tkinter as tk
root = tk.Tk()
img = tk.PhotoImage(data="iVBORw0KGgoAAAANSUhEUgAAACMAAAAjAQMAAAAkFyEaAAAABlBMVEUAAADw0gCjrW2CAAAAI0lEQVQI12NgQAL2////byCFPPihHg9JqmkHGOrxkHj1IgEAZH9nDhQLxPMAAAAASUVORK5CYII=")
canvas = tk.Canvas(root)
canvas.pack()
canvas.create_image((0, 0), image=img)
root.mainloop()
- Anchor 'nw' (and center point) is the most popular but you can also use other values
- 'ne' (North East, top right), 'se' (South East, bottom right), 'sw' (South West, bottom left),
This example uses different anchors to easily put images in corners. Image has size (300, 300) and images can use values 0 and 300
import tkinter as tk
root = tk.Tk()
img = tk.PhotoImage(data="iVBORw0KGgoAAAANSUhEUgAAACMAAAAjAQMAAAAkFyEaAAAABlBMVEX///8AAABVwtN+AAAAJ0lEQVQI12P4DwQPGCDkAQYGhgRSSDv+BjwkqabZ/2/AQ+LVi+QLAGveQwjt4H11AAAAAElFTkSuQmCC")
canvas = tk.Canvas(root, width=200, height=200)
canvas.pack()
canvas.create_image((100, 100), image=img) # center
canvas.create_image((0, 0), image=img, anchor='nw') # top, left corner
canvas.create_image((200, 0), image=img, anchor='ne') # top, right corner
canvas.create_image((200, 200), image=img, anchor='se') # bottom, right corner
canvas.create_image((0, 200), image=img, anchor='sw') # bottom, left corner
root.mainloop()
To change image in Canvas you have to use canvas.itemconfig() with object_id or tag
canvas.itemconfig(object_id, image=ImageTk.PhotoImage(image))
# or
img = ImageTk.PhotoImage(image)
# later
canvas.itemconfig(object_id, image=img)
This example uses object ID to change image
import tkinter as tk
# --- functions ---
def on_click():
canvas.itemconfig(img_id, image=img2)
# --- main ---
root = tk.Tk()
img1 = tk.PhotoImage(data="iVBORw0KGgoAAAANSUhEUgAAACMAAAAjAQMAAAAkFyEaAAAABlBMVEX///8AAABVwtN+AAAAJ0lEQVQI12P4DwQPGCDkAQYGhgRSSDv+BjwkqabZ/2/AQ+LVi+QLAGveQwjt4H11AAAAAElFTkSuQmCC")
img2 = tk.PhotoImage(data="iVBORw0KGgoAAAANSUhEUgAAACMAAAAjAQMAAAAkFyEaAAAABlBMVEUAAADw0gCjrW2CAAAAI0lEQVQI12NgQAL2////byCFPPihHg9JqmkHGOrxkHj1IgEAZH9nDhQLxPMAAAAASUVORK5CYII=")
canvas = tk.Canvas(root, width=200, height=200)
canvas.pack()
img_id = canvas.create_image((100, 100), image=img1) # center
button = tk.Button(root, text='CHANGE', command=on_click)
button.pack()
root.mainloop()
This example create three image with the same tag "smile" and later it uses tag "smile" to replace PhotoImage in all images.
import tkinter as tk
# --- functions ---
def on_click():
canvas.itemconfig("smile", image=img2)
# --- main ---
root = tk.Tk()
img1 = tk.PhotoImage(data="iVBORw0KGgoAAAANSUhEUgAAACMAAAAjAQMAAAAkFyEaAAAABlBMVEX///8AAABVwtN+AAAAJ0lEQVQI12P4DwQPGCDkAQYGhgRSSDv+BjwkqabZ/2/AQ+LVi+QLAGveQwjt4H11AAAAAElFTkSuQmCC")
img2 = tk.PhotoImage(data="iVBORw0KGgoAAAANSUhEUgAAACMAAAAjAQMAAAAkFyEaAAAABlBMVEUAAADw0gCjrW2CAAAAI0lEQVQI12NgQAL2////byCFPPihHg9JqmkHGOrxkHj1IgEAZH9nDhQLxPMAAAAASUVORK5CYII=")
canvas = tk.Canvas(root, width=200, height=200)
canvas.pack()
canvas.create_image((50, 100), image=img1, tag="smile")
canvas.create_image((100, 100), image=img1, tag="smile") # center
canvas.create_image((150, 100), image=img1, tag="smile")
button = tk.Button(root, text='CHANGE', command=on_click)
button.pack()
root.mainloop()
You can also replace data in PhotoImage like in "Reading image". It will change data in all images which use the same PhotoImage.
import tkinter as tk
# --- functions ---
def on_click():
img['data'] = "iVBORw0KGgoAAAANSUhEUgAAACMAAAAjAQMAAAAkFyEaAAAABlBMVEUAAADw0gCjrW2CAAAAI0lEQVQI12NgQAL2////byCFPPihHg9JqmkHGOrxkHj1IgEAZH9nDhQLxPMAAAAASUVORK5CYII="
# --- main ---
root = tk.Tk()
img = tk.PhotoImage(data="iVBORw0KGgoAAAANSUhEUgAAACMAAAAjAQMAAAAkFyEaAAAABlBMVEX///8AAABVwtN+AAAAJ0lEQVQI12P4DwQPGCDkAQYGhgRSSDv+BjwkqabZ/2/AQ+LVi+QLAGveQwjt4H11AAAAAElFTkSuQmCC")
canvas = tk.Canvas(root, width=200, height=200)
canvas.pack()
canvas.create_image((50, 100), image=img)
canvas.create_image((100, 100), image=img) # center
canvas.create_image((150, 100), image=img)
button = tk.Button(root, text='CHANGE', command=on_click)
button.pack()
root.mainloop()
Creating base64 string
To create base64 string you have to - read PNG or GIF in bytes mode rb - convert to base64 to get it as bytes - convert bytes to string
fh = open('smile.png', 'rb') # open in `bytes` mode
data = fh.read() # read all
data = base64.b64encode(data) # create byte64 `bytes`
data = data.decode() # convert `bytes` to `string`
or shorter
data = base64.b64encode(open('smile.png', 'rb').read()).decode()
Bug in PhotoImage
There is bug in PhotoImage which removes image from memory (so it is not displayed) when it is assigned to local variable created in function or class method.
It has to be assigned to global variable
def function():
global img
img = tk.PhotoImage(data="iVBORw0KGgoAAAANSUhEUgAAACMAAAAjAQMAAAAkFyEaAAAABlBMVEX///8AAABVwtN+AAAAJ0lEQVQI12P4DwQPGCDkAQYGhgRSSDv+BjwkqabZ/2/AQ+LVi+QLAGveQwjt4H11AAAAAElFTkSuQmCC")
or class variable
def function():
img = tk.PhotoImage(data="iVBORw0KGgoAAAANSUhEUgAAACMAAAAjAQMAAAAkFyEaAAAABlBMVEX///8AAABVwtN+AAAAJ0lEQVQI12P4DwQPGCDkAQYGhgRSSDv+BjwkqabZ/2/AQ+LVi+QLAGveQwjt4H11AAAAAElFTkSuQmCC")
label = tk.Label(image=img)
label.image = img # keep reference (it can be different name - ie. `label.img`)
label.pack()
or using self. in class
class MyWindow:
def method(self):
self.img = tk.PhotoImage(data="iVBORw0KGgoAAAANSUhEUgAAACMAAAAjAQMAAAAkFyEaAAAABlBMVEX///8AAABVwtN+AAAAJ0lEQVQI12P4DwQPGCDkAQYGhgRSSDv+BjwkqabZ/2/AQ+LVi+QLAGveQwjt4H11AAAAAElFTkSuQmCC")
Links:
- [web.archive.org] effbot.org: PhotoImage
- [web.archive.org] effbot.org: Label
- [web.archive.org] effbot.org: Button
- [web.archive.org] effbot.org: Canvas
Buy a Coffee