How to use python Tkinter to iterate images? - python

How to use python Tkinter to iterate images?
import tkinter as tk
from PIL import ImageTk, Image
win = tk.Tk()
win.geometry('800x500') # set window size
win.resizable(0, 0) # fix window
images = ['01.jpg', '02.jpg', '03.jpg']
def next_img():
# show next image
for img in images:
img = Image.open(img)
img = ImageTk.PhotoImage(img)
panel = tk.Label(win, image=img)
panel.pack()
btn = tk.Button(text='Next image', command=next_img)
btn.pack()
win.mainloop()
But my panel doesn't show any images. I hope the panel waits me and I click the button to show next images. How to solve it.

The reason why your code doesn't display any images is a bit complicated. There are two factors working together:
Because you're creating your labels (the ones that you use to display the images) in a loop, you end up creating 3 labels and 3 buttons - one for each image. All of these are arranged below each other, so depending on the size of your images, some of them might be below the window's bottom edge. If you use small images, you'll see three "Next image" buttons in your window.
Tkinter images are garbage collected if you don't keep a reference to them in your python code. Since your loop overwrites the value of the img variable in each iteration, all images except the last one are garbage collected and aren't displayed.
To fix your code, first start by removing that loop. You don't need 3 labels and 3 buttons, and you don't need to load all 3 images immediately when the program starts either.
The code that loads and displays the image should be moved into the next_img function. You can update the label's image with the command panel['image'] = img.
In order to cycle through the list of images, it's easiest to use an iterator. You can turn your list of images into an iterator by calling images = iter(images). Then you can use the next function to get the next image from the iterator when you need it.
import tkinter as tk
from PIL import ImageTk, Image
win = tk.Tk()
win.geometry('800x500') # set window size
win.resizable(0, 0) # fix window
panel = tk.Label(win)
panel.pack()
images = ['01.jpg', '02.jpg', '03.jpg']
images = iter(images) # make an iterator
def next_img():
try:
img = next(images) # get the next image from the iterator
except StopIteration:
return # if there are no more images, do nothing
# load the image and display it
img = Image.open(img)
img = ImageTk.PhotoImage(img)
panel.img = img # keep a reference so it's not garbage collected
panel['image'] = img
btn = tk.Button(text='Next image', command=next_img)
btn.pack()
# show the first image
next_img()
win.mainloop()
This code will loop through the images once, and when the last image is reached, pressing the "Next image" button will have no effect. If you want to wrap around to the first image, you can itertools.cycle to create an infinitely looping iterator instead:
images = itertools.cycle(images)

Unfortunately, Tkinter is a little confusing and when placing an image in a label or button you will want to add on the line beneath
panel.photo = img
I'm not exactly sure why this works but it seems there are 2 values that take the image to display it.

Related

Image does not show on button Tkinter

I'm trying to simply add an image to a tkinter button. I tried everything:
import tkinter as tk
root = tk.Tk()
root.geometry('300x300+300+150')
photo = tk.PhotoImage('home.gif')
btn = tk.Button(root, image = photo, width = 100, height = 100)
btn.image = photo # even with this does not work
btn.pack()
root.mainloop()
I also tried with PIL setting the photo variable equal to ImageTk.PhotoImage(Image.open('home.gif')), I tried easly the open function, the absolute path of the photo (and yes, the photo is inside the same directory of my script), but anything works. The window just pop up with a big button, without image inside.
UPDATE:
I tried with other images, and I noticed that some images are shown while others no. This is because the images with transparent background cause a bug or a problem to tkinter... so, I do not know if there's a way to solve this. On google I find out that some people use canvas but I actually need the image to be inside the button so I do not know how to do.
Please change your code as below
photo = tk.PhotoImage(file='home.gif')
because i changed the above code and it worked....

Is there any way with Tkinter to fade out widgets?

Is there any way to set an image as partially transparent, maybe using PIL or something? I know tkinter has a feature like this for Tk() and Toplevel(), but I wanted to know if there is a way to apply it to an a widget or maybe a PIL image that I can then put in a widget, either would do.
I want to make a game that fades to black when you lose, but I don't want the whole window to fade away.
You have two options: fade out the whole window or fade out an image using PIL, individual widgets cannot be faded out:
Fading out a window
Tk and TopLevel windows can be faded out entirely
import time
import threading
import tkinter
root = tkinter.Tk()
def fade():
global root
# Walk backwards through opacities (1 is opaque, 0 is transparent)
i = 1.0
while i >= 0:
root.attributes("-alpha", i)
i -= 0.1
# Sleep some time to make the transition not immediate
time.sleep(0.05)
# Put image fading in a thread so it doesn't block our GUI
fade_thread = threading.Thread(target=fade)
tkinter.Button(root, text="Fade out", command=fade_thread.start).pack()
root.mainloop()
Fading out an image
A bit more involved and a bit more computationally intensive (larger images exacerbate this problem). It could be worth precomputing these or using less steps (-10 vs -5 etc) to save some compute power.
import time
import threading
import tkinter
from PIL import Image, ImageTk
root = tkinter.Tk()
# Tested with .jpg and .png
IMAGE_PATH = "/path/to/image.jpg"
# Create a pillow image and a tkinter image. convert to RGBA to add alpha channel to image
image = Image.open(IMAGE_PATH).convert("RGBA")
image_tk = ImageTk.PhotoImage(image)
# We'll fade to whatever the background is here (black, white, orange, etc)
label = tkinter.Label(root, image=image_tk, bg="black")
label.pack()
def fade_image():
global image, image_tk, label
# Walk backwards through opacities (255 is opaque, 0 is transparent)
for i in range(255, 0, -5):
image.putalpha(i) # Set new alpha
image_tk = ImageTk.PhotoImage(image) # Cretae new image_tk
label.configure(image=image_tk)
# Sleep some time to make the transition not immediate
time.sleep(0.001)
# Put image fading in a thread so it doesn't block our GUI
fade_thread = threading.Thread(target=fade_image)
tkinter.Button(root, text="Fade To Black", command=fade_thread.start).pack()
root.mainloop()
Note this is bad practice in regards to using threads and time.sleep() inside a GUI program. Using widget.after(delay_in_ms, callback) is preferable. For more on how to do that check out tkinter: how to use after method

How to display an image in tkinter using grid

I am trying to display an image to my GUI, through PhotoImage, however it is telling me that PhotoImage has no "grid" member.
I am using .grid() for all of my other widgets, like labels and buttons, so I can't use .pack(), if that would make a difference anyway. So, how could I display an image to my GUI with grids?
I don't know if code is necessary for this question, but
# it works by getting a random image from a list of files
Label(root, text=f"Enter a number between one and {len(files)}")
self.entry = Entry(root)
self.entry.grid()
Button(root, text="Submit", command=self.getEntry).grid()
Then, getEntry() is:
def getEntry(self):
PhotoImage(name="",
file=f{self.path}\\{self.showImage(self.entry.get())}).grid(row=1)
"""
showImage() just returns the correct file
(e.g: files[index] where index is the input)
"""
NOTE:
grid() is returning NoneValue, which means that your variable 'file' will be NoneValue.
To display an image in tkinter you'll need to do something like:
from PIL import Image, ImageTk
image = Image.open(image_path)
photo = ImageTk.PhotoImage(image)
label = Label(root, image = photo)
label.image = photo
label.grid(row=1)
# label.pack()
NOTE:
The PhotoImage class can read GIF and PGM/PPM images from files:
photo = PhotoImage(file="image.gif")
photo = PhotoImage(file="lenna.pgm")
If you need to work with other file formats, the Python Imaging
Library (PIL) contains classes that lets you load images in over 30
formats, and convert them to Tkinter-compatible image objects:
from PIL import Image, ImageTk
image = Image.open("lenna.jpg")
photo = ImageTk.PhotoImage(image)
You can use a PhotoImage instance everywhere Tkinter accepts an image
object.
An example:
label = Label(image=photo)
label.image = photo # keep a reference!
label.pack()
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.
You have to use a widget that can support images. Typically a label, but button can also configure a button to show an image, as well as add images to text and canvas widgets.

Reload and zoom image

I am playing a little with a 100x100 pixel image, creating and updating it all the time. My code works fine, but I want the image to be shown and updated as the program runs, zoomed in so much that I can see the separate pixels.
Currently, I just open the resulting image in Eye of Gnome, which reloads automatically. Problem here, is that at every reload, the zoom level jumps back to 100% (and it should be at 600% or so).
while self.running:
img.save("image.tmp.png")
time.sleep(1)
os.rename("image.tmp.png", "image.png")
Trying to use PIL's show method works, but creates a new window per view.
while self.running:
img.show()
How can I reload the image all the time, while retaining the zoom level?
You can try to launch tkinter (install package python3-tk in Linux, don't know about other OSes) as separate process:
from PIL import ImageTk, Image
from scipy.ndimage import rotate
from scipy.misc import imresize
import numpy as np
import time
# do not import *, as there is another Image
from tkinter import NW, Tk, Canvas, Label
from multiprocessing import Process, Queue
# initial image
image = Image.open("img.png")
# convert to numpy
image = np.array(image)
# process which will show updated images
def display_process(q):
# initialize window and label which will show images
master = Tk()
label = Label(master)
label.pack()
# take numpy image from Queue, draw in on canvas
def change_img():
# get one image from queue.
# if there is no one, None will be received
image = q.get()
# if not image received yet, skip
if image is not None:
print(image.shape)
# image array should be uint8 type with 3 channels (x, x, 3)
photo = ImageTk.PhotoImage(image = Image.fromarray(image))
label.configure(image=photo)
label.image = photo
# needed to schedule next update:
master.update_idletasks()
# check for new image after 50ms
master.after(50, change_img)
change_img()
master.mainloop() # locks process
# Queue, through which exchange will be made
q = Queue()
q.put(image)
p = Process(target=display_process, args=(q,))
p.daemon = True # exit process on program exit
p.start() # start process
# main program (your loop code here) ----------
# Example:
for i in range(10):
# take numpy image, rotate it
image = rotate(image, 90)
# you can use fixed zoom factor for your images
# interp='nearest' uses nearest neghbor interpolation without smoothing
# (so you can see pixels)
image2 = imresize(image, 5.0, interp='nearest')
# send it to process
q.put(image2)
time.sleep(0.5)

Displaying a sequence of PIL images with Tkinter

I am generating PIL Image objects in a loop. I'd like to display these as they are generated with Tkinter.
How would I go about doing this?
You can create a TkInter window containing a Label, and each time you want to change the image, create a new ImageTk.PhotoImage from the old image and set the label's image property to the newly created PhotoImage. Example:
import Tkinter
import Image, ImageTk
root = Tkinter.Tk()
label = Tkinter.Label(root)
label.pack()
def change_image(image):
photoimage = ImageTk.PhotoImage(image)
label.config(image=photoimage)
root.mainloop()
Whenever you need to change the image, call change_image().

Categories

Resources