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()
Related
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 want to open images in Python by selecting them in a dialog box, how can I do that? I tried tkinter and easygui but when I use them the program freezes and never loads. Any suggestions?
As mentioned in the comments, you should provide a minimal reproducible example. Since you are a new member, I am giving you this example, which can be found here. https://www.geeksforgeeks.org/loading-images-in-tkinter-using-pil/
from tkinter import *
from PIL import ImageTk, Image
from tkinter import filedialog
def open_img():
# Select the Imagename from a folder
x = openfilename()
# opens the image
img = Image.open(x)
# resize the image and apply a high-quality down sampling filter
img = img.resize((250, 250), Image.ANTIALIAS)
# PhotoImage class is used to add image to widgets, icons etc
img = ImageTk.PhotoImage(img)
# create a label
panel = Label(root, image=img)
# set the image as img
panel.image = img
panel.grid(row=2)
def openfilename():
# open file dialog box to select image
# The dialogue box has a title "Open"
filename = filedialog.askopenfilename(title='"pen')
return filename
# Create a window
root = Tk()
# Set Title as Image Loader
root.title("Image Loader")
# Set the resolution of window
root.geometry("550x300+300+150")
# Allow Window to be resizable
root.resizable(width=True, height=True)
# Create a button and place it into the window using grid layout
btn = Button(root, text='open image', command=open_img).grid(row=1, columnspan=4)
root.mainloop()
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 have a project that draws turtle on a Canvas widget. I already know how to save the drawing but how do you open them. This is an example of what I'm trying to say :
from tkinter import *
root = Tk()
... #Just creating widgets
def openDrawing:
...#What goes in here ?
fileMenu.add_command(label = "Open Drawing",command=openDrawing,accelerator="Ctrl+O")
root.mainloop()
PostScript file formats containing image data are supported by the PIL library. So you can open your turtle .ps file like this:
import PIL
# Load the .ps file using PIL
ps_file = PIL.Image.open("turtle_file.ps")
ps_turtle = PIL.ImageTk.PhotoImage(ps_file)
...
...
# Let us suppose you want to display it on a label
label = Label(image=ps_turtle)
label.image = ps_turtle # keep a reference
label.pack()
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()