tkinter fails to display my .png button image - python

The following program attempts to display a row of four buttons. The inner two, "ArrowLeft" and "ArrowRight", display correctly. The outer two do not show the image. I have tried two different strategies in this code for importing the image ("LeftLeft" and "RightRight") and by no means will it work. Since I can't attach the image files in their original form, here is a zip file that includes the code below and six very small .png files. If anyone could take a look and see what is going wrong, I would sure appreciate it.
The alternative tryCanvas code successfully displays the LeftLeft image!
Zip file: https://easyupload.io/ubnfn1
import tkinter as tk
from tkinter import ttk, PhotoImage
from PIL import Image, ImageTk
root = tk.Tk()
def layout(frame):
global leftIcon, upIcon, rightIcon, downIcon
leftLeft1 = Image.open("LeftLeft.png")
leftLeft1.show()
leftIcon = ImageTk.PhotoImage(file = "ArrowLeft.png")
leftLeftIcon = ImageTk.PhotoImage(leftLeft1)
rightIcon = ImageTk.PhotoImage(file="ArrowRight.png")
rightRightIcon = ImageTk.PhotoImage(file = "RightRight.png")
ttk.Button(frame, image=leftLeftIcon).pack(side=tk.LEFT)
ttk.Button(frame, image=leftIcon).pack(side=tk.LEFT)
ttk.Button(frame, image=rightIcon).pack(side=tk.LEFT)
ttk.Button(frame, image=rightRightIcon).pack(side=tk.LEFT)
def tryCanvas():
can1 = tk.Canvas(root)
image = PhotoImage(file='LeftLeft.png')
item = can1.create_image(100, 100, image = image)
can1.image = image
can1.pack()
# tryCanvas()
layout(root)
root.mainloop()

Related

How can I Iterate through a list of Images and Display them in Tkinter

So, my goal is to create a sort of slideshow within Tkinter. I have a list of images like Images = ["1.png", "2.png", ...], I want to be able to iterate through the list and display each image in a Tkinter window, the concept is simple and as follows:
Display Image 1
30 Second Delay
Display Image 2
30 Second Delay
Display Image 3
I have managed to iterate the images using a button press, however, I do not want to have to click a button as it is meant to imitate a slideshow, I also attempted looping a function but the time.sleep() function does not delay in the correct way because of how Tkinter behaves.
I managed to achieve the above using mostly source code from here, and I would appreciate a little hand achieving the above.
My Code:
from tkinter import *
from PIL import ImageTk, Image
Window = Tk()
Window.geometry("1920x1080")
Window.resizable(0, 0)
Label1 = Label(Window)
Label1.pack()
Images = iter(["1.png", "2.png", "3.png", "4.png", "5.png",
"6.png", "7.png", "8.png", "9.png", "10.png"])
def Next_Image(Val):
try:
Image1 = next(Images)
except StopIteration:
return
Image1 = ImageTk.PhotoImage(Image.open(Image1))
Label1.Image = Image1
Label1["image"] = Image1
Button1 = Button(text = "Next image", command =
lambda:Next_Image(1))
Button1.place(x = 50, y = 50)
Next_Image(1)
Window.mainloop()
I also attempted to use .after(), however, it did not display each image, it skipped from the first image to the last straight away with the compounded delay.
for x in range(1, 11):
Window.after(1000, lambda : Next_Image(1))
You need to create a function that gets the image off of the list and displays it, and then uses after to call itself again in a second. Your main program needs to call this exactly once, and then it will run until it runs out of things to do.
Here's a working example that uses a text string for simplicity, but it should be obvious how to modify it to use images.
import tkinter as tk
images = iter(["1.png", "2.png", "3.png", "4.png", "5.png",
"6.png", "7.png", "8.png", "9.png", "10.png"])
def next_image():
try:
image = next(images)
label.configure(text=image)
root.after(1000, next_image)
except StopIteration:
return
root = tk.Tk()
label = tk.Label(root, width = 40, height=4)
label.pack()
next_image()
root.mainloop()
You can use .after() to switch image periodically:
from itertools import cycle
...
# use cycle() instead of iter()
Images = cycle([f"{i}.png" for i in range(1, 5)])
...
def next_image():
# use next() to get next image in the cycle list
Label1.image = ImageTk.PhotoImage(file=next(Images))
Label1['image'] = Label1.image
# switch image again after 1 second
Label1.after(1000, next_image)
next_image() # start the loop
Window.mainloop()
This worked, Thank you #acw1668 and #Bryan Oakley.
from tkinter import *
from PIL import ImageTk, Image
Window = Tk()
Window.geometry("1920x1080")
Window.resizable(0, 0)
Label1 = Label(Window)
Label1.pack()
Images = iter(["1.png", "2.png", "3.png", "4.png", "5.png", "6.png",
"7.png", "8.png", "9.png", "10.png"])
def Next_Image(Val):
try:
Image1 = next(Images)
except StopIteration:
return
Image1 = ImageTk.PhotoImage(Image.open("BuyingConfig\\" + Image1))
Label1.Image = Image1
Label1["image"] = Image1
Window.after(3000, lambda:Next_Image(1))
Window.after(0, lambda:Next_Image(1))
Window.mainloop()

Is it possible to provide animation on image ? - Tkinter

I'm developing a GUI in Tkinter and want to apply animation in the below GIF on the image when it appears.
Here is my code,
from tkinter import *
from PIL import Image, ImageTk
root = Tk()
frame = Frame(root)
frame.pack()
canvas = Canvas(frame, width=300, height=300, bd=0, highlightthickness=0, relief='ridge')
canvas.pack()
background = PhotoImage(file="background.png")
canvas.create_image(300,300,image=background)
my_pic = PhotoImage(file="start000-befored.png")
frame.after(1000, lambda: (canvas.create_image(50,50,image=my_pic, anchor=NW))) #and on this image, I want to give the effect.
root.mainloop()
Instead of clicking on the play button as shown in GIF, the image should automatically appears after 1 second like this animation and stays on screen. (No closing option).
I'm not 100% sure I understood the problem, but I'll describe how to animate an image.
Tkinter does not contain functions for animating images so you'll have to write them yourself. You will have to extract all subimages, subimage duration and then build a sequencer to swap subimages on your display.
Pillow can extract image sequences. WEBP images seems to only support one frame duration whereas GIF images may have different frame duration for each subimage. I will use only the first duration for GIF images even if there is many. Pillow does not support getting frame duration from WEBP images as far as I have seen but you gan read it from the file, see WebP Container Specification.
Example implementation:
import tkinter as tk
from PIL import Image, ImageTk, ImageSequence
import itertools
root = tk.Tk()
display = tk.Label(root)
display.pack(padx=10, pady=10)
filename = 'images/animated-nyan-cat.webp'
pil_image = Image.open(filename)
no_of_frames = pil_image.n_frames
# Get frame duration, assuming all frame durations are the same
duration = pil_image.info.get('duration', None) # None for WEBP
if duration is None:
with open(filename, 'rb') as binfile:
data = binfile.read()
pos = data.find(b'ANMF') # Extract duration for WEBP sequences
duration = int.from_bytes(data[pos+12:pos+15], byteorder='big')
# Create an infinite cycle of PIL ImageTk images for display on label
frame_list = []
for frame in ImageSequence.Iterator(pil_image):
cp = frame.copy()
frame_list.append(cp)
tkframe_list = [ImageTk.PhotoImage(image=fr) for fr in frame_list]
tkframe_sequence = itertools.cycle(tkframe_list)
tkframe_iterator = iter(tkframe_list)
def show_animation():
global after_id
after_id = root.after(duration, show_animation)
img = next(tkframe_sequence)
display.config(image=img)
def stop_animation(*event):
root.after_cancel(after_id)
def run_animation_once():
global after_id
after_id = root.after(duration, run_animation_once)
try:
img = next(tkframe_iterator)
except StopIteration:
stop_animation()
else:
display.config(image=img)
root.bind('<space>', stop_animation)
# Now you can run show_animation() or run_animation_once() at your pleasure
root.after(1000, run_animation_once)
root.mainloop()
There are libraries, like imgpy, which supports GIF animation but I have no experience in usig any such library.
Addition
The duration variable sets the animation rate. To slow the rate down just increase the duration.
The simplest way to put the animation on a canvas it simply to put the label on a canvas, see example below:
# Replace this code
root = tk.Tk()
display = tk.Label(root)
display.pack(padx=10, pady=10)
# with this code
root = tk.Tk()
canvas = tk.Canvas(root, width=500, height=500)
canvas.pack(padx=10, pady=10)
display = tk.Label(canvas)
window = canvas.create_window(250, 250, anchor='center', window=display)
Then you don't have to change anything else in the program.

Tkinter image not showing

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.

how to loop code that opening the last 2 pictures in a folder

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

Tkinter, overlay foreground image on top of a background image with transparency

I have 2 images, in png format.
The second image, is a shape with transparent background.
First image:
Second image:
I cannot make the second image on top of the first at given coordinates (x,y) with the first image visible through the transparent zone of the second image.
Result desired:
import Tkinter
import Image, ImageTk
# open an image
head = Image.open('background2.png')
hand = Image.open('foreground2.png')
root = Tkinter.Tk() # A root window for displaying objects
head.paste(hand,(20,20))
# Convert the Image object into a TkPhoto object
tkimage = ImageTk.PhotoImage(head)
root.mainloop() # Start the GUI
An empty tk window is displayed.
Thanks Bryan.
Got it, in addition to label, the issue of transparency resolved from another question here (same foregound image used as a mask)
I guess, this is what cost me -2 points :-|
Now it works as expected.
from Tkinter import *
import Tkinter
from PIL import Image, ImageTk
root = Tkinter.Tk() # A root window for displaying objects
# open image
imageHead = Image.open('head.png')
imageHand = Image.open('hand.png')
imageHead.paste(imageHand, (20, 40), imageHand)
# Convert the Image object into a TkPhoto object
tkimage = ImageTk.PhotoImage(imageHead)
panel1 = Label(root, image=tkimage)
panel1.grid(row=0, column=2, sticky=E)
root.mainloop() # Start the GUI
Hey guys I know that I am 6 years late but I can help you with this
from PIL import Image
import numpy as np
# Create Image
img = Image.open("forground.png")
background = Image.open("background.png")
background.paste(img, (0, 0), img)
background.save('NewImg.png',"PNG")
NewImg = Image.open('NewImg.png')
# Use Image
tkimage = ImageTk.PhotoImage(NewImg)
panel1 = Label(root, image=tkimage)
panel1.grid(row=0, column=2, sticky=E)
root.mainloop() # Start the GUI
So all you have to do is use NewImg when dealing with the file.
This code creates an image out of the two images and then utilises that image in the program.

Categories