Tkinter Image viewer - python

I'm trying to create a program using Tkinter that displays a thumbnail from several different directories in on window. So far I have this:
import Tkinter as tk
from PIL import Image, ImageTk
import Image, os
root = tk.Tk()
root.title('Shot Viewer')
w, h, x, y = 1000, 1000, 0, 0
root.geometry("%dx%d+%d+%d" % (w, h, x, y))
#quit
def quit(root):
root.quit()
root.destroy()
path = "/media/Expansion Drive/Heros Mission 3/Scenes/Scene 1-3/Shots/"
labels = []
for files in os.listdir(path):
number = files.split("_")[1]
filed = "/media/Expansion Drive/Heros Mission 3/Scenes/Scene 1-3/Shots/Shot_{} /Frames/Shot_{}_000000.png".format(number, number)
if os.path.lexists(filed) == 'False':
pass
else:
im = Image.open(imageFile)
im.thumbnail((96, 170), Image.ANTIALIAS)
image = ImageTk.PhotoImage(im)
label = tk.Label(root, image=image, name=number)
labels.append(label)
print labels
for label in labels:
panel = label.grid()
panel2.grid(row=2, column=1)
button2 = tk.Button(panel2, text='Quit', command=lambda root=root:quit(root))
button2.grid(row=1, column=1, sticky='NW')
root.mainloop()
However this is not working, does anyone have any suggestions?
Thanks
Tom

Use the glob module to help find the relevant files.
As for images failing to appear:
import Tkinter as tk
from PIL import Image, ImageTk
import glob
root = tk.Tk()
labels = []
for jpeg in glob.glob("C:/Users/Public/Pictures/Sample Pictures/*.jpg")[:5]:
im = Image.open(jpeg)
im.thumbnail((96, 170), Image.ANTIALIAS)
photo = ImageTk.PhotoImage(im)
label = tk.Label(root, image=photo)
label.pack()
label.img = photo # *
# * Each time thru the loop, the name 'photo' has a different
# photoimage assigned to it.
# This means that you need to create a separate, 'longer-lived'
# reference to each photoimage in order to prevent it from
# being garbage collected.
# Note that simply passing a photoimage to a Tkinter widget
# is not enough to keep that photoimage alive.
labels.append(label)
root.mainloop()

I do not think you are handling it correctly where you say panels = label.grid(). Instead, try to just do label.grid so it is not an assignment operator but an action.

Related

image is not displayed on canvas

I'm working on a python project that takes an image that the user selects and removes a certain color from it, I'm trying to display the selected image along with a button that takes you to the selection menu on the file explorer on the user's computer, the button works just fine, but the image is not being displayed at all. any help would be appreciated.
the code:
#import
from PIL import Image, ImageTk
from fileinput import filename
import tkinter as tk
from tkinter import filedialog
from tkinter import *
root = Tk()
root.geometry("750x500")
root.title("Shibly Letter Head")
canvas = Canvas(root, width = 500, height = 500)
canvas.pack()
#uploaded info
def UploadAction(event=None):
from PIL import Image
filename = filedialog.askopenfilename()
print('Selected:', filename)
img = Image.open(filename)
img.show()
image_data = img.load()
height,width = img.size
for loop1 in range(height):
for loop2 in range(width):
r,g,b = image_data[loop1,loop2]
image_data[loop1,loop2] = 0,g,b
img.save('changed.jpeg')
#display image
canvas.create_image(10,
10,
anchor=NW,
image= filename
)
#button
button = tk.Button(root, text='Open', height=5, width=45, command=UploadAction)
button.place(x=220, y=419)
root.mainloop()
PS, apologies if this is a dumb question but I'm still a beginner and couldn't fix this problem myself.

How to show images in a Listbox in tkinter?

I want to show images in the Listbox tkinter widget. My code looks like this:
mylist = Listbox(self.labelFrame3, yscrollcommand=self.yscrollbar.set, selectmode=SINGLE)
mylist.grid(row=0, column=0, rowspan=21, columnspan=21, sticky=(N, S, E, W))
img = Image.open('modeldata/photosamples/amanullahattamuhammad2.0.jpg') # PIL solution
img = img.resize((200, 200), Image.ANTIALIAS) # The (250, 250) is (height, width)
img = ImageTk.PhotoImage(img)
How to print this image in Listbox?
As someone mentioned in a comment you can't put images in a Listbox, but as I mentioned in another, you could use a Text widget instead because you can put images into them. Below is a relatively simple demonstration of how something like that can be done. Its two buttons illustrate how it can display just text or a combination of the two simultaneously. Text widget are actually very versatile.
Anyway, since presumably you want a scrollable list, the Text subclass named tkinter.scrolledtext is used instead of plain one to save work. Since it's a subclass, it can do anything its baseclass can do.
from pathlib import Path
from PIL import Image, ImageTk
import tkinter as tk
from tkinter.constants import *
from tkinter.scrolledtext import ScrolledText
class App:
def __init__(self, image_folder_path, image_file_extensions):
self.root = tk.Tk()
self.image_folder_path = image_folder_path
self.image_file_extensions = image_file_extensions
self.create_widgets()
self.root.mainloop()
def create_widgets(self):
self.list_btn = tk.Button(self.root, text='List Images', command=self.list_images)
self.list_btn.grid(row=0, column=0)
self.show_btn = tk.Button(self.root, text='Show Images', command=self.show_images)
self.show_btn.grid(row=1, column=0)
self.text = ScrolledText(self.root, wrap=WORD)
self.text.grid(row=2, column=0, padx=10, pady=10)
self.text.image_filenames = []
self.text.images = []
def list_images(self):
''' Create and display a list of the images the in folder that have one
of the specified extensions. '''
self.text.image_filenames.clear()
for filepath in Path(self.image_folder_path).iterdir():
if filepath.suffix in self.image_file_extensions:
self.text.insert(INSERT, filepath.name+'\n')
self.text.image_filenames.append(filepath)
def show_images(self):
''' Show the listed image names along with the images themselves. '''
self.text.delete('1.0', END) # Clear current contents.
self.text.images.clear()
# Display images in Text widget.
for image_file_path in self.text.image_filenames:
img = Image.open(image_file_path).resize((64, 64), Image.ANTIALIAS)
img = ImageTk.PhotoImage(img)
self.text.insert(INSERT, image_file_path.name+'\n')
self.text.image_create(INSERT, padx=5, pady=5, image=img)
self.text.images.append(img) # Keep a reference.
self.text.insert(INSERT, '\n')
image_folder_path = 'modeldata/photosamples'
image_file_extensions = {'.jpg', '.png'}
App(image_folder_path, image_file_extensions)
Here's it running on a folder on my computer with the images shown:

why function does not work to get display image on label tkinter python, but it works not using function

This code without function does work to get display image on label tkinter python 3.8.5.
from tkinter import *
from PIL import Image, ImageTk
import os
import os.path
root = Tk()
root.title("Test_image")
root.geometry("1000x600")
# def display_image():
images_folder = "C:/TextSoundSource/B-V/I-WORD-E-00-B-V-0-renamed"
os.chdir(images_folder)
displaying_img = "022.jpg"
# Open Image
img = Image.open(displaying_img)
# Resize Image
resized = img.resize((300, 200), Image.ANTIALIAS)
resized_img = ImageTk.PhotoImage(resized)
image_display_frame = LabelFrame(root, text="Image", font=(
"times", 15, "bold"), bg="Navyblue", fg="white", bd=5)
image_display_frame.place(x=0, y=0, width=1000, height=300)
img_label = Label(image_display_frame, image=resized_img)
img_label.pack(pady=20)
# display_image()
root.mainloop()
But when using function like below it does not work, how do I get images by using function?
from tkinter import *
from PIL import Image, ImageTk
import os
import os.path
# == == == == == == == == == == == ========
# Using function does not work ???? why ???
# == == == == == == == == == == == ========
root = Tk()
root.title("Test_image")
root.geometry("1000x600")
def display_image():
images_folder = "C:/TextSoundSource/B-V/I-WORD-E-00-B-V-0-renamed"
os.chdir(images_folder)
displaying_img = "001.jpg"
# Open Image
img = Image.open(displaying_img)
# Resize Image
resized = img.resize((300, 200), Image.ANTIALIAS)
resized_img = ImageTk.PhotoImage(resized)
image_display_frame = LabelFrame(root, text="Image", font=(
"times", 15, "bold"), bg="Navyblue", fg="white", bd=5)
image_display_frame.place(x=0, y=0, width=1000, height=300)
img_label = Label(image_display_frame, image=resized_img)
img_label.pack(pady=20)
display_image()
root.mainloop()
I appreciate in advance, please let me know how to get image display by using function in tkinter python.
In Python, a function has its own local scope for object names. When you exit the function all names are garbage collected and not available for the program.
You can add the image reference to the global scope with the global keyword, as #Cool cloud suggests. Another common technique is to save the image reference to the label widget:
img_label.image = resized_img

tkinter background image in Frame

I want to put a background image in a Frame, this is the code that I'm trying to run without success.
import tkinter as tk
from tkinter import *
root = tk.Tk()
F1 = Frame(root)
F1.grid(row=0)
photo = PhotoImage(file="sfondo.png")
label = Label(F1, image=photo)
label.image = photo
label.place(x=0, y=0)
b = tk.Button(label, text="Start")
b.grid(row=8, column=8)
root.mainloop()
If I run the code as this, only a little point in the top left corner is displayed (the frame without nothing in, even if I placed the label inside of it). If I replace the label parent with root, it displays the button with a little part of the image as backgound (only the perimeter of the button is colored for a few pixels). However what I want is a full displayed background image in the frame where I can put the widgets that I want.
I tried to with the place method as this and PIL module
import tkinter as tk
from tkinter import *
from PIL import Image, ImageTk
root = tk.Tk()
F1 = Frame(root)
F1.grid(row=0)
image = Image.open("sfondo.png")
render = ImageTk.PhotoImage(image)
img = tk.Label(F1, image=render)
img.image = render
img.place(x=0, y=40)
b = tk.Button(img, text="Start")
b.grid(row=8, column=8)
root.mainloop()
Here more or less I'm having the same problems, if I set the parent of the label to root, the button is displayed with the perimeter coloured.
If I set the parent to F1 nothing happens and in both cases if I set the parent as root and remove the button, the image is fully displayed.
But what I want is that the image is fully displayed in the frame and after diplay on the background image the widgets.
You could put an image on a Canvas, and then place a Button on that by putting it inside a Canvas window object which can hold any Tkinter widget.
Additional widgets can be added in a similar fashion, each inside its own Canvas window object (since they can hold only a single widget each). You can workaround that limitation by placing a Frame widget in the Canvas window, and then putting other widgets inside it.
Here's an example showing how to display a single Button:
from PIL import Image, ImageTk
import tkinter as tk
IMAGE_PATH = 'sfondo.png'
WIDTH, HEIGTH = 200, 200
root = tk.Tk()
root.geometry('{}x{}'.format(WIDTH, HEIGHT))
canvas = tk.Canvas(root, width=WIDTH, height=HEIGTH)
canvas.pack()
img = ImageTk.PhotoImage(Image.open(IMAGE_PATH).resize((WIDTH, HEIGTH), Image.ANTIALIAS))
canvas.background = img # Keep a reference in case this code is put in a function.
bg = canvas.create_image(0, 0, anchor=tk.NW, image=img)
# Put a tkinter widget on the canvas.
button = tk.Button(root, text="Start")
button_window = canvas.create_window(10, 10, anchor=tk.NW, window=button)
root.mainloop()
Screenshot:
Edit
While I don't know of a way to do it in Frame instead of a Canvas, you could derive your own Frame subclass to make adding multiple widgets easier. Here's what I mean:
from PIL import Image, ImageTk
import tkinter as tk
class BkgrFrame(tk.Frame):
def __init__(self, parent, file_path, width, height):
super(BkgrFrame, self).__init__(parent, borderwidth=0, highlightthickness=0)
self.canvas = tk.Canvas(self, width=width, height=height)
self.canvas.pack()
pil_img = Image.open(file_path)
self.img = ImageTk.PhotoImage(pil_img.resize((width, height), Image.ANTIALIAS))
self.bg = self.canvas.create_image(0, 0, anchor=tk.NW, image=self.img)
def add(self, widget, x, y):
canvas_window = self.canvas.create_window(x, y, anchor=tk.NW, window=widget)
return widget
if __name__ == '__main__':
IMAGE_PATH = 'sfondo.png'
WIDTH, HEIGTH = 350, 200
root = tk.Tk()
root.geometry('{}x{}'.format(WIDTH, HEIGTH))
bkrgframe = BkgrFrame(root, IMAGE_PATH, WIDTH, HEIGTH)
bkrgframe.pack()
# Put some tkinter widgets in the BkgrFrame.
button1 = bkrgframe.add(tk.Button(root, text="Start"), 10, 10)
button2 = bkrgframe.add(tk.Button(root, text="Continue"), 50, 10)
root.mainloop()
Result:
Update
It recently dawned on me that there actually is a simpler way to do this than creating a custom Frame subclass as shown in my previous edit above. The trick is to place() a Label with image on it in the center of the parent Frame — you are then free to use other geometry managers like pack() and grid() as you normally would — as illustrated in the sample code below. Not only is it less complicated, it's also a more "natural" way of adding widgets than needing to call a non-standard method such as add().
from PIL import Image, ImageTk
import tkinter as tk
IMAGE_PATH = 'sfondo.png'
WIDTH, HEIGHT = 250, 150
root = tk.Tk()
root.geometry('{}x{}'.format(WIDTH, HEIGHT))
# Display image on a Label widget.
img = ImageTk.PhotoImage(Image.open(IMAGE_PATH).resize((WIDTH, HEIGHT), Image.ANTIALIAS))
lbl = tk.Label(root, image=img)
lbl.img = img # Keep a reference in case this code put is in a function.
lbl.place(relx=0.5, rely=0.5, anchor='center') # Place label in center of parent.
# Add other tkinter widgets.
button = tk.Button(root, text="Start")
button.grid(row=0, column=0)
button = tk.Button(root, text="Continue")
button.grid(row=0, column=1, padx=10)
root.mainloop()
Result#2
P.S. You can download a copy of the sfondo.png background image from here.

Loop through images with Tkinter canvas in Python

I'm trying to write a script that will allow the user to select a folder with images and then save the coordinates of the user's clicks on each image. To do this I would like to display each image file on a Tkinter canvas, save click locations, then when the canvas is closed, open the next image.
I can get this to work for a single image with the code below (adapted from this question). I was hoping the for File in imgs loop would keep opening the next image, but it does not. I suspect I need a on_closing function to tell Tkinter to open another image.
What's the proper way to have Tkinter open the next image after closing an image?
from Tkinter import *
from tkFileDialog import askopenfilenames, askopenfilename, askdirectory
from PIL import Image, ImageTk
import cv2
import numpy as np
import os
if __name__ == "__main__":
root = Tk()
#setting up a tkinter canvas with scrollbars
frame = Frame(width=1920, height=1080, bd=2, relief=SUNKEN)
frame.grid_rowconfigure(0, weight=1)
frame.grid_columnconfigure(0, weight=1)
xscroll = Scrollbar(frame, orient=HORIZONTAL)
xscroll.grid(row=1, column=0, sticky=E+W)
yscroll = Scrollbar(frame)
yscroll.grid(row=0, column=1, sticky=N+S)
canvas = Canvas(frame, bd=0, xscrollcommand=xscroll.set, yscrollcommand=yscroll.set)
canvas.config(width=1920, height=1080)
canvas.grid(row=0, column=0, sticky=N+S+E+W)
xscroll.config(command=canvas.xview)
yscroll.config(command=canvas.yview)
frame.pack(fill=BOTH,expand=1)
# Function to be called when mouse is clicked
def save_coords(event):
coords.append([event.x, event.y])
# def on_closing():
# Open the next image file
# Create empty list for coordinate arrays to be appended to
coords = []
# Choose multiple images
img_dir = askdirectory(parent=root, initialdir="D:/Temp/", title='Choose folder')
os.chdir(img_dir)
imgs = os.listdir(img_dir)
#imgs = askopenfilenames(parent=root, initialdir="D:/Temp/cvCal/", title='Choose images')
for File in imgs:
img = ImageTk.PhotoImage(Image.open(File).resize((1280,720), Image.ANTIALIAS))
canvas.create_image(0,0,image=img,anchor="nw")
canvas.config(scrollregion=canvas.bbox(ALL))
canvas.bind("<Button 1>",save_coords)
# on_closing()...
root.mainloop()
It would be a lot easier if you can use a Label instead:
Also, note there is no reason to use ImageTk since loading an image from a file is built into tkinter as Tkinter.PhotoImage.
Also, I converted your wildcard import to a normal import; wildcard imports are messy and against PEP8.
Lastly, I don't know what you mean with 'on closing', so I added a button to advance to the next image. This will throw a StopIteration error on the last image that you will need to handle.
import Tkinter as tk
from tkFileDialog import askdirectory
import os
# Create empty list for coordinate arrays to be appended to
coords = []
# Function to be called when mouse is clicked
def save_coords(event):
click_loc = [event.x, event.y]
print "you clicked on", click_loc
coords.append(click_loc)
# Function to load the next image into the Label
def next_img():
img_label.img = tk.PhotoImage(file=next(imgs))
img_label.config(image=img_label.img)
root = tk.Tk()
# Choose multiple images
img_dir = askdirectory(parent=root, initialdir="D:/Temp/", title='Choose folder')
os.chdir(img_dir)
imgs = iter(os.listdir(img_dir))
img_label = tk.Label(root)
img_label.pack()
img_label.bind("<Button-1>",save_coords)
btn = tk.Button(root, text='Next image', command=next_img)
btn.pack()
next_img() # load first image
root.mainloop()
print coords

Categories

Resources