tkinter: why images are not dysplayed correctly - python

The code:
from tkinter import *
root = Tk()
f1=Frame(root)
for img,rlf in [ ('woman',RAISED),('mensetmanus',SOLID),
('terminal',SUNKEN), ('escherknot',FLAT),
('calculator',GROOVE),('letters',RIDGE)]:
filename = img + ".gif"
img1 = PhotoImage(file= filename)
Label(f1, image = img1, relief=rlf).pack(side=LEFT,
padx=5)
f1.pack()
root.mainloop()
Could you help me understand why this excerpt produces 5 empty places for images (though borders are drawn correctly according to what was meant), and 1 image. The last image (which is visible) is letters. And it seems to be cropped from than its actual size. My letters.gif contains letters from A to G, but this code displays only from half B to half F.

It does not work, i think, because img1 is overwritten in each loop. You need to keep references to image objects somewhere, so that garbage collector wont trash them:
from tkinter import *
root = Tk()
f1=Frame(root)
img_list = [] #<-- store references to images
for img,rlf in [ ('woman',RAISED),('mensetmanus',SOLID),
('terminal',SUNKEN), ('escherknot',FLAT),
('calculator',GROOVE),('letters',RIDGE)]:
filename = img + ".gif"
img1 = PhotoImage(file= filename)
img_list.append(img1) #<-- store references to images
Label(f1, image = img1, relief=rlf).pack(side=LEFT,
padx=5)
f1.pack()
root.mainloop()

Related

Generate Tkinter Canvas In Loop with Image

I'm attempting to create a series of canvases, each displaying a different section of an image according to the coordinates present in a list. The number of different coordinates is dynamic so it must be done in a loop.
The issue I am having is even though I'm saving my references garbage collection is preventing all canvases except the last from displaying an image.
Here is what I have so far:
import tkinter as tk
import cv2
from PIL import Image, ImageTk
class App(object):
def __init__(self):
self.root = tk.Tk()
# Candidates holds the coordinates for the image sections, and is hardcoded for this example
candidates = [[0,100,100,100], [15,15,200,200], [30,30,200,200], [50,50,200,200], [100,100,200,200], [200,200,200,200]]
self.im = cv2.imread(r"....")
j = 0
i = 0
frames = []
images = []
refs = []
for candidate in candidates:
x,y,w,h = tuple(candidate)
self.img_tk = ImageTk.PhotoImage(Image.fromarray(self.im[y:y+h, x:x+w]))
Frame = tk.Canvas(self.root,bg='white', highlightthickness=1, highlightbackground="black", width=100, height= 100)
Frame.grid(row = i, column = j, sticky = 'w, e, n, s', padx=5, pady=5)
ref = Frame.create_image(0, 0, image=self.img_tk, anchor="nw")
images.append(self.img_tk)
refs.append(ref)
frames.append(Frame)
if j<2:
j+=1
else:
j=0
i+=1
app = App()
app.root.mainloop()
and the result:
As you can see, only the last frame in the loop has an image created. Any thoughts are greatly appreciated, thanks.
It is because images is a local variable, so it will be garbage collected after the function exits, so are the image references stored in it. The last image can be shown because self.img_tk saves the reference.
To solve it, change images to instance variable self.images.

Adding multiple images from a list in Tkinter Python

I have a few images that I would like to display. I have tried storing the labels in a list, however when I call pack() only the last png shows like so:
How can I make them all appear?
import os
icons = []
for f in os.listdir():
if f.endswith('.png'):
print(f)
img = Image.open(f)
resized = img.resize((16, 16), Image.ANTIALIAS)
new_img = ImageTk.PhotoImage(resized)
label_img = tk.Label(root, image=new_img)
icons.append(label_img)
label_img.pack()

How to display one image after another using .after()

I am trying to display an image to the window based on a phrase. If the character in the phrase matches the name of the picture, it should be displayed on the window. If the phrase has more than one character, the picture related to the character after the current character should be displayed to the right of the current character's picture. How would I be able to make it so that a new side-by side picture duo shows up, this time showing the picture related to the second character on the left and the third character on the right, and so on? I tried using .after() but I am not sure where to place it.
Also, when I run the following code I get an index out of range error unless I use the break statement.but I can't seem to figure out what it is. Maybe using the after() method will fix it?
import tkinter as tk
from PIL import ImageTk, Image, ImageDraw, ImageFont
import time
def open_image():
global i
global image
global img
global image2
global img2
if i < len(phrase):
if phrase[i] == " ":
image = Image.open("Rest.jpg")
img = ImageTk.PhotoImage(image)
panel['image'] = img
else:
image = Image.open(phrase[i] + ".jpg")
img = ImageTk.PhotoImage(image)
panel['image'] = img
if phrase[i + 1] != None and phrase[i + 1] != " ":
image2 = Image.open(phrase[i + 1] + ".jpg")
panel2['image2'] = img2
else:
image2 = Image.open("Rest1.jpg")
panel2['image2'] = img2
i += 1
window.after(2000, open_image)
else:
window.destroy()
# --- main ---
i = 0
phrase = " trac "
window = tk.Tk()
panel = tk.Label(window) # without image
panel.pack(side = "left", fill = "both", expand = "1", padx = 5, pady = 5)
panel2 = tk.Label(window)
panel2.pack(side = "left", fill = "both", expand = "1", padx = 5, pady = 5)
# use it after creating Label so it can set image
open_image()
window.mainloop()
I reduced code to create working example.
after() uses function's name without ()
open_image() loads image, assigns to global variable (to resolve problem with bug in PhotoImage) and changes image in Label
At start I create Label without image and open_image() adds first image, and later it change this image to new one.
import tkinter as tk
from PIL import Image, ImageTk
# --- functions ---
def open_image():
global i
global image
global img
if i < len(phrase): # check at the beginning to resolve problem with `IndexError: list index out of range`
image = Image.open(phrase[i] + ".jpg")
img = ImageTk.PhotoImage(image)
panel['image'] = img
i += 1
window.after(2000, open_image)
else:
#window.quit()
window.destroy()
# --- main ---
i = 0
phrase = " trac "
window = tk.Tk()
panel = tk.Label(window) # without image
panel.pack()
# use it after creating Label so it can set image in existing label
open_image()
window.mainloop()

Images do not get shown in a scroll view associated top-level window in python tkinter

I have been trying to make a top-level window view which collects and shows all the images present in a folder in columns of 10. If the images were more than the allocated size of the window I wanted it to be possible to scroll through the images. I followed the answer given to Scrollable Toplevel Window (tkinter)
to correctly add an image to a canvas and make it possible to scroll through them. But, in my case the entire popup window just comes out to be blank. Here is the code
import tkinter as tk
from tkinter import *
import glob
import os
from PIL import Image, ImageTk
def pop_up_window():
win = Toplevel()
vbar = tk.Scrollbar(win, orient = VERTICAL)
vbar.grid(row = 0, column = 1, sticky = "ns")
container = tk.Canvas(win, height=300, width=720, scrollregion=(0, 0, 300, 720))
container.grid(row = 0, column = 0, sticky = "nsew")
vbar.config(command=container.yview)
container.config(yscrollcommand=vbar.set)
path = "D:\\image_collection"
COLUMNS = 10
image_count = 0
for infile in glob.glob(os.path.join(path, '*.jpg')):
image_count += 1
r, c = divmod(image_count-1, COLUMNS)
im = Image.open(infile)
resized = im.resize((100, 100), Image.ANTIALIAS)
img_part = ImageTk.PhotoImage(Image.open(infile).resize((100, 100), Image.ANTIALIAS))
image_in_canvas = container.create_image(r, c, image = img_part)
win.rowconfigure(0, weight=1)
win.columnconfigure(0, weight=1)
root = Tk()
button = Button(root, text='Call Pop-up window', command = pop_up_window)
button.place(x = 0, y = 0)
root.mainloop()
What changes should I make?
You need to keep a reference to your images or it will be garbage collected by Python. A simple change can do it:
placeholder = []
def pop_up_window():
...
for infile in glob.glob(os.path.join(path, '*.jpg')):
image_count += 1
r, c = divmod(image_count-1, COLUMNS)
im = Image.open(infile)
img_part = ImageTk.PhotoImage(Image.open(infile).resize((100, 100), Image.ANTIALIAS))
placeholder.append(img_part)
image_in_canvas = container.create_image(r, c, image = img_part)
Also I want to point that the create_image method takes two coordinates as args. You are currently creating them as if they are grids, and it won't show up in the alignment you expected.
Your code works but it have few issues with it. I fixed all of them or maybe most of them, in case I forgot to notice it.
When creating an image in a function always create a reference to them as here you have many images so you can create a list to your container.
To keep updating the scrollregion depending upon the amount of images bind "" to the container with the callback function lambda e: scrollregion=container.bbox('all').
Here are the changes that I did to your pop_up_window function.
...
path = "D:\\image_collection"
COLUMNS = 7
container.img_list = []
column = 0
row = 0
for infile in glob.glob(os.path.join(path, '*.jpg')):
if column >= COLUMNS:
column = 0
row += 1
im = Image.open(infile).resize((100, 100), Image.ANTIALIAS)
img = ImageTk.PhotoImage(im)
container.img_list.append(img)
container.create_image(column*100+10, row*100+10, image = img, anchor='nw')
column += 1
container.bind('<Configure>',lambda e:container.configure(scrollregion=container.bbox('all')))
...

Cropping an image in tkinter

I'm using tkinter and I have a "sprite sheet" and I want to cut it into multiple images. I tried PIL:
img = Image.open("test.png").convert("RGBA")
img2 = img.crop([300,300,350,350])
image = ImageTk.PhotoImage(img2)
win = tk.Tk()
label = tk.Label(win, image = image)
label.pack()
but on my window, there is only an empty white rectangle and I don't understand why. Moreover I tried img2.show() just to make shure that img2 wasn't empty and it wasn't.
Here is your code, with a few changes. Note the call to Tk() at the top, and mainloop() at the bottom. The other modification is that it obtains the width and height of the image and then crops 25% from each of the four sides to leave the middle 50% of the image.
#!/usr/bin/python
from tkinter import *
from PIL import ImageTk,Image
root = Tk()
img = Image.open("test.png").convert("RGBA")
w, h = img.size
left = w/4
right = 3*w/4
upper = h/4
lower = 3*h/4
img2 = img.crop([ left, upper, right, lower])
image = ImageTk.PhotoImage(img2)
label = Label(root, image = image)
label.pack()
root.mainloop()

Categories