I want to control the Speed of the images animation using the slider. So im trying to do the following but instead I get many images reappearing on the same canvas when i vary the slider. I want to vary the time interval between one image and the next using the slider.
from tkinter import *
#import tkFont
import random
from time import sleep
root = Tk()
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
scale = Scale(frame, from_=0, to=100, command=self.update)
scale.grid(row=0)
def update(self, duty):
#Importing the images. They are named a1.gif, a2.gif...a7.gif
frame=[]
for i in range(1,10):
fname="CORE\\a"+str(i)+".gif"
frame+=[PhotoImage(file=fname)]
wrap = Canvas(root, width=200, height=140)
wrap.pack()
def do_animation(currentframe):
def do_image():
wrap.create_image(100,70,image=frame[currentframe], tag='ani')
# Delete the current picture if one exists
wrap.delete('ani')
try:
do_image()
except IndexError:
# End of image list reached, start over at the first image
#- works for an arbitrary number of images
currentframe = 0
do_image()
wrap.update_idletasks() #Force redraw
currentframe = currentframe + 1
# Call myself again to keep the animation running in a loop
root.after(100, do_animation, currentframe)
# Start the animation loop just after the Tkinter loop begins
root.after(100, do_animation, 0)
app = App(root)
#app.geometry("800x480")
root.mainloop()
python
You're creating a new canvas every time, and not using the speed from the slider. I think this is what you want.
from tkinter import *
#import tkFont
import random
from time import sleep
root = Tk()
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.scale = Scale(frame, from_=0, to=1000)
self.scale.grid(row=0)
self.wrap = Canvas(root, width=200, height=140)
self.wrap.pack()
self.update()
def update(self):
#Importing the images. They are named a1.gif, a2.gif...a7.gif
frame=[]
for i in range(1,10):
fname="CORE\\a"+str(i)+".gif"
frame+=[PhotoImage(file=fname)]
def do_animation(currentframe):
def do_image():
self.wrap.create_image(100,70,image=frame[currentframe], tag='ani')
# Delete the current picture if one exists
self.wrap.delete('ani')
try:
do_image()
except IndexError:
# End of image list reached, start over at the first image
#- works for an arbitrary number of images
currentframe = 0
do_image()
self.wrap.update_idletasks() #Force redraw
currentframe = currentframe + 1
# Call myself again to keep the animation running in a loop
root.after(self.scale.get(), do_animation, currentframe)
# Start the animation loop just after the Tkinter loop begins
root.after(100, do_animation, 0)
app = App(root)
#app.geometry("800x480")
root.mainloop()
Related
I'm trying to make a video player that play's a video full screen. The user is also able to toggle onto another frame that I want to take up the full GUI and to be able to toggle in between them.. I have written some code and It works fine when I pack the video player to root but then when I put it inside a frame it shrinks down to a much smaller size than the GUI. Why is this happening and how can I fix it?
This is the code that has the video playing and resizing to the GUI size dynamically
import datetime
import tkinter as tk
from tkinter import *
from turtle import width
from tkVideoPlayer import TkinterVideo
#changing the visible frame on the screen
def showFrame (frame):
frame.tkraise()
root = tk.Tk()
root.title("Hology")
width1, height1= int(root.winfo_screenwidth()), int(root.winfo_screenheight())
root.geometry(str(width1-200) + "x" + str(height1-200))
vid_player = TkinterVideo(master=root, scaled=True)
# vid_player.set_size((width1-200, height1-200))
vid_player.pack(expand=True, fill=BOTH)
progress_value = tk.IntVar(root)
vid_player.bind("<<SecondChanged>>", loopVid)
load_file= "hol.mp4"
vid_player.load(load_file)
vid_player.play()
# vid_player.pack(expand=True, fill="both")
holo.tkraise()
root.mainloop()
This is where it is placed inside a frame
import datetime
import tkinter as tk
from tkinter import *
from turtle import width
from tkVideoPlayer import TkinterVideo
global vidVal
vidVal = 0
#changing the visible frame on the screen
def showFrame (frame):
frame.tkraise()
root = tk.Tk()
root.title("Hology")
width1, height1= int(root.winfo_screenwidth()), int(root.winfo_screenheight())
root.geometry(str(width1-200) + "x" + str(height1-200))
holo = Frame(root)
timeline = Frame(root)
holo.grid(row=0,column=0)
timeline.grid(row=0,column=0)
# root.attributes('-fullscreen',True)
# for frame in (holo, timeline):
# frame.master.geometry(str(width1-200) + "x" + str(height1-200))
# # frame.grid(row=1, column=1, columnspan=10, rowspan=8, sticky=tk.EW)
# # frame.pack_propagate(False)
# frame.pack()
vid_player = TkinterVideo(master=holo, scaled=True)
# vid_player.set_size((width1-200, height1-200))
vid_player.pack(expand=True, fill=BOTH)
load_file= "hol.mp4"
vid_player.load(load_file)
vid_player.play()
# vid_player.pack(expand=True, fill="both")
holo.tkraise()
root.mainloop()
Any help enabling me to keep the video at full size when inside a frame would be greatly appreciated. I have spent some time looking through the documentation and I know it will be a simple fix or something silly, I'm just stuck! :(
Thank you
In my code, I am trying to make a loading screen for a frogger game but for some reason I am encountering a problem where I display a picture and then do the .sleep function before displaying a label over the top of it however it displays both of them at the same time it just runs the code 1 second after it should, can anyone help?
Here is my code below:
from tkinter import *
import tkinter as tk
import time
window = Tk()
window.geometry("1300x899")
LoadingScreen = PhotoImage(file = "FroggerLoad.gif")
Loading = Label(master = window, image = LoadingScreen)
Loading.pack()
Loading.place(x = 65, y = 0)
time.sleep(1)
FroggerDisplay = Label(master = window, font ("ComicSans",100,"bold"),text = "Frogger")
FroggerDisplay.pack()
FroggerDisplay.place(x = 500, y = 300)
window.mainloop()
When you use time.sleep(1) before starting the window.mainloop(), the window is created only after 1 second, and the FroggerDisplay label will be created at the same time with it. So, you can't use time.sleep(seconds) now.However, you can use window.after(ms, func) method, and place into the function all the code between time.sleep(1) and window.mainloop(). Note, that unlike the time.sleep(seconds) you must give the time to window.after (the first argument) as milliseconds.Here is the edited code:
from tkinter import *
def create_fd_label():
frogger_display = Label(root, font=("ComicSans", 100, "bold"), text="Frogger") # create a label to display
frogger_display.place(x=500, y=300) # place the label for frogger display
root = Tk() # create the root window
root.geometry("1300x899") # set the root window's size
loading_screen = PhotoImage(file="FroggerLoad.gif") # create the "Loading" image
loading = Label(root, image=loading_screen) # create the label with the "Loading" image
loading.place(x=65, y=0) # place the label for loading screen
root.after(1000, create_fd_label) # root.after(ms, func)
root.mainloop() # start the root window's mainloop
PS: 1) Why do you use .pack(...) and then .place(...) methods at the same time - the first one (.pack(...) here) will be ignored by Tkinter.
2) It's better to use a Canvas widget for creating a game - unlike labels it supports transparency and simpler to use. For example:
from tkinter import *
root = Tk() # create the root window
root.geometry("1300x899") # set the root window's size
canv = Canvas(root) # create the Canvas widget
canv.pack(fill=BOTH, expand=YES) # and pack it on the screen
loading_screen = PhotoImage(file="FroggerLoad.gif") # open the "Loading" image
canv.create_image((65, 0), image=loading_screen) # create it on the Canvas
root.after(1000, lambda: canv.create_text((500, 300),
font=("ComicSans", 100, "bold"),
text="Frogger")) # root.after(ms, func)
root.mainloop() # start the root window's mainloop
Note: you might need to change coords with Canvas.
I'm having a problem that when i try to create an image on the canvas i am unable to produce the image. However before my after() loop is initiated i am able to create and configure my images. Also, i am able to remove objects from my canvas using canvas.delete() in my after() loop so i do still have some level of control.
I am on windows 8.1, using Python 3.5.4
import tkinter as tk
from PIL import Image, ImageTk
from math import floor
import numpy as np
from scipy import ndimage
root = tk.Tk()
HEIGHT = 600
WIDTH = 600
CANVAS_MID_X = WIDTH / 2
CANVAS_MID_Y = HEIGHT / 2
def my_mainloop():
#THIS WORKS!!! REMOVES THE DIAL CREATED BEFORE THE MAINLOOP
canvas.delete(dial_1)
#THIS WORKS BELOW BEFORE MAINLOOP, BUT NOT IT WONT WORK! (use opencv?)
img = dial_1_img_resized
img2 = img.rotate(45, expand=True)
dial_1_photo_new = ImageTk.PhotoImage(img2)
dial_2 = canvas.create_image((dial_1_center), image=dial_1_photo_new, anchor=tk.E)
'''CANT DRAW TO CANVAS IN AFTER LOOP'''
print("loop!")
root.after(4000,my_mainloop)
'''-------------------Create Canvas, and starting dials in their starting positions---------------------'''
canvas = tk.Canvas(root, width=HEIGHT, height=WIDTH, bg="black")
canvas.grid(row=0, column=0)
dial_1_path = "gauge1.png"
dial_1_width = 400
dial_1_img = Image.open(dial_1_path, 'r') #open image
dial_1_img_ratio = int(dial_1_img.size[1]) / int(dial_1_img.size[0])
dial_1_img_resized = dial_1_img.resize((dial_1_width, floor(dial_1_img_ratio * dial_1_width)), 1)
dial_1_photo = ImageTk.PhotoImage(dial_1_img_resized)
dial_1_center = (CANVAS_MID_X, CANVAS_MID_Y)
#CREATE DIAL ON CANVAS, THIS WORKS!!
dial_1 = canvas.create_image((dial_1_center), image=dial_1_photo)
'''Start Main Loop'''
root.after(0, my_mainloop)
root.mainloop()
Therefore my question is: is there a way to manipulate and create canvas images in the after() loop? (called my_mainloop) any help is appreciated!
You'll need to save a reference to your photo because it is being garbage collected after my_mainloop runs. You can add it to your canvas object for instance:
canvas.dial_1_photo_new = ImageTk.PhotoImage(img2)
dial_2 = canvas.create_image((dial_1_center), image=canvas.dial_1_photo_new, anchor=tk.E)
I am trying to display the animation from my gif image. From my previous question, I discovered that Tkinter doesn't animate images automatically. My Tk interface shows the first frame of the image, and when I click the button to play its animation, it does nothing. It's likely something to do with the command associated with the button. Here's the code:
from Tkinter import *
import Tkinter
root = Tk()
photo_path = "/users/zinedine/downloads/091.gif"
photo = PhotoImage(
file = photo_path
)
def run():
frame = 1
while True:
try:
photo = PhotoImage(
file = photo_path,
format = "gif - {}".format(frame)
)
frame = frame + 1
except Exception: # This because I don't know what exception it would raise
frame = 1
break
picture = Label(image = photo)
picture.pack()
picture.configure(run())
animate = Button(
root,
text = "animate",
command = run()
)
animate.pack()
root.geometry("250x250+100+100")
root.mainloop()
You can use the universal Tk widget after() method to schedule a function to run after a specified delay given in milliseconds. This only happens once, so typically the function itself also calls after() to perpetuate the process.
In the code below a custom AnimatedGif container class is defined which loads and holds all the frames of animated sequence separately in a list which allows quick (random) access to them using [] indexing syntax. It reads individual frames from the file using the -index indexvalue image format suboption mentioned on the photo Tk manual page.
I got the test image shown below from the Animation Library website.
Here's how things should look when it's initially started.
You should be able use the same technique to animate multiple images or those that are attached to other kinds of widgets, such as Button and Canvas instances.
try:
from tkinter import *
except ImportError:
from Tkinter import * # Python 2
class AnimatedGif(object):
""" Animated GIF Image Container. """
def __init__(self, image_file_path):
# Read in all the frames of a multi-frame gif image.
self._frames = []
frame_num = 0 # Number of next frame to read.
while True:
try:
frame = PhotoImage(file=image_file_path,
format="gif -index {}".format(frame_num))
except TclError:
break
self._frames.append(frame)
frame_num += 1
def __len__(self):
return len(self._frames)
def __getitem__(self, frame_num):
return self._frames[frame_num]
def update_label_image(label, ani_img, ms_delay, frame_num):
global cancel_id
label.configure(image=ani_img[frame_num])
frame_num = (frame_num+1) % len(ani_img)
cancel_id = root.after(
ms_delay, update_label_image, label, ani_img, ms_delay, frame_num)
def enable_animation():
global cancel_id
if cancel_id is None: # Animation not started?
ms_delay = 1000 // len(ani_img) # Show all frames in 1000 ms.
cancel_id = root.after(
ms_delay, update_label_image, animation, ani_img, ms_delay, 0)
def cancel_animation():
global cancel_id
if cancel_id is not None: # Animation started?
root.after_cancel(cancel_id)
cancel_id = None
root = Tk()
root.title("Animation Demo")
root.geometry("250x125+100+100")
ani_img = AnimatedGif("small_globe.gif")
cancel_id = None
animation = Label(image=ani_img[0]) # Display first frame initially.
animation.pack()
Button(root, text="start animation", command=enable_animation).pack()
Button(root, text="stop animation", command=cancel_animation).pack()
Button(root, text="exit", command=root.quit).pack()
root.mainloop()
Here's an alternative version of my previous answer. Although also based on the universal Tk widget after() method, it uses the PIL (or the pillow fork of it) module to read the gif image file. With PIL it's not only easy to extract each frame from the file, but also to get the delay (or "duration") between frames of the animation directly from the gif file — which eliminates guessing what it should be for different files.
try:
from tkinter import *
except ImportError:
from Tkinter import *
from PIL import Image, ImageSequence, ImageTk
class AnimatedGif(object):
""" Animated GIF Image Container. """
def __init__(self, image_file_path):
# Read in all the frames of a multi-frame gif image.
self._frames = []
img = Image.open(image_file_path)
for frame in ImageSequence.Iterator(img):
photo = ImageTk.PhotoImage(frame)
photo.delay = frame.info['duration'] * 10 # Add attribute.
self._frames.append(photo)
def __len__(self):
return len(self._frames)
def __getitem__(self, frame_num):
return self._frames[frame_num]
def update_label_image(label, ani_img, frame_num):
""" Change label image to given frame number of AnimatedGif. """
global cancel_id
frame = ani_img[frame_num]
label.configure(image=frame)
frame_num = (frame_num+1) % len(ani_img) # Next frame number.
cancel_id = root.after(frame.delay, update_label_image, label, ani_img, frame_num)
def enable_animation():
""" Start animation of label image. """
global cancel_id
if cancel_id is None: # Animation not started?
cancel_id = root.after(ani_img[0].delay, update_label_image, animation, ani_img, 0)
def cancel_animation():
""" Stop animation of label image. """
global cancel_id
if cancel_id is not None: # Animation started?
root.after_cancel(cancel_id)
cancel_id = None
root = Tk()
root.title("Animation Demo")
root.geometry("250x125+100+100")
ani_img = AnimatedGif("small_globe.gif")
cancel_id = None
animation = Label(image=ani_img[0]) # Display first frame initially.
animation.pack()
Button(root, text="start animation", command=enable_animation).pack()
Button(root, text="stop animation", command=cancel_animation).pack()
Button(root, text="exit", command=root.quit).pack()
root.mainloop()
I'm writing a slideshow program with Tkinter, but I don't know how to go to the next image without binding a key.
import os, sys
import Tkinter
import Image, ImageTk
import time
root = Tkinter.Tk()
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
root.overrideredirect(1)
root.geometry("%dx%d+0+0" % (w, h))
root.focus_set()
root.bind("<Escape>", lambda e: e.widget.quit())
image_path = os.path.join(os.getcwd(), 'images/')
dirlist = os.listdir(image_path)
for f in dirlist:
try:
image = Image.open(image_path+f)
tkpi = ImageTk.PhotoImage(image)
label_image = Tkinter.Label(root, image=tkpi) # ?
label_image.place(x=0,y=0,width=w,height=h)
root.mainloop(0)
except IOError:
pass
root.destroy()
I would like to add a time.sleep(10) "instead" of the root.mainloop(0) so that it would go to the next image after 10s. Now it changes when I press ESC. How can I have a timer there?
edit: I should add that I don't want another thread that does a sleep even though it works.
You can try
root.after(10*1000, root.quit)
There's no need to do a loop over your images -- you're already running in a loop (mainloop) so take advantage of it. The typical way to do this is to create a method that draws something, waits for a period of time, then calls itself. This isn't recursion, it's just telling the main loop "after N seconds, call me again".
Here's a working example:
import glob
import Tkinter
class Slideshow:
def __init__(self, pattern="*.gif", delay=10000):
root = Tkinter.Tk()
root.geometry("200x200")
# this label will be used to display the image. Make
# it automatically fill the whole window
label = Tkinter.Label(root)
label.pack(side="top", fill="both", expand=True)
self.current_image = None
self.image_label = label
self.root = root
self.image_files = glob.glob(pattern)
self.delay = delay # milliseconds
# schedule the first image to appear as soon after the
# the loop starts as possible.
root.after(1, self.showImage)
root.mainloop()
def showImage(self):
# display the next file
file = self.image_files.pop(0)
self.current_image = Tkinter.PhotoImage(file=file)
self.image_label.configure(image=self.current_image)
# either reschedule to display the file,
# or quit if there are no more files to display
if len(self.image_files) > 0:
self.root.after(self.delay, self.showImage)
else:
self.root.after(self.delay, self.root.quit)
def quit(self):
self.root.quit()
if __name__ == "__main__":
app=Slideshow("images/*.gif", 1000)