update tkinter window with gif - python

import tkinter as tk
import time
from tkinter import *
from tkinter import messagebox
from time import sleep
from PIL import Image, ImageTk
from itertools import count
from time import sleep
class ImageLabel(tk.Label):
def load(self, im):
if isinstance(im, str):
im = Image.open(im)
self.loc = 0
self.frames = []
try:
for i in count(1):
self.frames.append(ImageTk.PhotoImage(im.copy()))
im.seek(i)
except EOFError:
pass
try:
self.delay = im.info['10']
except:
self.delay = 100
if len(self.frames) == 1:
self.config(image=self.frames[0])
else:
for i in range(1):
self.next_frame()
def unload(self):
self.config(image=None)
self.frames = None
def next_frame(self):
if self.frames:
self.loc += 1
self.loc %= len(self.frames)
self.config(image=self.frames[self.loc])
self.after(self.delay, self.next_frame)
root = tk.Tk()
root.geometry('400x200')
root.title("מערכת הסבת וואלה מייל")
lbl = ImageLabel(root)
lbl.pack()
lbl.load('Logo.gif')
I ran this code and I don't know how to update the tkinter win after one shows the gif.
Can someone help me and tell me how to do this? I tried many things and I don't know what to do.

When I run this program in IDLE it seems to work fine. The thing that IDLE does that is not in the program is running the mainloop. Try adding as the last line in the program:
root.mainloop()

Related

Display an animated gif in parallel to a Tkinter process, how?

I searched all around the web and the StackOverflow website, but didn't find any suitable answer to my problem.
I have a Python class used to create gifs objects:
import tkinter as tk
from PIL import ImageTk, Image
from itertools import count
class ImageLabel(tk.Label):
def load(self, im):
if isinstance(im, str):
im = Image.open(im)
self.loc = 0
self.frames = []
try:
for i in count(1):
self.frames.append(ImageTk.PhotoImage(im.copy()))
im.seek(i)
except EOFError:
pass
try:
self.delay = im.info['duration']
except:
self.delay = 100
if len(self.frames) == 1:
self.config(image=self.frames[0])
else:
self.next_frame()
def unload(self):
self.config(image="")
self.frames = None
def next_frame(self):
if self.frames:
self.loc += 1
self.loc %= len(self.frames)
self.config(image=self.frames[self.loc])
self.after(self.delay, self.next_frame)
Which can be used as follows (supposing we defined a frame before):
gif = ImageLabel( frame )
gif.load( "path/to/spinner.gif" )
gif.place( anchor="center", relx= 0.7, rely=0.5 )
I would like to be able to run this created gif in parallel with another command, in the same frame. For example: let's say I am clicking a button which performs a long operation, in this case I would like to have a gif displayed among it, which runs parallely and be destroyed after the process finishes.
Can you help me? Thanks.
I solved by simply placing a gif and sending in parallel a function to execute the job:
self.spinner_gif.place( anchor = "center", relx = 0.7, rely = 0.55 )
threading.Thread( target = self.Function ).start()
and then at the end of the Function function:
self.spinner_gif.place_forget()

How to display an online .webp file in Tkinter using PIL

I have an animated .webp file which is hosted online and I tried to display it using Tkinter.Here is my code.
import urllib.request
from PIL import Image, ImageTk
root=tkinter.Tk()
u = urllib.request.urlopen("https://..../.....webp")
raw_data = u.read()
u.close()
im = Image.open(BytesIO(raw_data))
image = ImageTk.PhotoImage(im.resize((470,210)))
Label(root,image=image).pack()
root.mainloop()
This works without raising any errors but only displays one frame.Why is that so?
Is there any way how I can solve this issue
Based on #Atlas435's comment I have made a code which can display webp files in tkinter without downloading them.This is the code:
from io import BytesIO
from tkinter import *
import tkinter
import urllib.request
from PIL import Image, ImageTk
root=tkinter.Tk()
ar=0
import tkinter as tk
from PIL import Image, ImageTk
u = urllib.request.urlopen(url)
raw_data = u.read()
u.close()
im = Image.open(BytesIO(raw_data))
from itertools import count
class ImageLabel(tk.Label):
"""a label that displays images, and plays them if they are gifs"""
def load(self, im):
if isinstance(im, str):
im = im
self.loc = 0
self.frames = []
try:
for i in count(1):
self.frames.append(ImageTk.PhotoImage(im.copy().resize((470,210))))
im.seek(i)
except EOFError:
pass
try:
self.delay = im.info['duration']
except:
self.delay = 100
if len(self.frames) == 1:
self.config(image=self.frames[0])
else:
self.next_frame()
def unload(self):
self.config(image="")
self.frames = None
def next_frame(self):
if self.frames:
self.loc += 1
self.loc %= len(self.frames)
self.config(image=self.frames[self.loc])
self.after(self.delay, self.next_frame)
lbl = ImageLabel(root)
lbl.pack()
lbl.load(im)
root.mainloop()

Slide show program, event processing

I need the images to change if the cursor is inside the window without moving, but I managed to make the image changes only if the cursor is moving inside the window, how can I change the code?
from itertools import cycle
import tkinter as tk
from PIL import ImageTk, Image
import glob
image_files = glob.glob("*.jpg")
root = tk.Toplevel()
root.geometry("1600x900")
pictures = cycle((ImageTk.PhotoImage(file=image), image) for image in image_files)
picture_display = tk.Label(root)
picture_display.pack()
def show_slides(event):
img_object, img_name = next(pictures)
root.after(500, picture_display.config(image=img_object))
root.bind("<Motion>", show_slides)
root.mainloop()
You could bind the <Enter> and <Leave> events and use a flag to control the call, followed by using the after method to loop the function.
def show_slides(event=None):
global change_slide
img_object, img_name = next(pictures)
picture_display.config(image=img_object)
change_slide=root.after(500,show_slides)
def stop_slides(event):
root.after_cancel(change_slide)
root.bind("<Enter>", show_slides)
root.bind("<Leave>", stop_slides)
UPDATE
Using a flag might cause multiple calls being scheduled it the events happen during the 500ms delay, you can use after_cancel to terminate it.
You can calculate cursor position in loop and show image if it's located within tkinter window:
import glob
import tkinter as tk
from PIL import ImageTk
from itertools import cycle
class App(object):
def __init__(self):
self.root = tk.Tk()
self.root.geometry('900x600')
self.lbl = tk.Label(self.root)
self.lbl.pack()
files = glob.glob('*.jpg')
self.images = cycle((ImageTk.PhotoImage(file=f), f) for f in files)
self.show()
self.root.mainloop()
def show(self):
abs_coord_x = self.root.winfo_pointerx() - self.root.winfo_rootx()
abs_coord_y = self.root.winfo_pointery() - self.root.winfo_rooty()
if 0 <= abs_coord_x <= self.root.winfo_width() and 0 <= abs_coord_y <= self.root.winfo_height():
img_object, img_name = next(self.images)
self.lbl.config(image=img_object)
self.root.after(1000, self.show)
App()

Multithreading Progressbar and gif simultaneously in Tkinter

I am trying to thread gif animation which I put in a label Tkinter widget and a progressbar so that they run at the same time when the script is executed. Thereafter, I would like to use the time.sleep(10) to have them run at the same time for 10 seconds then have the progressbar stop using progressbar.stop(). My code is below:
import tkinter
from tkinter import ttk
from tkinter import *
import time
from PIL import Image, ImageTk
from itertools import count
import threading
def main_fun():
global progressbar, lbl
window = tkinter.Tk()
window.geometry("390x600") # Width x Height
# progress bar
progressbar = ttk.Progressbar(None) # ttk is method inside tkinter
progressbar.config(orient="horizontal",
mode='indeterminate', maximum=100, value=0)
progressbar.pack(side=TOP)
# gif image class
class ImageLabel(tkinter.Label):
"""a label that displays images, and plays them if they are gifs"""
def load(self, im):
if isinstance(im, str):
im = Image.open(im)
self.loc = 0
self.frames = []
try:
for i in count(1):
self.frames.append(ImageTk.PhotoImage(im.copy()))
im.seek(i)
except EOFError:
pass
try:
self.delay = im.info['duration']
except:
self.delay = 100
if len(self.frames) == 1:
self.config(image=self.frames[0])
else:
self.next_frame()
def unload(self):
self.config(image=None)
self.frames = None
def next_frame(self):
if self.frames:
self.loc += 1
self.loc %= len(self.frames)
self.config(image=self.frames[self.loc])
self.after(self.delay, self.next_frame)
lbl = ImageLabel(window)
lbl.pack(anchor="center")
lbl.load(
'C:/Users/*****/test.gif')
# thread the label with the gif
t = threading.Thread(target=lbl, args=(None,))
t.start()
window.mainloop()
main_fun()
progressbar.start(8) # 8 is for speed of bounce
t = threading.Thread(target=progressbar, args=(None,)
) # thread the progressbar
#t.daemon = True
t.start()
time.sleep(10) # 10 second delay, then progressbar must stop
progressbar.stop()
I am not familiar with threading and so I don't understand what I'm doing wrong. I get the errors:
TypeError: 'ImageLabel' object is not callable
TypeError: 'progressbar' object is not callable
Please assist.
You can use the answer given here to implement the progress bar on another thread. Also, what was wrong with what you did is that your progressbar isn't a callable object, nor does it override the run() method.

Play an Animated GIF in python with tkinter

I am wanting to create a virtual pet style game using python3 and tkinter. So far I have the main window and have started putting labels in, but the issue I am having is playing an animated gif. I have searched on here and have found some answers, but they keep throwing errors. The result I found has the index position of the gif using PhotoImage continue through a certain range.
# Loop through the index of the animated gif
frame2 = [PhotoImage(file='images/ball-1.gif', format = 'gif -index %i' %i) for i in range(100)]
def update(ind):
frame = frame2[ind]
ind += 1
img.configure(image=frame)
ms.after(100, update, ind)
img = Label(ms)
img.place(x=250, y=250, anchor="center")
ms.after(0, update, 0)
ms.mainloop()
When I run this in terminal with "pyhton3 main.py" I get the following error:
_tkinter.TclError: no image data for this index
What am I overlooking or completely leaving out?
Here is the link to the GitHub repository to see the full project:VirtPet_Python
Thanks in advance!
The error means that you tried to load 100 frames, but the gif has less than that.
Animated gifs in tkinter are notoriously bad. I wrote this code an age ago that you can steal from, but will get laggy with anything but small gifs:
import tkinter as tk
from PIL import Image, ImageTk
from itertools import count
class ImageLabel(tk.Label):
"""a label that displays images, and plays them if they are gifs"""
def load(self, im):
if isinstance(im, str):
im = Image.open(im)
self.loc = 0
self.frames = []
try:
for i in count(1):
self.frames.append(ImageTk.PhotoImage(im.copy()))
im.seek(i)
except EOFError:
pass
try:
self.delay = im.info['duration']
except:
self.delay = 100
if len(self.frames) == 1:
self.config(image=self.frames[0])
else:
self.next_frame()
def unload(self):
self.config(image="")
self.frames = None
def next_frame(self):
if self.frames:
self.loc += 1
self.loc %= len(self.frames)
self.config(image=self.frames[self.loc])
self.after(self.delay, self.next_frame)
root = tk.Tk()
lbl = ImageLabel(root)
lbl.pack()
lbl.load('ball-1.gif')
root.mainloop()
First of all, you need to know what is the last range of your GIF file. so by changing the different value of i, you will get it.For my condition is 31.
then just need to put the condition.So it will play gif infinitely.
from tkinter import *
import time
import os
root = Tk()
frames = [PhotoImage(file='./images/play.gif',format = 'gif -index %i' %(i)) for i in range(31)]
def update(ind):
frame = frames[ind]
ind += 1
print(ind)
if ind>30: #With this condition it will play gif infinitely
ind = 0
label.configure(image=frame)
root.after(100, update, ind)
label = Label(root)
label.pack()
root.after(0, update, 0)
root.mainloop()
A very simple approach would be to use multithreading.
To run the GIF infinitely in a Tkinter window you should follow the following:
Create a function to run the GIF.
Put your code to run the GIF inside while True inside the function.
Create a thread to run the function.
Run root.mainloop() in the primary flow of the program.
Use time.sleep() to control the speed of your animation.
Refer to my code below:
i=0
ph = ImageTk.PhotoImage(Image.fromarray(imageframes[i]))
imglabel=Label(f2,image=ph)
imglabel.grid(row=0,column=0)
def runthegif(root,i):
while True:
i = i + 7
i= i % 150
ph=ImageTk.PhotoImage(PhotoImage(file='images/ball.gif',format='gif -index %i' %i))
imagelabel=Label(f2,image=ph)
imagelabel.grid(row=0,column=0)
time.sleep(0.1)
t1=threading.Thread(target=runthegif,args=(root,i))
t1.start()
root.mainloop()

Categories

Resources