How to get circles to appear over the video in canvas tkinter? - python

I have the below code:
import tkinter as tk, threading
from tkinter import *
import imageio
from PIL import Image, ImageTk
from random import *
video_name = "video.mp4" #This is your video file path
video = imageio.get_reader(video_name)
def stream(label):
for image in video.iter_data():
frame_image = ImageTk.PhotoImage(Image.fromarray(image))
label.config(image=frame_image)
label.image = frame_image
def circle():
global circ
x = randint(0, 299)
y = randint(0, 299)
diameter = randint(10, 100)
circ = canvas.create_oval(x, y, x + diameter, y + diameter, tags="circle")
canvas.tag_raise(circ)
if __name__ == "__main__":
root = tk.Tk()
canvas = Canvas(root, bg="green")
canvas.pack(expand=True, fill=BOTH)
my_label = tk.Label(canvas)
my_label.pack()
b = Button(canvas, text="Circle", command=circle)
b.pack()
thread = threading.Thread(target=stream, args=(my_label,))
thread.daemon = 1
thread.start()
root.mainloop()
It works fine, and the circles appear, but they go behind the video playing. How can I make the circles appear on top of the video?
Thanks!

You will need to use a text item on the canvas rather than a label. The canvas does not allow you to draw on top of widgets embedded in or on the canvas.

Related

How to make my variable update based on cursor position?

I tried to build off of the solution here. My code is:
from tkinter import mainloop, Tk, Frame, Button, Label, Canvas, PhotoImage, NW
from tkinter import ttk
from tkinter import filedialog
import tkinter as tk
from PIL import Image, ImageTk
class my_class(tk.Tk):
def __init__(self):
super().__init__()
self.geometry=('1400x1400')
self.filename = ''
my_notebook = ttk.Notebook(self)
my_notebook.pack(pady=5)
self.selections = Frame(my_notebook, width = 1100, height = 700)
self.selections.pack(fill = "both", expand=1)
my_notebook.add(self.selections, text = "Selections")
Button(self.selections, text = "Select an Image", command = self.get_image).place(x=10,y=40)
self.image_frame = Frame(my_notebook, width = 1100, height = 700)
self.image_frame.pack(fill = "both", expand=1)
my_notebook.add(self.image_frame, text = "Image")
self.my_canvas = Canvas(self.image_frame, width=800, height=600, bg="white")
self.my_canvas.pack()
self.rgb_var = tk.StringVar(self.image_frame, '0 0 0')
self.rgb_label = tk.Label(self.image_frame, textvariable = self.rgb_var)
self.rgb_label.pack()
self.image_frame.bind('<Motion>', lambda e: self.get_rgb(e))
def get_image(self):
self.filename = filedialog.askopenfilename(initialdir="D:/Python", title="select a file", filetypes = (("png files","*.png"),("jpg files","*.jpg")))
self.img = Image.open(self.filename)
self.img_rgb = self.img.convert('RGB')
dim_x, dim_y = self.img_rgb.size
self.img_tk = ImageTk.PhotoImage(self.img_rgb.resize((dim_x, dim_y)))
self.my_canvas.create_image(dim_x // 2, dim_y // 2, image = self.img_tk)
def get_rgb(self, event):
x, y = event.x, event.y
try:
rgb = self.img_rgb.getpixel((x, y))
self.rgb_var.set(rgb)
except IndexError:
pass # ignore errors if cursor is outside the image
if __name__ == '__main__':
app = my_class()
app.geometry=('1200x900')
app.mainloop()
I can use the button to select an image. Then I click the (Image) tab and see the selected image on the canvas.
I expected the (rgb_var) displayed under the image to update as I move the mouse pointer across the image. Instead the numbers under the image only update when the mouse pointer is in the frame, but outside the canvas. Also the numbers displayed seem to be unrelated to pixels in the image. How can I display the RGB values of a pixel that is (under the mouse pointer) when the mouse pointer is over the image?

How do I delete the Label I click on?

I have been working on a project where I use labels that I want to disappear on click, but it only deletes the last label that was created. Here's my code:
from tkinter import *
import tkinter
import random
from PIL import Image, ImageTk
from functools import partial
width1=1280
height1=720
canvas = tkinter.Canvas(width=width1,height=height1, bg = 'white')
canvas.pack()
def clicked(*args):
label.destroy()
def square():
global label
global img
sq_time = random.randrange(4000,6000)
x = random.randrange(100,width1-40,40)
y = random.randrange(40,height1-40,40)
label = Label(canvas, image = img)
label.place(x = x , y = y)
label.bind("<Button-1>",partial(clicked))
canvas.after(sq_time, square)
img = ImageTk.PhotoImage(Image.open('froggy.png'))
square()
mainloop()
froggy.png is a image that I have saved in the same folder as the code. Can someone tell me how do I delete the label that was clicked?
In tkinter event handler functions are automatically passed an event object argument that, among other things, has an attribute that identifies the widget that triggered them. This means you can use that instead of creating a partial to get the information needed.
from tkinter import *
import tkinter
import random
from PIL import Image, ImageTk
width1 = 1280
height1 = 720
canvas = tkinter.Canvas(width=width1, height=height1, bg='white')
canvas.pack()
def clicked(event):
event.widget.destroy()
def square():
global label
global img
sq_time = random.randrange(4000, 6000)
x = random.randrange(100, width1-40, 40)
y = random.randrange(40, height1-40, 40)
label = Label(canvas, image = img)
label.place(x=x, y=y)
label.bind("<Button-1>", clicked)
canvas.after(sq_time, square)
img = ImageTk.PhotoImage(Image.open('froggy.png'))
square()
mainloop()
def on_click():
label.after(1000, label.destroy)
Button(win, text="Delete", command=on_click).pack()

I need help displaying full screen images with tkinter

I am doing a people counter in raspberry pi. I want to display an one image if someone comes in, and another one if someone comes out. Right now i am using the code below (that i took from another question here xd) to change the image that tkinter is displaying. The problem with this is thay it only shows the picture cat.jpg for a second, and then it shows a black screen and nothing happends.
import sys
if sys.version_info[0] == 2: # the tkinter library changed it's name from Python 2 to 3.
import Tkinter
tkinter = Tkinter #I decided to use a library reference to avoid potential naming conflicts with people's programs.
else:
import tkinter
from PIL import Image, ImageTk
import time
def updateRoot(root,imagen):
pilImage = Image.open(imagen)
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
root.overrideredirect(1)
root.geometry("%dx%d+0+0" % (w, h))
root.focus_set()
root.bind("<Escape>", lambda e: (e.widget.withdraw(), e.widget.quit()))
canvas = tkinter.Canvas(root,width=w,height=h)
canvas.pack()
canvas.configure(background='black')
imgWidth, imgHeight = pilImage.size
if imgWidth > w or imgHeight > h:
ratio = min(w/imgWidth, h/imgHeight)
imgWidth = int(imgWidth*ratio)
imgHeight = int(imgHeight*ratio)
pilImage = pilImage.resize((imgWidth,imgHeight), Image.ANTIALIAS)
image = ImageTk.PhotoImage(pilImage)
imagesprite = canvas.create_image(w/2,h/2,image=image)
root.update()
root = tkinter.Tk()
updateRoot(root,"Cat.jpg")
time.timesleep(5)
updateRoot(root,"Dog.jpg")
Before this I used this code
import tkinter
from PIL import Image, ImageTk
from tkinter import ttk
def updateRoot(root,imagen):
image1 = Image.open(imagen)
image2 = ImageTk. PhotoImage(image1)
image_label = ttk. Label(root , image =image2)
image_label.place(x = 0 , y = 0)
root.update()
That works fine, but it's not full screen.
First you should do the followings outside updateRoot():
make root window fullscreen (you can simply use root.attributes('-fullscreen', 1))
bind the <Escape> key
create the canvas and create_image() (you can use Label to do the same thing)
Then just update the image inside updateRoot().
Also you should use after() instead of time.sleep().
Below is an example:
try:
import Tkinter as tkinter
except:
import tkinter
from PIL import Image, ImageTk
def updateRoot(imagen):
# resize the image to fill the whole screen
pilImage = Image.open(imagen)
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
image = ImageTk.PhotoImage(pilImage.resize((w,h)))
# update the image
canvas.itemconfig(imgbox, image=image)
# need to keep a reference of the image, otherwise it will be garbage collected
canvas.image = image
root = tkinter.Tk()
root.attributes('-fullscreen', 1)
root.bind('<Escape>', lambda _: root.destroy())
canvas = tkinter.Canvas(root, highlightthickness=0)
canvas.pack(fill=tkinter.BOTH, expand=1)
imgbox = canvas.create_image(0, 0, image=None, anchor='nw')
# show the first image
updateRoot('Cat.jpg')
# change the image 5 seconds later
root.after(5000, updateRoot, 'Dog.jpg')
root.mainloop()
Fixed your Black issue using labels, try this. i think you still need to resize image to fit screen
import sys
if sys.version_info[0] == 2: # the tkinter library changed it's name from Python 2 to 3.
import Tkinter
tkinter = Tkinter #I decided to use a library reference to avoid potential naming conflicts with people's programs.
else:
import tkinter
from PIL import Image, ImageTk
import time
from tkinter import *
import PIL.Image
def updateRoot(root,imagen):
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
root.overrideredirect(1)
root.geometry("%dx%d+0+0" % (w, h))
root.focus_set()
root.bind("<Escape>", lambda e: (e.widget.withdraw(), e.widget.quit()))
img = PIL.Image.open(imagen)
root.tkimage = ImageTk.PhotoImage(img)
Label(root,image = root.tkimage).place(x=0, y=0, relwidth=1, relheight=1)
root.update()
root = tkinter.Tk()
updateRoot(root,"Cat.jpg")
time.sleep(3)
updateRoot(root,"Dog.jpg")
root.mainloop()

Can't run an infinite thread with Tkinter

The code below works, but if i make the thread an infinite loop (by uncommenting the line i=False) the window does'nt show anymore. What Am I missing?
My goal is to keep updating the image while it's shown.
from PIL import Image,ImageTk
from tkinter import Tk,Canvas,NW,mainloop
import threading
from time import sleep
from random import randint
imgx = 512; imgy = 512
def mi_thread():
global pix
i=True
while i:
#i=False
for k in range(imgy):
pix[k,k]=(randint(0,255),randint(0,255),randint(0,255))
sleep(1)
window = Tk()
canvas = Canvas(window, width = imgx, height = imgy, bg = "#000000");canvas.pack()
img2 = Image.new("RGB", (imgx, imgy))
pix = img2.load()
t= threading.Thread(target=mi_thread())
t.start()
imgx =ImageTk.PhotoImage(img2)
canvas.create_image((0, 0), image = imgx, state = "normal", anchor = NW)
mainloop()
In threading.Thread(target=mi_thread()) you are actually running your function. You need to remove the parentheses and pass the function reference only. ie:
threading.Thread(target = mi_thread)

Tkinter resize background image to window size

Trying to set up a background for my tkinter window. I have a square background image, which fades to black around the edges, and then the main window has a black background. The image is placed over the background, and if the window is wider than it is tall, the image centers itself in the middle over the black background, and it all looks very nice.
However when the window is smaller than the image in width and height, it puts the center of the image in the center of the window, so you don't see the whole image, and it looks a little odd. Is there a way of resizing the image so that if the largest of the width and height of the window is smaller than the image, the image is adjusted to that size, keeping aspect ratio.
So say the background image is 600x600:
In a 800x400 window, the image does not resize, and centers itself vertically.
In a 500x400 window, the image resizes to 500x500, and still centers itself vertically.
In a 400x900 window, the image does not resize, and centers itself horizontally.
The centering functionality is already there, I just need the resize functionality.
Currently what I have is:
from tkinter import *
root = Tk()
root.title("Title")
root.geometry("600x600")
root.configure(background="black")
background_image = PhotoImage(file="Background.gif")
background = Label(root, image=background_image, bd=0)
background.pack()
root.mainloop()
Not sure if there is a way of doing this in tkinter? Or if perhaps I would write my own function that resizes the image according to the window size, however the image needs to resize relatively smoothly and quickly if the user resizes the window at any point.
This is example application that uses Pillow to resize image on the Label as the label changes size:
from tkinter import *
from PIL import Image, ImageTk
root = Tk()
root.title("Title")
root.geometry("600x600")
root.configure(background="black")
class Example(Frame):
def __init__(self, master, *pargs):
Frame.__init__(self, master, *pargs)
self.image = Image.open("./resource/Background.gif")
self.img_copy= self.image.copy()
self.background_image = ImageTk.PhotoImage(self.image)
self.background = Label(self, image=self.background_image)
self.background.pack(fill=BOTH, expand=YES)
self.background.bind('<Configure>', self._resize_image)
def _resize_image(self,event):
new_width = event.width
new_height = event.height
self.image = self.img_copy.resize((new_width, new_height))
self.background_image = ImageTk.PhotoImage(self.image)
self.background.configure(image = self.background_image)
e = Example(root)
e.pack(fill=BOTH, expand=YES)
root.mainloop()
This is how it works using Lenna image as example:
I have modified the above code so it is not in a class
#!/usr/bin/python3.5
from tkinter import *
from tkinter import ttk
from PIL import Image, ImageTk
root = Tk()
root.title("Title")
root.geometry('600x600')
def resize_image(event):
new_width = event.width
new_height = event.height
image = copy_of_image.resize((new_width, new_height))
photo = ImageTk.PhotoImage(image)
label.config(image = photo)
label.image = photo #avoid garbage collection
image = Image.open('image.gif')
copy_of_image = image.copy()
photo = ImageTk.PhotoImage(image)
label = ttk.Label(root, image = photo)
label.bind('<Configure>', resize_image)
label.pack(fill=BOTH, expand = YES)
root.mainloop()
Just sugesting a slight change in the answer. Using self.master.winfo_width(),self.master.winfo_height() instead of 'event' makes he adjustment to size much quicker.
import tkinter as tk
from PIL import Image, ImageTk
class Layout:
def __init__(self,master):
self.master = master
self.rootgeometry()
self.canvas = tk.Canvas(self.master)
self.canvas.pack()
self.background_image = Image.open('image_file.PNG')
self.image_copy = self.background_image.copy()
self.background = ImageTk.PhotoImage(self.background_image)
self.loadbackground()
def loadbackground(self):
self.label = tk.Label(self.canvas, image = self.background)
self.label.bind('<Configure>',self.resizeimage)
self.label.pack(fill='both', expand='yes')
def rootgeometry(self):
x=int(self.master.winfo_screenwidth()*0.7)
y=int(self.master.winfo_screenheight()*0.7)
z = str(x) +'x'+str(y)
self.master.geometry(z)
def resizeimage(self,event):
image = self.image_copy.resize((self.master.winfo_width(),self.master.winfo_height()))
self.image1 = ImageTk.PhotoImage(image)
self.label.config(image = self.image1)
root = tk.Tk()
a = Styling.Layout(root)
root.mainloop()
i have created function for calling resize a single time with methods after et after cancel
def on_resize(self, evt):
if self.inter == 0:
self.inter = 1
self.minuteur = self.fenetrePrincipale.after(100, self.endResize)
else:
self.minuteur = self.fenetrePrincipale.after_cancel(self.minuteur)
self.minuteur = self.fenetrePrincipale.after(100, self.endResize)
def endResize(self):
self.inter = 0
self.fenetrePrincipale.background = self.fenetrePrincipale.background.resize((self.fenetrePrincipale.winfo_width(), self.fenetrePrincipale.winfo_height()))
self.pixi = ImageTk.PhotoImage(self.fenetrePrincipale.background)
self.canvas.configure(width=self.fenetrePrincipale.winfo_width(), height=self.fenetrePrincipale.winfo_height())
self.canvas.create_image(0, 0, anchor=NW, image=self.pixi)
Here is the principle, after defines a timer and a function to be recalled at the end, after_cancel cleans the timer so each iteration of the function cleans the timer and starts it, at the last iteration of resize the timer remains triggered.
for more information on cancel and timer with after:
after detailled

Categories

Resources