python tkinter image layers (paste / unpaste image on background) - python-3.x

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()

Related

button to take frame screenshot

can someone help me take a screnshot of a specific frame?
been playing with these, but cant seem to specify just the frame
import pyautogui
#im1 = pyautogui.screenshot()
#im1.save('my_screenshot.png')
#im2 = pyautogui.screenshot('my_screenshot2.png')
from tkinter import *
import time
from PIL import ImageTk, Image
import pyautogui as pg
# Create an instance of tkinter frame or window
win = Tk()
# Set the size of the window
win.geometry("700x350")
frm2shoot = Frame(win)
frm2shoot.grid(column=0, row=0)
lbl = Label(frm2shoot, width=16, text="testing testing:", justify=LEFT, anchor="w").grid(row=0, column=0, sticky=W, pady=2)
# Define a function for taking screenshot
def screenshot():
random = int(time.time())
filename = "/Users/ricardosimoes/Desktop/"+ str(random) + ".jpg"
ss = pg.screenshot(filename)
ss.show()
frm2shoot.deiconify()
# Create a Button to take the screenshots
button = Button(win, text="Take Screenshot", font=('Aerial 11 bold'), background="#aa7bb1", foreground="white", command=screenshot)
button.grid(column=5, row=0)
win.mainloop()
Anyone have any idea how to do this??

Flickering video for tkinter video

I am trying to make a simple play/pause application in tkinter. Basically I want to show a video and have a play/pause button underneath.
So, after some research I found this suitable post to show a video using tkinter and opencv:
to show video streaming inside frame in tkinter
When using the code, given in the accepted answer to show a video, there is no problem and I don't see any flickering. Here is the code:
# import the necessary packages
from __future__ import print_function
import tkinter as tk
from PIL import ImageTk, Image
import cv2
root = tk.Tk()
# Create a frame
app = tk.Frame(root, bg="white")
app.grid()
# Create a label in the frame
lmain = tk.Label(app)
lmain.grid()
# Capture from camera
cap = cv2.VideoCapture(r'PATH_TO_VIDEO_FILE')
# function for video streaming
frame_number = 0
def video_stream():
global frame_number
cap.set(cv2.CAP_PROP_POS_FRAMES, frame_number)
success, frame = cap.read()
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image)
imgtk = ImageTk.PhotoImage(image=img)
lmain.imgtk = imgtk
lmain.configure(image=imgtk)
lmain.after(1, video_stream)
frame_number += 1
video_stream()
root.mainloop()
Now, I slightly altered the code to be able to use the grid manager and add a play button:
# import the necessary packages
from __future__ import print_function
import tkinter as tk
from PIL import ImageTk, Image
import cv2
class PhotoBoothApp:
def __init__(self, path_to_video):
# initialize the root window
self.window = tk.Tk()
self.window.title("Video_Player")
self.videocap = cv2.VideoCapture(path_to_video)
self.frame_number = 0
# Initalize
self.videocap.set(cv2.CAP_PROP_POS_FRAMES, self.frame_number)
success, self.frame = self.videocap.read()
cv2image = cv2.cvtColor(self.frame, cv2.COLOR_BGR2RGBA)
self.img = Image.fromarray(cv2image)
self.imgtk = ImageTk.PhotoImage(image=self.img)
# Show frames
self.picture_label = tk.Label(self.window, image=self.imgtk, relief=tk.RIDGE).grid(row=0, column=0)
self.btn_next_image=tk.Button(self.window, text="Play", width=50, bg ="green",command=self.video_stream).grid(row=1,column=0)
self.window.mainloop()
def video_stream(self):
self.videocap.set(cv2.CAP_PROP_POS_FRAMES, self.frame_number)
sucess, frame = self.videocap.read()
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image)
self.imgtk = ImageTk.PhotoImage(image=img)
self.picture_label = tk.Label(self.window, image=self.imgtk, relief=tk.RIDGE).grid(row=0, column=0)
# Update Frame Number to display
self.frame_number = self.frame_number + 1
self.window.after(1, self.video_stream)
ph = PhotoBoothApp(r'PATH_TO_FILE')
The problem is that when I execute the above code, the video flickers as if tkinter need to reload something in-between frames. I have no clue why this happens.
P.S. This post here Flickering video in opencv-tkinter integration did not help me.
You need to make two changes: split your self.picture_label line to create a proper reference to your Label object, and then use self.picure_label.config(...) to change the image.
class PhotoBoothApp:
def __init__(self, path_to_video):
# initialize the root window
...
self.picture_label = tk.Label(self.window, image=self.imgtk, relief=tk.RIDGE)
self.picture_label.grid(row=0, column=0)
...
def video_stream(self):
...
img = Image.fromarray(cv2image)
self.imgtk = ImageTk.PhotoImage(image=img)
self.picture_label.config(image=self.imgtk)
# Update Frame Number to display
self.frame_number = self.frame_number + 1
self.window.after(1, self.video_stream)
ph = PhotoBoothApp(r'PATH_TO_FILE')

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)

how can I fit the widgets to the entire frame to enable resizing in tkinter?

I have been able to resize the GUI, it stretches left right, and the content resizes the widgets, but there seems to be a cut off point which results in just the GUI widgets resizing, how can I make the widgets of the frame resize over the whole frame...
from tkinter import *
from tkinter import ttk
class MainWindow:
def __init__(self,master):
self.master = master
self.master.geometry('300x300')
self.container = Frame(self.master)
self.container.grid(row=0,column=0,sticky='NSEW')
self.container['bg']='skyblue'
self.chip_frame = Frame(self.container)#,height=100,width=100)
self.random = Frame(self.container)#,height=100,width=100)
self.fish_frame = Frame(self.container)#,height=100,width=100)
self.cat_frame = Frame(self.container)#,height=100,width=100)
self.cow_frame = Frame(self.container)#,height=100,width=100)
self.brad_frame = Frame(self.container)#,height=100,width=100)
self.chip_frame.grid(row=0,column=0,sticky='NSEW')
self.random.grid(row=0,column=1,sticky='NSEW')
self.fish_frame.grid(row=0,column=2,sticky='NSEW')
self.cat_frame.grid(row=1,column=0,sticky='NSEW')
self.cow_frame.grid(row=1,column=2,sticky='NSEW')
self.brad_frame.grid(row=0,column=3,sticky='NSEW',rowspan=2)
self.fish_frame['bg']='orange'
self.chip_frame['bg']='pink'
self.random['bg']='black'
self.cat_frame['bg']='Blue'
self.cow_frame['bg']='Green'
self.brad_frame['bg']='Red'
for i in range(4):
self.container.columnconfigure(i,weight=1)
for i in range(4):
self.container.rowconfigure(i,weight=1)
root = Tk()
root.title('Nathans APP')
root.columnconfigure(0,weight=1)
root.rowconfigure(0,weight=1)
app = MainWindow(root)
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