Advice on Python tkinter PIL slideshow script - python-3.x

I am trying to run a script that seems to work for others, but not me: https://gist.github.com/terencewu/034e09f0e318c621516b
I have all the packages installed on my mac, using python3 pip, os is 10.15.7.
The only change I've made to the script is to put in a path to a folder with some pictures in it.
When I run the script, I don't get any errors, and the Python terminal prints the full correct paths, but no slideshow. I'm using IDLE 3.9.1.
I would be very grateful for any suggestions as to how to get the script working.
Here's the code (curr_dir contains 3 jpg files):
import tkinter as tk
from PIL import Image, ImageTk
import time
import sys
import os
class HiddenRoot(tk.Tk):
def __init__(self):
tk.Tk.__init__(self)
#hackish way, essentially makes root window
#as small as possible but still "focused"
#enabling us to use the binding on <esc>
self.wm_geometry("0x0+0+0")
self.window = MySlideShow(self)
self.window.startSlideShow()
class MySlideShow(tk.Toplevel):
def __init__(self, *args, **kwargs):
tk.Toplevel.__init__(self, *args, **kwargs)
#remove window decorations
self.overrideredirect(True)
#save reference to photo so that garbage collection
#does not clear image variable in show_image()
self.persistent_image = None
self.imageList = []
self.pixNum = 0
#used to display as background image
self.label = tk.Label(self)
self.label.pack(side="top", fill="both", expand=True)
self.getImages()
def getImages(self):
curr_dir = "/Users/kenpeel/Pictures/testpics/FolderA/FolderA1"
for root, dirs, files in os.walk(curr_dir):
for f in files:
if f.endswith(".png") or f.endswith(".jpg"):
img_path = os.path.join(root, f)
print(img_path)
self.imageList.append(img_path)
def startSlideShow(self, delay=4): #delay in seconds
myimage = self.imageList[self.pixNum]
self.pixNum = (self.pixNum + 1) % len(self.imageList)
self.showImage(myimage)
#its like a callback function after n seconds (cycle through pics)
self.after(delay*1000, self.startSlideShow)
def showImage(self, filename):
image = Image.open(filename)
img_w, img_h = image.size
scr_w, scr_h = self.winfo_screenwidth(), self.winfo_screenheight()
width, height = min(scr_w, img_w), min(scr_h, img_h)
image.thumbnail((width, height), Image.ANTIALIAS)
#set window size after scaling the original image up/down to fit screen
#removes the border on the image
scaled_w, scaled_h = image.size
self.wm_geometry("{}x{}+{}+{}".format(scaled_w,scaled_h,0,0))
# create new image
self.persistent_image = ImageTk.PhotoImage(image)
self.label.configure(image=self.persistent_image)
slideShow = HiddenRoot()
slideShow.bind("<Escape>", lambda e: slideShow.destroy()) # exit on esc
slideShow.mainloop()
The IDLE shell prints the paths, but there are no other errors:
/Users/kenpeel/Pictures/testpics/FolderA/FolderA1/PICT0001.jpg
/Users/kenpeel/Pictures/testpics/FolderA/FolderA1/PICT0002.jpg
/Users/kenpeel/Pictures/testpics/FolderA/FolderA1/PICT0003.jpg
>>>
It seems it could be Mac OS compatability with Pillow and tkinter?

Related

Tkinter program displays blank screen on linux, works on windows

I've been writing this code for a slideshow program in Linux. The problem I'm having is that when run from a windows environment it works perfectly (Full-screen resized images), however when run from a virtual Linux (Ubuntu x64) environment a blank white canvas appears with no images being displayed.
The code:
from PIL import Image, ImageTk
import tkinter as tk
import os
import glob
import random
class App(tk.Tk):
def __init__(self, image_files, delay):
tk.Tk.__init__(self)
self.w = self.winfo_screenwidth()
self.h = self.winfo_screenheight()
self.overrideredirect(1)
self.geometry("%dx%d+0+0" % (self.w, self.h))
self.delay = delay
self.pictures = []
self.track_img_ndex = 0
for img in image_files:
self.pictures.append(img)
self.picture_display = tk.Label(self)
self.picture_display.pack(expand=True, fill="both")
def show_slides(self):
if self.track_img_ndex < len(self.pictures):
x = self.pictures[self.track_img_ndex]
self.track_img_ndex +=1
original_image = Image.open(x)
resized = original_image.resize((self.w, self.h),Image.ANTIALIAS)
new_img = ImageTk.PhotoImage(resized)
self.picture_display.config(image=new_img)
self.picture_display.image = new_img
self.title(os.path.basename(x))
self.after(self.delay, self.show_slides)
else:
print("End of list!")
delay = 5000
playlist = glob.glob(r'\mnt\hgfs\E\Images\*.*')
random.shuffle(playlist)
image_files = playlist
app = App(image_files, delay)
app.show_slides()
app.mainloop()
Any help would be appreciated!
winfo_screenwidth() and winfo_screenheight() give not correct display size on Linux. Use full-screen mode to fix it.
The code:
from PIL import Image, ImageTk
import tkinter as tk
import os
import glob
import random
class App(tk.Tk):
def __init__(self, image_files, delay):
tk.Tk.__init__(self)
# start window size (any)
self.w = 500
self.h = 500
self.attributes("-fullscreen", True) # Activate full-screen mode
# Change full-screen mode bind
self.bind("<F11>", lambda event: self.attributes("-fullscreen",
not self.attributes("-fullscreen")))
# Bind full-screen mode exit
self.bind("<Escape>", lambda event: self.attributes("-fullscreen", False))
self.geometry("%dx%d+100+100" % (self.w, self.h))
self.delay = delay
self.pictures = []
self.track_img_ndex = 0
for img in image_files:
self.pictures.append(img)
self.picture_display = tk.Label(self)
self.picture_display.pack(expand=True, fill="both")
def show_slides(self):
if self.track_img_ndex < len(self.pictures):
x = self.pictures[self.track_img_ndex]
self.track_img_ndex += 1
original_image = Image.open(x)
window_size_now = (self.winfo_width(), self.winfo_height()) # Getting current window size :
resized = original_image.resize(window_size_now, Image.ANTIALIAS)
new_img = ImageTk.PhotoImage(resized)
self.picture_display.config(image=new_img)
self.picture_display.image = new_img
self.title(os.path.basename(x))
self.after(self.delay, self.show_slides)
else:
print("End of list!")
_delay = 5000
playlist = glob.glob(r'/home/yaroslav_admin/PycharmProjects/LearnPython_01/imgs/*.*')
random.shuffle(playlist)
_image_files = playlist
app = App(_image_files, _delay)
app.after(100, app.show_slides) # delayed start,
# because tkinter need some time to count full-screen window size in main loop !
app.mainloop()

display video camera on two different windows tkinter python

i have a GUI python application that contain two different function windows and i'am trying to display video camera in the second window of tkinter application.
But, the video frame always be showing on the first windows!! i don't know if it is a problem of threads, or camera librairy. is there any fault in my code?
thanks in advance.
# add the necessairy librairy
import tkinter as tk
import threading
import time
import subprocess
from imutils.video import VideoStream
import time
import imutils
import cv2
import argparse
from PIL import Image
from PIL import ImageTk
class PhotoBoothApp:
def __init__(self,vs):
# store the video stream object and output path, then initialize
# the most recently read frame, thread for reading frames, and
# the thread stop event
self.vs = vs
self.frame = None
self.thread = None
self.stopEvent_2 = None
# initialize the root window and image panel
self.root = tk.Tk()
self.panel = None
self.user_input = tk.StringVar(self.root)
self.but_frame = tk.Frame(self.root)
w = 800 # width for the Tk root
h = 500 # height for the Tk root
# get screen width and height
ws = self.root.winfo_screenwidth() # width of the screen
hs = self.root.winfo_screenheight() # height of the screen
# calculate x and y coordinates for the Tk master window
x = (ws/3) - (w/3)
y = (hs/3) - (h/3)
# set the dimensions of the screen
# and where it is placed
self.root.geometry('%dx%d+%d+%d' % (w, h, x, y))
# when open the second window i want it to be on toplevel; it means when i click outside the frame it won't get hide
self.root.title("Test thread")
# create a button, that when pressed, will take the current
# frame and save it to file
btn = tk.Button(self.but_frame, bd = '5',text=" photo ")
self.but_frame.pack(side="left")
# start a thread that constantly pools the video sensor for
# the most recently read frame
self.stopEvent_2 = threading.Event()
self.thread = threading.Thread(target=self.videoLoop, args=())
self.thread.start()
# set a callback to handle when the window is closed
self.root.wm_protocol("WM_DELETE_WINDOW", self.onClose)
def videoLoop(self):
# DISCLAIMER:
# I'm not a GUI developer, nor do I even pretend to be. This
# try/except statement is a pretty ugly hack to get around
# a RunTime error that Tkinter throws due to threading
try:
# keep looping over frames until we are instructed to stop
while not self.stopEvent_2.is_set():
# grab the frame from the video stream and resize it to
# have a maximum width of 300 pixels
self.frame = self.vs.read()
self.frame = imutils.resize(self.frame, width=300)
#cv2.imshow('video player', self.frame)
# OpenCV represents images in BGR order; however PIL
# represents images in RGB order, so we need to swap
# the channels, then convert to PIL and ImageTk format
image = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGB)
image = Image.fromarray(image)
image = ImageTk.PhotoImage(image)
# if the panel is not None, we need to initialize it
if self.panel is None:
self.panel = tk.Label(image=image)
self.panel.image = image
self.panel.pack(side="left", padx=10, pady=10)
# otherwise, simply update the panel
else:
self.panel.configure(image=image)
self.panel.image = image
except RuntimeError :
print("[INFO] caught a RuntimeError_________________________________________")
def onClose(self):
# set the stop event, cleanup the camera, and allow the rest of
# the quit process to continue
print("[INFO] closing...")
self.stopEvent_2.set()
self.vs.stop()
self.root.quit()
# here's the main window
Mafenetre = tk.Tk()
#set main window title
Mafenetre.title("GUI")
Mafenetre['bg']='white' # couleur de fond
# get screen width and height
wf1= Mafenetre.winfo_screenwidth()
hf1= Mafenetre.winfo_screenheight()
A = str(wf1)
B = str(hf1)
# set the dimensions of the screen
# and where it is placed
w = 500 # width for the Tk root
h = 500 # height for the Tk root
# get screen width and height
ws = Mafenetre.winfo_screenwidth() # width of the screen
hs = Mafenetre.winfo_screenheight() # height of the screen
# calculate x and y coordinates for the Tk master window
x = (ws/2) - (w/2)
y = (hs/2) - (h/2)
# set the dimensions of the screen
# and where it is placed
Mafenetre.geometry('%dx%d+%d+%d' % (w, h, x, y))
# add test button to the main window
reglage = tk.Button(Mafenetre, bd = '5',text=" PARAM ", bg='#c42034', fg='white',font= "Helvetica", 12))
reglage.pack(side=tk.TOP, padx=6,pady=35)
camera = VideoStream(0).start()
pba = PhotoBoothApp(camera)
Mafenetre.mainloop()

How to enhance window size selection on a tkinter project including button-image as label?

I'm currently working on a little project on python-3.x including some tkinter ressources. My program is made to display on a screen a list of pictures included in a directory, each picture is put on a button that is a sixth of the original image, and if we click on it, it display the image on his original size on a new window. The original window is set by the amount of pictures i put in the columns (i can choose in the code) and i ve made a scrollbar because i have to work with a lot of pictures.
But here is my problem, it's works fine except that if i change the window size, like reduce it for example, the buttons don't follow, they just vanish behind the window, and with the scrollbar.
I'm not particularly good in python so i was wondering that maybe by doing like a threading we could get the window size in live and then if the window size is inferior/superior of our columns of buttons, we could resize it and change the amount of columns then reload the page, but i will have to work with multiple image so it will take a lot of time.
from tkinter import *
from tkinter.filedialog import *
from tkinter.messagebox import *
from PIL import Image, ImageTk
import tkinter as tk
import glob
import os
import cv2
import copy
import _thread
import time
folder = 'X:/users/Robin/data/dataset-valid/visu/*.jpg'
a=glob.glob(folder)
fic = "../data/list.txt"
fichObj=open(fic,"w")
p = []
for f in a:
fichObj.write(f+"\n")
fichObj.close()
class SuperPhoto(object):
def __init__(self, photo , image):
self.photo = photo
temp = cv2.resize(image, (int((self.photo.width())/6) , int((self.photo.height())/6)))
red = temp[:,:,2].copy()
blue = temp[:,:,0].copy()
temp[:,:,0] = red
temp[:,:,2] = blue
temp = Image.fromarray(temp)
self.miniature = ImageTk.PhotoImage(temp)
def agrandir(self):
Newfen=Toplevel()
Newfen.geometry("+60+60")
#self.photo.resize((500,500))
print(type(self.photo))
label = Label(Newfen, image=self.photo, width=self.photo.width(), height=self.photo.height())
label.image = self.photo # keep a reference!
label.pack()
if os.path.exists (fic): #os.path utile
count = len(open(fic).readlines())
print(count)
#lin = open(fic).readlines()
#print(lin)
class ScrollableCanvas(Frame):
def __init__(self, parent, *args, **kw):
Frame.__init__(self, parent, *args, **kw)
canvas=Canvas(self,bg='#FFFFFF',width=300,height=300,scrollregion=(0,0,500,500))
canvas.update_idletasks()
vbar=Scrollbar(self,orient=VERTICAL)
vbar.pack(side=RIGHT, fill=Y)
vbar.config(command=canvas.yview)
canvas.config(width=1200,height=700)
canvas.config(yscrollcommand=vbar.set)
canvas.pack(side=LEFT,expand=True,fill=BOTH)
# create a frame inside the canvas which will be scrolled with it
self.interior = interior = Frame(canvas)
interior_id = canvas.create_window(0, 0, window=interior, anchor=NW )
# track changes to the canvas and frame width and sync them,
# also updating the scrollbar
def _configure_interior(event):
# update the scrollbars to match the size of the inner frame
size = (interior.winfo_reqwidth(), interior.winfo_reqheight())
canvas.config(scrollregion="0 0 %s %s" % size)
if interior.winfo_reqwidth() != canvas.winfo_width():
# update the canvas's width to fit the inner frame
canvas.config(width=interior.winfo_reqwidth())
interior.bind('<Configure>', _configure_interior)
def _configure_canvas(event):
if interior.winfo_reqwidth() != canvas.winfo_width():
# update the inner frame's width to fill the canvas
canvas.itemconfigure(interior_id, width=canvas.winfo_width())
canvas.bind('<Configure>', _configure_canvas)
class Main_frame(Frame):
# Init
def __init__(self, fenetre_principale=None):
Frame.__init__(self, fenetre_principale)
self.grid()
self.scrollable_canvas = ScrollableCanvas(self)
self.scrollable_canvas.grid(row=1,column=1)
nbCol = 4
for file in a:
image = Image.open(file)
photo = ImageTk.PhotoImage(image)
w = photo.width()
L.append(int(w/6))
#print(L)
sumL = int(sum(L)/nbCol)
print(sumL)
p.append(SuperPhoto(photo, cv2.imread(file)))
for ligne in range(int(count/nbCol)):
for colonne in range(nbCol):
photo = p[ligne * nbCol + colonne]
button = Button(self.scrollable_canvas.interior, image=photo.miniature, command=photo.agrandir)
button.grid(row=ligne, column=colonne)
if __name__ == "__main__":
root = Tk()
root.title("VISU")
root.geometry("+0+0")
L= []
interface = Main_frame(fenetre_principale=root)
root.update_idletasks()
print(root.winfo_width())
print(root.geometry())
interface.mainloop()
So, I except this program to work like a classic directory display, with the columns that change automatically when we resize the window and with the scrollbar that follow it.
If you have any solutions it will really help me ..
You can try it, just put some jpeg pictures in a directory and change the folder variable with the link of your directory.
Thanks in advance for your help, if you have any questions to understand more clearly what i've said don't hesitate.
Each time the root window is resized, a <Configure> event is triggered. Catch it as follows:
def resize(event):
root.update_idletasks()
#update all image sizes here if needed
#all widgets can be 're-grided' here based on new width and height of root window
root.bind('<Configure>', resize)
If you want to ensure that your window cannot be resized, use the following:
root.resizable(False, False)

python tkinter image layers (paste / unpaste image on background)

i have a background image using tkinter canvas,
and i'm adding images on top of it.
so far so good it works well. but what i would like to do is to be able to remove some of the forground images on demand. and when i remove some of them i would like to see the background behind them as it were before adding those forground images on it.
that would be like: paste 5 foreground images and then remove 1 or 2 of them.
so this program i have to far, adds little white filled circles at random position.
if i keep a handle on every little white circles (i can put them in variables and have them all in a list, and get their coordinates later for example). how can i remove some of them and get to see my background behind the removed whites circles ?
is it even possible ?
#!/usr/bin/env python3
from tkinter import *
from PIL import Image, ImageTk
from random import *
class App(object):
def __init__(self):
self.root = Tk()
self.canvas = Canvas(self.root, height=222, width=227)
self.canvas.grid()
# small nature landscape
self.backgnd = PhotoImage( file = "images/nature.png" )
# small white circle
self.mycloud = PhotoImage( file = "images/white.png" )
backgnd_width = (self.backgnd.width()/2)
backgnd_height = (self.backgnd.height()/2)
self.canvas.create_image(backgnd_width,backgnd_height,image=self.backgnd)
def cloud(self):
pos_x = randint(1,220)
pos_y = randint(1,220)
self.canvas.create_image(pos_x,pos_y, image=self.mycloud)
app = App()
app.cloud()
app.cloud()
app.cloud()
app.cloud()
app.cloud()
app.root.mainloop()
in case it might help others here's a working solution.
i added a button that will remove each object placed on the canvas, one a a time.
(thanks for the help, Bryan Oakley)
#!/usr/bin/env python3
from tkinter import *
from PIL import Image, ImageTk
from tkinter import ttk
from random import *
class App(object):
def __init__(self):
self.root = Tk()
self.canvas = Canvas(self.root, height=300, width=227)
self.canvas.grid()
self.mylist=[]
self.backgnd = PhotoImage( file = "images/nature.png" )
self.mycloud = PhotoImage( file = "images/white.png" )
backgnd_width = (self.backgnd.width()/2)
backgnd_height = (self.backgnd.height()/2)
self.canvas.create_image(backgnd_width,backgnd_height,image=self.backgnd)
# button to remove things on the canvas
button_del = ttk.Button(self.root, text='Del')
button_del['command'] = self.rem
button_del.place(x=100, y=250)
def cloud(self):
# add 5 object at random position on the canvas
for idx in range(5):
pos_x = randint(1,220)
pos_y = randint(1,220)
self.mylist.append(self.canvas.create_image(pos_x,pos_y, image=self.mycloud))
def rem(self):
# delete elements placed on the canvas
self.canvas.delete(self.mylist[-1])
self.mylist.pop()
app = App()
app.cloud()
app.root.mainloop()
made a few changes to make above code compatible with python 2:
from Tkinter import *
from PIL import Image, ImageTk
import ttk
from random import *
class App(object):
def __init__(self):
self.root = Tk()
self.canvas = Canvas(self.root, height=300, width=227)
self.canvas.grid()
self.mylist=[]
self.backgnd = ImageTk.PhotoImage( Image.open("sunshine.jpg") )
self.mycloud = ImageTk.PhotoImage( Image.open("Y.png") )
backgnd_width = (self.backgnd.width()/2)
backgnd_height = (self.backgnd.height()/2)
self.canvas.create_image(backgnd_width,backgnd_height,image=self.backgnd)
# button to remove things on the canvas
button_del = ttk.Button(self.root, text='Del')
button_del['command'] = self.rem
button_del.place(x=100, y=250)
def cloud(self):
# add 5 object at random position on the canvas
for idx in range(5):
pos_x = randint(1,220)
pos_y = randint(1,220)
self.mylist.append(self.canvas.create_image(pos_x,pos_y, image=self.mycloud))
def rem(self):
# delete elements placed on the canvas
self.canvas.delete(self.mylist[-1])
self.mylist.pop()
app = App()
app.cloud()
app.root.mainloop()

Tkinter Filenames with StringVar

I have a problem setting a filename in a tkinter environment (python 3.2 on a raspberry). To specify what I mean, I will use my code:
from tkinter import Tk, Canvas, StringVar
from PIL import ImageTk, Image
from threading import Thread
class proc(Thread):
def __init__(self):
Thread.__init__(self)
def run(self):
self.root=tkinter.Tk()
self.labelstring = StringVar()
self.labelstring.set('Foo')
self.path = StringVar()
self.path.set('cold.jpg')
canvas = Canvas(self.root, width=888, height=600)
canvas.pack()
im = Image.open(self.path) #<-- does not work
canvas.image = ImageTk.PhotoImage(im)
canvas.create_image(0, 0, image=canvas.image, anchor='nw')
label = tkinter.Label(self.root,textvariable=self.labelstring)
label.pack()
self.root.mainloop()
app = proc()
app.start()
for i in range(0, 10):
time.sleep(5)
proc.labelstring.set(i)
The part where I change the label labelstring.set(i) works fine, but what does not work is sending a filename via path.set('image.jpg'). I konw, the filetype is not a path this way, it is a tkinter.StringVar Object... I did not find a good way to make it a path variable.
At the end of the day
im = Image.open(self.path)
canvas.image = ImageTk.PhotoImage(im)
canvas.create_image(0, 0, image=canvas.image, anchor='nw')
cannot be called with previously define self.path.set('image.jpg'). I want to maybe have a list of xy pics and do path.set(piclist[i]) to change the image in the tkinter.canvas.
I dont know what you want to achieve, and why use Threads here. Your code has some inconsistencies, missing import statements, etc. Thus, I simplified it so that I can run it and just concentrate on the line you had indicated. The simplified version is:
from tkinter import Tk, Canvas, StringVar, Label
from PIL import ImageTk, Image
from threading import Thread
class proc():
def __init__(self):
pass
# Thread.__init__(self)
def run(self):
self.root=Tk()
self.labelstring = StringVar()
self.labelstring.set('Foo')
self.path = StringVar()
self.path.set('empty.gif')
canvas = Canvas(self.root, width=888, height=600)
canvas.pack()
im = Image.open(self.path.get()) #<-- does not work
canvas.image = ImageTk.PhotoImage(im)
canvas.create_image(0, 0, image=canvas.image, anchor='nw')
label = Label(self.root,textvariable=self.labelstring)
label.pack()
self.root.mainloop()
app = proc()
app.run()
Concentrating on the line which does not work, in your example you have:
im = Image.open(self.path)
But you should be getting the file's path as follows (as in my example):
im = Image.open(self.path.get())

Resources