Reputation: 51
I tried to create a slideshow in python to loop over 5500 images faster than I could manually. I used tkinter and the parameter slide_interval should do the job. The slideshow will indeed be longer if I set slide_interval=5000 for example, but it makes no differnce if I set it to 500,50 or 5, it will still take approximately same number of seconds to display each image, while what I would be interested in would be 1 or 0.5 seconds spent per image.
Here is the code:
#!/usr/bin/env python3
"""Display a slideshow from a list of filenames"""
import os
import tkinter
from itertools import cycle
from PIL import Image, ImageTk
class Slideshow(tkinter.Tk):
"""Display a slideshow from a list of filenames"""
def __init__(self, images, slide_interval):
"""Initialize
images = a list of filename
slide_interval = milliseconds to display image
"""
tkinter.Tk.__init__(self)
self.geometry("+0+0")
self.slide_interval = slide_interval
self.images = None
self.set_images(images)
self.slide = tkinter.Label(self)
self.slide.pack()
def set_images(self, images):
self.images = cycle(images)
def center(self):
"""Center the slide window on the screen"""
self.update_idletasks()
w = self.winfo_screenwidth()
h = self.winfo_screenheight()
size = tuple(int(_) for _ in self.geometry().split('+')[0].split('x'))
x = w / 2 - size[0] / 2
y = h / 2 - size[1] / 2
self.geometry("+%d+%d" % (x, y))
def set_image(self):
"""Setup image to be displayed"""
self.image_name = next(self.images)
filename, ext = os.path.splitext(self.image_name)
self.image = ImageTk.PhotoImage(Image.open(self.image_name))
def main(self):
"""Display the images"""
self.set_image()
self.slide.config(image=self.image)
self.title(self.image_name)
self.center()
self.after(self.slide_interval, self.start)
def start(self):
"""Start method"""
self.main()
self.mainloop()
if __name__ == "__main__":
slide_interval = 1
import glob
images = glob.glob("traralgon/*.jpg")
# start the slideshow
slideshow = Slideshow(images, slide_interval)
slideshow.start()
Upvotes: 3
Views: 9287
Reputation: 10809
Something like this maybe:
import tkinter as tk
class Application(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title("Slideshow")
self.geometry("256x256")
self.resizable(width=False, height=False)
self.current_slide = tk.Label(self)
self.current_slide.pack()
self.duration_ms = 1000
def set_image_directory(self, path):
from pathlib import Path
from PIL import Image, ImageTk
from itertools import cycle
image_paths = Path(path).glob("*.jpg")
self.images = cycle(zip(map(lambda p: p.name, image_paths), map(ImageTk.PhotoImage, map(Image.open, image_paths))))
def display_next_slide(self):
name, self.next_image = next(self.images)
self.current_slide.config(image=self.next_image)
self.title(name)
self.after(self.duration_ms, self.display_next_slide)
def start(self):
self.display_next_slide()
def main():
application = Application()
application.set_image_directory("dir/to/images")
application.start()
application.mainloop()
if __name__ == "__main__":
import sys
sys.exit(main())
You'll have to ask yourself if you want to load all images upfront before the slideshow starts, and just keep them in memory (this could take some time depending on how many images you have), or if you want to load images only when they should be displayed (if the interval between images is especially short, you may notice that loading the next image slows things down).
Upvotes: 4
Reputation: 386020
The problem is that you're calling self.main()
and self.mainloop()
on every interval. That will cause huge performance problems and will probably crash after only a second or two. You should never call mainloop()
more than once, and there's no point in recreating the entire UI on every loop.
Instead, you need to write a function that gets the image and then configures the existing label rather than recreating the whole GUI on each iteration.
Example:
def main(self):
...
self.next_image()
def next_image(self):
self.image_name = next(self.images)
filename, ext = os.path.splitext(self.image_name)
self.image = tkinter.PhotoImage(file=self.image_name)
self.slide.configure(image=self.image)
self.after(self.slide_interval, self.next_image)
Upvotes: 1