Domanda

I am trying to display images in a directory randomly in a single window that changes every 3 seconds. I also want it to be cross platform, as I am developing in Windows, but it will run on linux.

Currently I have this working code that iterates through all the image files of a directory with a mouse click (Code Below)

import os, sys, Tkinter, Image, ImageTk

def button_click_exit_mainloop (event):
    event.widget.quit()

root = Tkinter.Tk()
root.bind("<Button>", button_click_exit_mainloop)
root.geometry('+%d+%d' % (-5,-5)) #controls where the window is

#gets list of file names in certain directory. In this case, the directory it is in
dirlist = os.listdir('.') 

for f in dirlist:
    try:
        image1 = Image.open(f)
        root.geometry('%dx%d' % (image1.size[0],image1.size[1]))
        tkpi = ImageTk.PhotoImage(image1)
        label_image = Tkinter.Label(root, image=tkpi)
        label_image.place(x=0,y=0,width=image1.size[0],height=image1.size[1])
        root.mainloop() # wait until user clicks the window

    except Exception, e:
        pass

The way it does this however is when the mouse clicks on the window it calls a function to close the widget.

The problem I am having is how to call this function, or close the widget without an event. Any suggestions?

This is what I currently have. This doesn't work obviously because it is stuck in the root.mainloop(), but it shows what I have generally in mind (Code below)

import os, sys, Tkinter, Image, ImageTk, random

root = Tkinter.Tk()
root.geometry('+%d+%d' % (-5,-5)) #controls where the window is

#gets list of file names in certain directory. In this case, the directory it is in
dirlist = os.listdir('.') #might not be in order, CHECK!!!

while True:
    randInt = random.randint(0, 1)
    image = Image.open(dirlist[randInt])
    root.geometry('%dx%d' % (image.size[0],image.size[1]))
    tkpi = ImageTk.PhotoImage(image)
    label_image = Tkinter.Label(root, image=tkpi)
    label_image.place(x=0,y=0,width=image.size[0],height=image.size[1])
    root.mainloop()
    time.sleep(3)

Thank you!

-Jonathan

EDIT: Response to Bryan Oakley: I tried what you suggested, and this looks like the solution.

The function is being called every 3 seconds, and a window is being created, but the image is not being placed in the window.

Is it that I do not have access to the root? How do I gain access?

Here is what I have currently:

import os, sys, Tkinter, Image, ImageTk, random

def changeImage():
    #gets list of file names in certain directory. In this case, the directory it is in
    dirlist = os.listdir('.') #might not be in order, CHECK!!!

    #get random image
    randInt = random.randint(0, 1)
    image = Image.open(dirlist[randInt])

    #set size to show, in this case the whole picture
    root.geometry('%dx%d' % (image.size[0],image.size[1]))

    #Creates a Tkinter compatible photo image
    tkpi = ImageTk.PhotoImage(image)

    #Put image in a label and place it
    label_image = Tkinter.Label(root, image=tkpi)
    label_image.place(x=0,y=0,width=image.size[0],height=image.size[1])

    # call this function again in three seconds
    root.after(3000, changeImage)


root = Tkinter.Tk()
root.geometry('+%d+%d' % (-5,-5)) #controls where the window is

changeImage()

root.mainloop()

Thank you!!

SOLUTION EDIT: I did not change the code so the label is only created once, so a label is created with each call. I did not do this because this could be applied to many other variables (dirlist = os.listdir('.') for exmaple), but would make the code harder to read. I did not see any disadvantage other than maybe more cycles used? I did not see a memory increase over time, which is all that mattered to me.

Here is the code, thank you Bryan Oakley for helping me!!

import os, Tkinter, Image, ImageTk, random

def changeImage():
    global tkpi #need global so that the image does not get derefrenced out of function

    #gets list of file names in certain directory. In this case, the directory it is in
    dirlist = os.listdir('.')

    #get random image
    randInt = random.randint(0, 1)
    image = Image.open(dirlist[randInt])

    #set size to show, in this case the whole picture
    root.geometry('%dx%d' % (image.size[0],image.size[1]))

    #Creates a Tkinter compatible photo image
    tkpi = ImageTk.PhotoImage(image)

    #Put image in a label and place it
    label_image = Tkinter.Label(root, image=tkpi)
    label_image.place(x=0,y=0,width=image.size[0],height=image.size[1])

    # call this function again in 1/2 a second
    root.after(500, changeImage)

tkpi = None #create this global variable so that the image is not derefrenced

root = Tkinter.Tk()
root.geometry('+%d+%d' % (-5,-5)) #controls where the window is
changeImage()
root.mainloop()
È stato utile?

Soluzione

You need to remove your infinite loop -- tkinter already has one built-in. Instead, use after to periodically call a function:

def changeImage():
    <do whatever you want, such as swapping out images>

    # call this function again in three seconds
    root.after(3000, changeImage)

Then, in your main program you would call the function before calling mainloop:

root = Tkinter.Tk()
...
changeImage()
root.mainloop()

Also, you don't need changeImage to create a new label widget every time -- create the label once outside the function, and just change the image each time it is called.

Altri suggerimenti

All credit goes to BryanOakley for finding the root cause of the error. I cleaned up your code slightly. The imports also changed a little since I'm running Python3, but wanted to make sure it would work.

import os, sys, tkinter, random # N.B. tkinter not Tkinter in py3
from PIL import Image, ImageTk  # these are submodules in pillow py3

class Root(tkinter.Tk): # I buried this in a class. I prefer that for tkinter
    def __init__(self):
        super().__init__() # call Tk.__init__

        self.CYCLEDELAY = 3000 # 3 second per cycle

        # create and place the label once.
        self.image_label = tkinter.Label(self)
        self.image_label.place(x=0,y=0)

        # call our function
        self.changeImage()

    def changeImage(self):
        """Change the image on a delay"""
        dirlist = os.listdir('.')
        image = Image.open(random.choice(dirlist))
        # you had a funky method of getting a random member here. I cleaned it up

        i_width, i_height = image.size

        self.geometry("{}x{}".format(i_width,i_height))
        # change root's geometry using string formatting (preferred)

        self.image_label.configure(width=i_width, height=i_height)
        # change the label's geometry using string formatting

        self.tkpi = ImageTk.PhotoImage(image)
        self.image_label.configure(image=self.tkpi)
        # configure the label to use the PhotoImage

        self.after(self.CYCLEDELAY,self.changeImage)
        # loop!

root = Root()
root.mainloop()
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top