I've written a snippet of code to display images in a Tkinter app. However, the image does not display unless I read all of the images before assigning it to the label. Here is my code:
from tkinter import *
from PIL import ImageTk, Image
root = Tk()
root.title("Image Viewer app")
def readImage(image_name):
my_img = Image.open(image_name)
my_img = my_img.resize((640, 360), Image.ANTIALIAS)
my_img = ImageTk.PhotoImage(my_img)
return my_img
frame_lst = list(range(0,120))
img_lst = []
path = "usr/data/images"
start = time.time()
for file in os.listdir(path):
if file.endswith('.jpg'):
filepath = os.path.join(path, file)
img_lst.append(filepath) #print time taken
end = time.time()
print(f"Time taken to read images is {end-start} seconds")
image_displayed = Label(image=readImage(img_lst[0]))
image_displayed.grid(row=0,column=0,columnspan=3)
root.mainloop()
This does not work but if I replace img_lst.append(filepath) with img_lst.append(imageRead(filepath)) and image_displayed = Label(image=readImage(img_lst[0])) with image_displayed = Label(image=img_lst[0]) then it works.
That is, the app does not work when I try to read the image directly without preloading it in memory. Where am I going wrong?
Since there is no variable to store the reference of the image returned by readImage() in the following line:
image_displayed = Label(image=readImage(img_lst[0]))
the image will be garbage collected. Use a variable to store the reference, like below:
image = readImage(img_lst[0]) # save the reference of the image
image_displayed = Label(image=image)
Related
I don't know why this code is not displaying any image when I run it.
from tkinter import *
import os
root = Tk()
images = os.listdir()
i = 0
for images in images:
if images.endswith(".png"):
photo = PhotoImage(file=images)
label = Label(image=photo)
label.pack()
print("reached here")
root.mainloop()
So basically you need to have PIL installed
pip install PIL
then
from tkinter import *
import os
from PIL import Image, ImageTk
root = Tk()
images = os.listdir()
imglist = [x for x in images if x.lower().endswith(".jpg")]
for index, image in enumerate(imglist): #looping through the imagelist
photo_file = Image.open(image)
photo_file = photo_file.resize((150, 150),Image.ANTIALIAS) #resizing the image
photo = ImageTk.PhotoImage(photo_file) #creating an image instance
label = Label(image=photo)
label.image = photo
label.grid(row=0, column=index) #giving different column value over each iteration
print("reached here with "+image)
root.mainloop()
If you want to use pack() manager, then change
for image in imglist:
....... #same code as before but grid() to
label.pack()
Do let me know if any errors or doubts
Cheers
I played a little and got some results. You can refine it:
from tkinter import *
import os
root = Tk()
images = os.listdir()
imglist = [x for x in images if x.lower().endswith(".png")]
i = 0
photolist = []
labellist= []
for image in imglist:
photo = PhotoImage(file=image)
photolist.append(photo)
label = Label(image=photo)
labellist.append(label)
label.pack()
print("reached here with "+image)
root.mainloop()
I am trying to create a Tkinter to make a window that shows images using a label, then update the image using an update function, but the image that I am trying to show doesn't show up in the Tkinter window, instead, a black screen appears
I have two working code
one that shows an image on the Tkinter window
one what loops a GIF using an update function
I tried to combine them
the code I am working on that doesn't work
#import GUI
from tkinter import *
#change dir
import os
os.chdir("C:/Users/user/Desktop/test image folder/")
#add delay
import time
#import image
from PIL import Image, ImageTk
#set up the window
window = Tk()
#window.title("modify images")
#list of filename
filelist = []
#loop over all files in the working directory
for filename in os.listdir("."):
if not (filename.endswith('.png') or filename.endswith('.jpg')):
continue #skip non-image files and the logo file itself
filelist = filelist + [filename]
#list of filename
print(filelist)
#show first pic
imagefile = filelist[0]
photo = ImageTk.PhotoImage(Image.open(imagefile))
label1 = Label(window, image = photo)
label1.pack()
#update image
def update(ind):
imagefile = filelist[ind]
im = ImageTk.PhotoImage(Image.open(imagefile))
if ind < len(filelist):
ind += 1
else:
ind = 0
label1.configure(image=im)
window.after(2000, update, ind)
window.after(2000, update, 0)
#run the main loop
window.mainloop()
the other code I am trying to combine
1:the one that shows image
import tkinter as tk
from tkinter import *
from PIL import Image, ImageTk # Place this at the end (to avoid any conflicts/errors)
window = tk.Tk()
imagefile = "image.jpg"
img = ImageTk.PhotoImage(Image.open(imagefile))
lbl = tk.Label(window, image = img).pack()
window.mainloop()
print('hi')
2:updates gif
from tkinter import *
#change dir
import os
os.chdir("C:/Users/user/Desktop/Learn Python")
#add delay
import time
##### main:
window = Tk()
##### My Photo
photo1 = [PhotoImage(file="anime.gif", format="gif -index %i" %(i)) for i in range(85)]
#update image
def update(ind):
frame = photo1[ind]
if ind < 84:
ind += 1
else:
ind = 0
label.configure(image=frame)
window.after(80, update, ind)
label = Label(window, bg="black")
label.pack()
window.after(0, update, 0)
#####run the main loop
window.mainloop()
I expect it to show all images in the file one by one
it instead shows only the first image, then the window goes blank
You have problem because there is bug in PhotoImage. If you create it in function and assign to local variable then Garbage Collector removes image from memory and you see empty image. You have to create PhotoImages outside function or you have to assign it to some global variable.
Popular solution is to assign it to label which will display it.
label.im = im
Function:
def update(ind):
imagefile = filelist[ind]
im = ImageTk.PhotoImage(Image.open(imagefile))
if ind < len(filelist):
ind += 1
else:
ind = 0
label1.configure(image=im)
label1.im = im # <-- solution
window.after(2000, update, ind)
Doc: PhotoImage
I made this piece of code:
from tkinter import *
from PIL import ImageTk, Image
import sys
import getnew
class startUp:
def __init__(self, master):
master.title("Tag checker")
master.resizable(False, False)
img1 = ImageTk.PhotoImage(Image.open("images/ss.png"))
cercaImg = Label(master, image = img1)
cercaImg.bind("<Button-1>",clicka)
cercaImg.grid(row=0,column=0)
img2 = ImageTk.PhotoImage(Image.open("images/opz.png"))
opzioniImg = Label(master, image = img2)
opzioniImg.grid(row=0,column=1)
img3 = ImageTk.PhotoImage(Image.open("images/exit.png"))
esciImg = Label(master, image = img3)
esciImg.bind("<Button-1>",(master.destroy and quit))
esciImg.grid(row=0,column=2)
def clicka(event):
print('ciaooo')
x = getnew.getSchools()
print(x[0][0],x[0][1],x[0][2])
root = Tk()
st = startUp(root)
root.mainloop()
The point is to have 3 images that, when clicked, execute a function, but he images don't show up. They do appear as size and 'clickable' zone and they execute the function, but the image as it is doesn't show up.
What am I doing wrong here ?
From tkinter docs on PhotoImage:
You must keep a reference to the image object in your Python program, either by storing it in a global variable, or by attaching it to another object.
The reason to do so is :
When a PhotoImage object is garbage-collected by Python (e.g. when you return from a function which stored an image in a local variable), the image is cleared even if it’s being displayed by a Tkinter widget.
To avoid this, the program must keep an extra reference to the image object. A simple way to do this is to assign the image to a widget attribute.
Hence for your program:
img1 = ImageTk.PhotoImage(Image.open("images/ss.png"))
cercaImg = Label(master, image = img1)
cercaImg.image = img1 # Keep a reference
Similarly for the other images as well.
i built a code that checks what is the most recent picture in a folder (there are going to be only pictures in the file), and it works about two pictures. The code is going to be some part of a larger code of video streaming and thats why i need to switch between the most recent pictures fastly.
So, That`s what i had tried yet:
this code checks twice the most recent picture and openes it via Tkinter.
What i need now is to make it a code that runs in a infinity loop and switches picture after picture.
This is the code:
import tkinter as tk
from PIL import Image, ImageTk
from Tkinter import *
import Image, ImageTk
import glob,os
def RecentFilePath():
folder = "C:\\NIR"
return(str(max((x for x in glob.glob(os.path.join(folder,"*")) if os.path.isfile(x)),key=os.path.getmtime)))
root = tk.Tk()
img = ImageTk.PhotoImage(Image.open(RecentFilePath))
panel = tk.Label(root, image = img)
panel.pack(side = "bottom", fill = "both", expand = "yes")
t = True
def callback():
global t
t = not t
if(t):
img2 = ImageTk.PhotoImage(Image.open(RecentFilePath))
else:
img2 = ImageTk.PhotoImage(Image.open(RecentFilePath))
panel.configure(image = img2)
panel.image = img2
root.after(1000, callback)
root.after(1000, callback)
root.mainloop()
Thank you very much!!
You are not calling RecentFilePath in your PhotoImage creations (img and two img2 assignings).
img = ImageTk.PhotoImage(Image.open(RecentFilePath()))
^^ you need to call it, to get return value
I try to show a image in a label when I push a button, but the image are too large and I have tried to resize the image. I have created this function:
def image_resize(imageFile):
width = 500
height = 300
image = Image.open(imageFile)
im2 = image.resize((width, height), Image.ANTIALIAS)
return im2
To show the image I have created this function:
def show_image():
label_originalimage ['image'] = image_tk
And the button with the command=show_image:
filename = 'bild_1.jpg'
image_resize = image_resize(filename)
image_tk = PhotoImage(image_resize)
button_open = Button(frame_open, text='Open Image', command=show_image)
I get only this:
TypeError : __str__ returned non-string (type instance)
The PhotoImage class from tkinter takes a filename as an argument, and as it cannot convert the image into a string, it complains. Instead, use the PhotoImage class from the PIL.ImageTk module. This works for me:
from tkinter import *
from PIL import ImageTk, Image
def image_resize(imageFile):
width = 500
height = 300
image = Image.open(imageFile)
im2 = image.resize((width,height), Image.ANTIALIAS)
return im2
def show_image():
label_originalimage ['image'] = image_tk
root = Tk()
filename = './Pictures/Space/AP923487321702.jpg'
image_resize = image_resize(filename)
image_tk = ImageTk.PhotoImage(image_resize)
label_originalimage = Label(root)
label_originalimage.pack()
button_open = Button(root, text='Open Image', command=show_image)
button_open.pack()
root.mainloop()
Notice the change from image_tk = PhotoImage(image_resize) to image_tk = ImageTk.PhotoImage(image_resize).
I had the same problem when I try to construct a canvas image item for
tkinter from a tkinter PhotoImage. The latter was constructed from some
image data in memory (in my case an opencv image). The same exception
occurs if I simply try to convert the PhotoImage to a string.
I guess there is a bug in the conversion method __str__ of the PhotoImage,
making it simply returns the image source. If constructed from a file name
(see below) this works fine. If constructed from some image data, this is not
of type string and yields an exception.
Unfortunately, using the compatible PhotoImage from PIL's
ImageTk module like matsjoyce suggested didn't help me either because I experienced an even worse problem, probably a platform or library version dependent bug (I used OS X 10.11.6, python 3.5, tkinter 8.6, PIL 1.1.7): Now the python script crashed at the construction of the canvas image item with a "Bus Error".
The only workaround I am aware of was to store the image data into a temporary file and use a tkinter PhotoImage constructed from that file name. (Trying the same with the PIL PhotoImage still crashes.)
#!/usr/bin/env python3
import tkinter
import tempfile
import cv2
def opencv2photoimg(opencv_img):
"""Convert OpenCV (numpy) image to tkinter photo image."""
# ugly workaround: store as file & load file, because direct
# construction leads to a crash on my platform
tmpfile = tempfile.NamedTemporaryFile(suffix='.png', delete=True)
# ^^^ I am using PNGs only, you might want to use another suffix
cv2.imwrite(tmpfile.name, opencv_img)
return tkinter.PhotoImage(file=tmpfile.name)
# load image
img = cv2.imread('test.png')
# do something w/ the image ...
# setup tk window w/ canvas containing an image
root = tkinter.Tk()
canvas = tkinter.Canvas(root, width=img.shape[1], height=img.shape[0])
canvas.pack()
# keep reference to PhotoImage to avoid it being garbage collected
# (well known tkinter bug for canvas image items)
photo_img = opencv2photoimg(img)
# create a canvas item
img_item = canvas.create_image(0, 0, anchor=tkinter.NW, image=photo_img)
# display the window
tkinter.mainloop()
I do not think it's elegant, but it works.
Yes it works, but yeeeucchh - what I way to have to do it.
Surely there is a better way.
Here is my test code I got to starting from here....
import tkinter
from PIL import Image
import numpy
import time
import io
#python2 version (original) -> 120fps
#full physical file io and new image each cycle -> 130fps
#reuse PIL Image instead of create new each time -> 160fps
class mainWindow():
times=1
timestart=time.clock()
data=numpy.array(numpy.random.random((400,500))*100,dtype=int)
theimage = Image.frombytes('L', (data.shape[1],data.shape[0]),data.astype('b').tostring())
def __init__(self):
self.root = tkinter.Tk()
self.frame = tkinter.Frame(self.root, width=500, height=400)
self.frame.pack()
self.canvas = tkinter.Canvas(self.frame, width=500,height=400)
self.canvas.place(x=-2,y=-2)
self.root.after(0,self.start) # INCREASE THE 0 TO SLOW IT DOWN
self.root.mainloop()
def start(self):
global data
global theimage
self.theimage.frombytes(self.data.astype('b').tobytes())
self.theimage.save('work.pgm')
self.photo = tkinter.PhotoImage(file='work.pgm')
self.canvas.create_image(0,0,image=self.photo,anchor=tkinter.NW)
self.root.update()
self.times+=1
if self.times%33==0:
print("%.02f FPS"%(self.times/(time.clock()-self.timestart)))
self.root.after(10,self.start)
self.data=numpy.roll(self.data,-1,1)
if __name__ == '__main__':
x=mainWindow()
Here it is: I found that the input data to photoimage can be a byte array that looks like a ppm file, although it only appears to work on a subset of legal ppm (e.g. 16 bit values don't work)
So for future reference.......
import tkinter
import numpy
import time
#python2 version (original) -> 120fps
#full physical file io and new image each cycle -> 130fps
#reuse PIL Image instead of create new each time -> 160fps
#and... direct image into tkinter using ppm byte array -> 240 fps
class mainWindow():
times=1
timestart=time.clock()
data=numpy.array(numpy.random.random((400,500))*900,dtype=numpy.uint16)
def __init__(self):
self.root = tkinter.Tk()
self.frame = tkinter.Frame(self.root, width=500, height=400)
self.frame.pack()
self.canvas = tkinter.Canvas(self.frame, width=500,height=400)
self.canvas.place(x=-2,y=-2)
xdata = b'P5 500 400 255 ' + self.data.tobytes()
self.photo = tkinter.PhotoImage(width=500, height=400, data=xdata, format='PPM')
self.imid = self.canvas.create_image(0,0,image=self.photo,anchor=tkinter.NW)
self.root.after(1,self.start) # INCREASE THE 0 TO SLOW IT DOWN
self.root.mainloop()
def start(self):
global data
xdata = b'P5 500 400 255 ' + numpy.clip(self.data,0,255).tobytes()
self.photo = tkinter.PhotoImage(width=500, height=400, data=xdata, format='PPM')
if True:
self.canvas.itemconfig(self.imid, image = self.photo)
else:
self.canvas.delete(self.imid)
self.imid = self.canvas.create_image(0,0,image=self.photo,anchor=tkinter.NW)
self.times+=1
if self.times%33==0:
print("%.02f FPS"%(self.times/(time.clock()-self.timestart)))
self.root.update()
self.root.after(0,self.start)
self.data=numpy.roll(self.data,-1,1)
if __name__ == '__main__':
x=mainWindow()