Can't run an infinite thread with Tkinter - python

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)

Related

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

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.

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

How to set the speed of a gif in Python

i am using Python and I inserted a gif in my project. The issue is when i start the application the gif in the application runs in different way than orginal gif. I think that the frames in the gif are run in different speed. How to set the original gif?
I imported PIL and tkinter. This is the code:
import threading
from tkinter import *
from PIL import Image, ImageTk, ImageSequence
def play_gif():
global img
img = Image.open("Gifs/beaming_face_with_smiling_eyes.gif")
lbl = Label(root)
lbl.place(x = 0, y = 0)
for img in ImageSequence.Iterator(img):
img = ImageTk.PhotoImage(img)
lbl.config(image = img)
root.update()
def exit():
root.destroy()
threading.Timer(3.0, play_gif).start()
Button(root,text = "exit", command=exit).place(x = 450, y = 300)
root.mainloop()

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

Python tkinter displaying images as movie stream

I am trying to screen-grab and display the image quickly like a recording. It seems to all function well except the display window is "blinking" occasionally with a white frame. It doesn't appear to be every update or every other frame, but rather every 5 or so. Any thoughts on the cause?
from tkinter import *
from PIL import Image, ImageGrab, ImageTk
import threading
from collections import deque
from io import BytesIO
class buildFrame:
def __init__(self):
self.root = Tk()
self.land = Canvas(self.root, width=800, height=600)
self.land.pack()
self.genObj()
self.thsObj = self.land.create_image(0,0, anchor='nw', image=self.imgObj)
self.sStream = deque()
self.spinning = True
prQ = threading.Thread(target=self.procQ)
prQ.start()
t1 = threading.Thread(target=self.snapS, args=[100])
t1.start()
def genObj(self):
tmp = Image.new('RGBA', (800, 600), color=(0, 0, 0))
self.imgObj = ImageTk.PhotoImage(image=tmp)
def procQ(self):
while self.spinning == True:
if self.sStream:
self.land.itemconfig(self.thsObj, image=self.sStream[0])
self.sStream.popleft()
def snapS(self, shtCount):
quality_val = 70
for i in range(shtCount):
mem_file = BytesIO()
ImageGrab.grab().save(mem_file, format="JPEG", quality=quality_val)
mem_file.seek(0)
tmp = Image.open(mem_file)
tmp.thumbnail([800, 600])
img = ImageTk.PhotoImage(tmp)
self.sStream.append(img)
mem_file.close()
world = buildFrame()
world.root.mainloop()
You should avoid making Tk calls on non GUI threads. This works much more smoothly if you get rid of the threads entirely and use after to schedule the image capture.
from tkinter import *
from PIL import Image, ImageGrab, ImageTk
from io import BytesIO
class buildFrame:
def __init__(self):
self.root = Tk()
self.land = Canvas(self.root, width=800, height=600)
self.land.pack()
tmp = Image.new('RGBA', (800, 600), color=(0, 0, 0))
self.imgObj = ImageTk.PhotoImage(image=tmp)
self.thsObj = self.land.create_image(0,0, anchor='nw', image=self.imgObj)
self.root.after("idle", self.snapS)
def snapS(self):
quality_val = 70
mem_file = BytesIO()
ImageGrab.grab().save(mem_file, format="JPEG", quality=quality_val)
mem_file.seek(0)
tmp = Image.open(mem_file)
tmp.thumbnail([800, 600])
self.image = ImageTk.PhotoImage(tmp)
self.land.itemconfig(self.thsObj, image=self.image)
mem_file.close()
self.root.after(10, self.snapS)
world = buildFrame()
world.root.mainloop()
If you really want to use threads, you should queue the image stream and have the UI thread deserialize the tkinter image from the stream and display it. So one thread for capture and the main thread doing display.
EDIT
The following version keeps using a thread for the capture and passes the data via the deque but ensures that only the Tk UI thread operates on Tk objects. This needs some work to avoid accumulating images in the queue but a delay of 100ms between images works fine for now.
from tkinter import *
from PIL import Image, ImageGrab, ImageTk
import sys, threading, time
from collections import deque
from io import BytesIO
class buildFrame:
def __init__(self):
self.root = Tk()
self.root.wm_protocol("WM_DELETE_WINDOW", self.on_destroy)
self.land = Canvas(self.root, width=800, height=600)
self.land.pack()
self.genObj()
self.thsObj = self.land.create_image(0,0, anchor='nw', image=self.imgObj)
self.sStream = deque()
self.image_ready = threading.Event()
self.spinning = True
self.prQ = threading.Thread(target=self.procQ)
self.prQ.start()
self.t1 = threading.Thread(target=self.snapS, args=[100])
self.t1.start()
def on_destroy(self):
self.spinning = False
self.root.after_cancel(self.afterid)
self.prQ.join()
self.t1.join()
self.root.destroy()
def genObj(self):
tmp = Image.new('RGBA', (800, 600), color=(0, 0, 0))
self.imgObj = ImageTk.PhotoImage(image=tmp)
def procQ(self):
while self.spinning == True:
if self.image_ready.wait(0.1):
print(len(self.sStream))
self.image_ready.clear()
self.afterid = self.root.after(1, self.show_image)
def show_image(self):
stream = self.sStream[0]
self.sStream.popleft()
tmp = Image.open(stream)
tmp.thumbnail([800, 600])
self.image = ImageTk.PhotoImage(tmp)
self.land.itemconfig(self.thsObj, image=self.image)
stream.close()
def snapS(self, shtCount):
quality_val = 70
while self.spinning:
mem_file = BytesIO()
ImageGrab.grab().save(mem_file, format="JPEG", quality=quality_val)
mem_file.seek(0)
self.sStream.append(mem_file)
self.image_ready.set()
time.sleep(0.1)
def main():
world = buildFrame()
world.root.mainloop()
return 0
if __name__ == '__main__':
sys.exit(main())

Categories

Resources