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()
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.
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()
Related
Today i was Trying to Display All .png files in my current Directory In a GUI With Python and Tkinter but The Code Was Not Wroking in a Way That i expected It only Displays The Last Image of the Folder and The Space for Other Images Was Blank.
# Importing Essentials
import os
from tkinter import Tk, Label, PhotoImage
# Initialize Tk root Class
root = Tk()
# Set Default Size of Our Tkinter Window - (WidthxHeight)
root.geometry("200x910")
# Set Minimum Size of Our Window - (Width, Height)
root.minsize(350, 200)
# Add a Label
WelcomeLabel = Label(text="All Images of This Folder")
WelcomeLabel.pack()
# Get All PNG Files in a List name pngFiles
pngFiles = []
for item in os.listdir():
if item.endswith(".png"):
pngFiles.append(item)
# Display All The PNG files to our GUI Screen
for file in pngFiles:
photoLoaded = PhotoImage(file=file)
mainImageLabel = Label(image=photoLoaded)
mainImageLabel.pack()
# Run The MainLoop
root.mainloop()
In tkinter the images must have references in memory, you cannot overwrite and reuse them, my solution is to make a list using pathlib, and then create the labels
If you display an image inside a function, then make sure to keep reference to the > image object in your Python program, either by storing it in a global variable or > by attaching it to another object.
Basics For Displaying Image In Tkinter Python
import pathlib
from tkinter import Tk, Label, PhotoImage
root = Tk()
root.geometry("200x910")
root.minsize(350, 200)
wellcome = Label(text="All Images of This Folder")
wellcome.pack()
# create a list from images with memory reference
images_with_ref = []
for img in pathlib.Path('.').glob('*.png'):
photoLoaded = PhotoImage(file=img)
images_with_ref.append(photoLoaded)
# create labels with correct images
for image in images_with_ref:
Label(image=image).pack()
root.mainloop()
With the current code you are overwriting your photoLoaded with every loop.
Instead you have can pack these photoLoaded in a list.
Additionally working with grids is recommended when working with multiple items.
both things are implemented in the code below:
# Importing Essentials
import os
from tkinter import Tk, Label, PhotoImage
# Initialize Tk root Class
root = Tk()
# Set Default Size of Our Tkinter Window - (WidthxHeight)
root.geometry("200x910")
# Set Minimum Size of Our Window - (Width, Height)
root.minsize(350, 200)
# Add a Label
WelcomeLabel = Label(text="All Images of This Folder")
WelcomeLabel.grid(row=0)
# Get All PNG Files in a List name pngFiles
pngFiles = []
for item in os.listdir():
if item.endswith(".png"):
pngFiles.append(item)
print(pngFiles)
# Display All The PNG files to our GUI Screen
i = 0
pics = []
for file in pngFiles:
pics.append(PhotoImage(file=file))
Label(root, image=pics[i]).grid(row=i + 1)
i += 1
# Run The MainLoop
root.mainloop()
I am writing an application that consists of three tkinter windows on the same page: a calendar, a notepad and a 'picture of the day' which is fetched from a bank of images when initiated by the user. The image is named after the calendar date + .jpg. It works fine...the first time. When I select another date and retrieve the image for that date, it comes up behind the first image in the 'picture of the day' window. The problem is that the first image does not disappear when replaced by another. I do not want to close the window, just close the current picture and replace it by the new one. There might a simple way, but I spent hours searching for it. The code below of part of the application. Hopefully, it shows where the problem is. Can you point me in the right direction? Thanks
from tkinter import *
from PIL import ImageTk, Image
from tkinter import filedialog
import os
photo = Toplevel()
photo.geometry("300x250+1300+150")
photo.resizable(width=True, height=True)
photo.attributes("-topmost", True)
def openfn(): # To go fetch a new image
filename = filedialog.askopenfilename(title='open')
return filename
root.destroy()
def open_img(): # To open a new image
x = openfn()
img = Image.open(x)
img = img.resize((225,200), Image.ANTIALIAS)
im = ImageTk.PhotoImage(img)
panel = Label(photo, image=im)
panel.image = im
panel.pack()
img=img.save(cal.calphoto) # Saves the image (calphoto is date + .jpg)
def retrieve_photo(): # To open an existing image
img=Image.open(cal.calphoto)
im = ImageTk.PhotoImage(img)
panel = Label(photo, image=im)
panel.image = im
panel.pack()
I changed a few things in your code. The main thing is the take the label with the image into the global scope so the function open_img() can make changes to it instead of creating a new label each time it is called. Below open_img() now configures the label to show the image selected each time it is called. (This is basically illustrating what Delrius said.)
from tkinter import *
from PIL import ImageTk, Image
from tkinter import filedialog
import os
photo = Toplevel()
photo.geometry("300x250+1300+150")
photo.resizable(width=True, height=True)
photo.attributes("-topmost", True)
panel = Label(photo) # panel Label moved into global scope so the
# functions below can access it
def openfn():
filename = filedialog.askopenfilename(title='open')
return filename
root.destroy()
def open_img():
x = openfn()
img = Image.open(x)
img = img.resize((225,200), Image.ANTIALIAS)
im = ImageTk.PhotoImage(img)
panel.config(image=im) # panel label is configured to show image selected
panel.image = im
panel.pack()
I am working on a project, and one of our spec is to be able to display PGM images with TKinter.
Well, for some reason, after switching the image a specific number of times, our application would always end with a Segmentation Fault...
For debugging purposes, I then made a very simple TKinter program that lists all the images in a directory, and lets the user switch the displayed one using a slider.
This program has the same issue: after every 26th image, I get a segmentation faut with a "malloc(): memory corruption".
Here is my debug code:
#!/usr/bin/python3
from tkinter import *
import os
class MainFrame():
def __init__(self, main):
self.input_canvas = Canvas(main, width=640, height=480)
self.input_canvas.grid(column=1, row=1)
self.token = False
self.files = os.listdir('.')
self.files.sort()
self.scale = Scale(main, orient='horizontal', from_=0, to=len(self.files), resolution=1,
tickinterval=1/len(self.files), length=600, command=self.load_image)
self.scale.grid(column=1, row=2)
self.load_image("0")
def load_image(self, scale_value):
# The token is only here to make sure load_image is not called multiple times at the same time
if not self.token:
self.token = True
else:
return
input_pic = PhotoImage(file=self.files[int(scale_value)])
self.input_canvas.create_image(0, 0, anchor=NW, image=input_pic)
self.input_canvas.image = input_pic
self.token = False
if __name__ == '__main__':
root = Tk()
MainFrame(root)
root.mainloop()
I guess I encountered a bug with TKinter, but I didn't find any information about that, so maybe I'm doing something wrong that I just can't see :/
Also, I wanted to use PIL with the ImageTk module, but for some reason it couldn't open our test images (they were blank).
Here is an archive with some of our test images (those are 16bits PGM):
https://mega.nz/#!CkxwzaZK!2d0vf4ORHh9H9LqWeKpfaZDTPrsmM89lYntrlO44AOE
Any help is welcome!
I have located this useful code for Tkinter animations from https://www.daniweb.com/programming/software-development/threads/396918/how-to-use-animated-gifs-with-tkinter ,supplied by "vegaseat".
I have adapted a similar design for displaying gifs animations to a project. I am wishing to implement this as a function to certain areas of a script, e.g. importing modules etc. I have tried a few approaches but when I called this as a function, it first runs the animation and then imports the module (as we would expect).
I guess I am exploring ways to get this to work concurrently...while the script is importing modules( or running another process where I wish to display the animation), the animation would be displayed, and then disappear, until the next call. Suggestions would be appreciated.
Thanks a lot.
# mimic an animated GIF displaying a series of GIFs
# an animated GIF was used to create the series of GIFs
# with a common GIF animator utility
import time
from Tkinter import *
root = Tk()
imagelist = ["dog001.gif","dog002.gif","dog003.gif",
"dog004.gif","dog005.gif","dog006.gif","dog007.gif"]
# extract width and height info
photo = PhotoImage(file=imagelist[0])
width = photo.width()
height = photo.height()
canvas = Canvas(width=width, height=height)
canvas.pack()
# create a list of image objects
giflist = []
for imagefile in imagelist:
photo = PhotoImage(file=imagefile)
giflist.append(photo)
# loop through the gif image objects for a while
for k in range(0, 1000):
for gif in giflist:
canvas.delete(ALL)
canvas.create_image(width/2.0, height/2.0, image=gif)
canvas.update()
time.sleep(0.1)
root.mainloop()
EDIT: I am attempting to implement the code,below, per some helpful suggestions. The goal is to begin the animation, while the application is importing the modules in the "IMPORTS" function, and then have it destroyed after the imports are completed.
# Import modules
from Tkinter import *
from PIL import ImageTk
from PIL import Image
import os,time
from os.path import dirname
from os.path import join
def IMPORTS():
import tkMessageBox
from ttk import Combobox
import csv,datetime
import xlrd,xlwt
import getpass
import traceback
import arcpy
from arcpy import AddMessage
import win32com.client
inGif = #root image (.gif)
FramesFolder = #Folder containing frames of the root image
W=Toplevel()
W.wm_overrideredirect(True) # I wish to only display the widget spinning without the window frame
imagelist = [os.path.join(FramesFolder,s) for s in os.listdir(FramesFolder) if not s.endswith('db')]
# extract width and height info
photo = PhotoImage(file=imagelist[0])
width = photo.width()
height = photo.height()
canvas = Canvas(W,width=width, height=height)
canvas.pack()
# create a list of image objects
giflist = []
for imagefile in imagelist:
photo = PhotoImage(file=imagefile)
giflist.append(photo)
timer_id = None
def start_loading(n=0):
global timer_id
gif = giflist[n%len(giflist)]
canvas.create_image(gif.width()//2, gif.height()//2, image=gif)
timer_id = W.after(100, start_loading, n+1) # call this function every 100ms
def stop_loading():
if timer_id:
W.after_cancel(timer_id)
canvas.delete(ALL)
start_loading()
IMPORTS()
stop_loading()
# The spinning widget should be completely destroyed before moving on...
It is returning
"NameError: name 'tkMessageBox' is not defined"
You can use Tk.after() and Tk.after_cancel() to start and stop the animation:
timer_id = None
def start_loading(n=0):
global timer_id
gif = giflist[n%len(giflist)]
canvas.create_image(gif.width()//2, gif.height()//2, image=gif)
timer_id = root.after(100, start_loading, n+1) # call this function every 100ms
def stop_loading():
if timer_id:
root.after_cancel(timer_id)
canvas.delete(ALL)
Then, you can call start_loading() before the long process and call stop_loading() after the long process:
start_loading()
long_process() # your long process
stop_loading()
I am trying to display an image to a tkinter GUI using tkinter.Label() widget. The procedure seems simple and straightforward, but this code doesn't work!
code:
import Tkinter as tk
import Image, ImageTk, sys
filename = 'AP_icon.gif'
im = Image.open(filename) # Image is loaded, because the im.show() works
tkim = ImageTk.PhotoImage(im)
root = tk.Tk()
label = tk.Label(root, image = tkim) # Here is the core problem (see text for explanation)
label.image = tkim # This is where we should keep the reference, right?
label.grid (row = 0, column = 0)
tk.Button(root, text = 'quit', command = lambda: sys.exit()).grid(row = 1, column = 1)
root.mainloop()
When we execute this code, it doesn't compile, giving an error:
TclError: image "pyimage9" doesn't exist
When I define label without its parent root, No compilation error occurs, but the GUI does not display any image!
Can anyone identify what could be the issue?
This problem happens when we attempt to run the above code in Ipython. And it can be solved by changing the line
root = tk.Tk() to
root = tk.Toplevel()
You need to create the root widget before you call any other tkinter functions. Move the creation of root to be before the creation of the image.
The general way which I use to display an image in tkinter is:
import Tkinter as tk
root = tk.Tk()
image1 = tk.PhotoImage(file = 'name of image.gif')
# If image is stored in the same place as the python code file,
# otherwise you can have the directory of the image file.
label = tk.Label(image = image1)
label.image = image1 # yes can keep a reference - good!
label.pack()
root.mainloop()
In the above case it works but you have something like:
import Tkinter as tk
image = tk.PhotoImage(file = 'DreamPizzas.gif') #here this is before root = tk.Tk()
root = tk.Tk()
# If image is stored in the same place as the python code file,
# otherwise you can have the directory of the image file.
label = tk.Label(image = image)
label.image = image
label.pack()
root.mainloop()
this gives me a runtime error: too early to create image.
but you have said that your error is image pyimage9 doesn't exist, this is strange because at the top you have set filename to 'AP_icon.gif', so you would think that you get a different error as I dont know where pyimage9 comes from. This makes me think that maybe you are getting the file name incorrect somewhere? You also need to move root = tk.Tk() to the top under imports.
Restart the Kernel to get rid of the error "TclError: image "pyimage9" doesn't exist"
Try the following code as I am able to rectify the same error:
window=Tk()
c=Canvas(window,height=2000,width=2000)
p=PhotoImage(file='flower1.gif',master = c)
c.create_image(500,500,image=p)