for a personnal project I've been playing with pyvirtualcam,
and wanted a nice feedback with interactions, so I started implementing some tkinter in it. But as I tried to display the image on the canvas, I faced a wall...
My problem is that when printing that "image" which I forced to be a black square, it do not display at all (I am convinced about this because my canvas background is red). Any idea where this can come from?
Here is the code to reproduce the problem I have:
self.screen = tk.Tk()
self.canvas = tk.Canvas(self.screen, bg = "red")
self.canvas.pack(fill=tk.BOTH, expand=tk.YES, side=tk.TOP)
size = 20, 20, 3
output = np.zeros(size, dtype=np.uint8)
imageRGB = cv2.cvtColor(output, cv2.COLOR_BGRA2RGBA)
imageTk = ImageTk.PhotoImage(image = Image.fromarray(imageRGB))
self.canvas.create_image(100, 100, image = imageTk, anchor=tk.NW)
questions you might ask:
console is empty as if everything was fine (no error)
when printing the "output" or "imageRGB" variables it prints the matrix fine,
when printing the "imageTk" variable is prints a < TkImage > object,
For more info, be free to ask me,
Thanks for your help!
Related
I´m designing a slideshow with no user intervention, where:
a. Every image is generated by the Python script itself
b. There´s no file saving, for performance reasons
c. Every image is shown in fullscreen for a certain time
d. It´s a loop that´s supposed to never end. There´s always going to be an image to show
So far, by adapting code found in a few pages, I have it running. But every image is shown for X time and then the desktop background appears for a second or so.
I´d like to have a smooth switching from one file to next, such as FEH does. As a matter of fact, I´m trying to replace FEH because I need finer control of the display of each file (for instance, changing the time it appears on screen).
Here´s my code:
from PIL import Image
from PIL import ImageFont
from PIL import ImageDraw
from PIL import ImageTk
import tkinter
def show_center(pil_image, msDelay):
root = tkinter.Tk()
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
root.overrideredirect(1)
root.geometry("%dx%d+0+0" % (w, h))
root.focus_set()
root.attributes("-topmost", True)
canvas = tkinter.Canvas(root, width=w, height=h, highlightthickness=0)
canvas.pack()
canvas.configure(background='black')
image = ImageTk.PhotoImage(pil_image)
imagesprite = canvas.create_image(w / 2, h / 2, image=image)
root.after(msDelay, root.destroy)
root.mainloop()
### script body
while True:
# loads common background image
img = Image.open(baseImage)
draw = ImageDraw.Draw(img)
# here: image customization
draw.rectangle(....)
draw.text(....)
img.paste(....)
# shows this file
thisDelay = some Number Calculation
show_center(img, thisDelay)
Any ideas on how to avoid the desktop appearing between images? This will run in a headless Raspberry. I´m using Python3 on Raspbian.
Thanks in advance!
You can use after() instead of the while loop and simply use Label instead of Canvas:
import tkinter as tk
from PIL import Image, ImageTk, ImageDraw, ImageFont
import time
import random
def update_image():
# sample drawing
image = base_image.copy()
draw = ImageDraw.Draw(image)
draw.rectangle((100, 100, 500, 400), outline=random.choice(('red', 'green', 'blue', 'magenta', 'gold', 'orange')))
draw.text((120, 120), f"""{time.strftime("%F %T")}""")
# update image
tkimg = ImageTk.PhotoImage(image)
label.config(image=tkimg)
label.image = tkimg # save a reference to avoid garbage collected
ms_delay = random.randint(1000, 9000) # sample delay calculation
root.after(ms_delay, update_image)
root = tk.Tk()
root.attributes("-fullscreen", 1, "-topmost", 1)
base_image = Image.open("/path/to/base/image")
# label for showing image
label = tk.Label(root, bg="black")
label.pack(fill="both", expand=1)
update_image() # start the slide show
root.mainloop()
Well, it´s working quite well.
The solution required a bit of logic (maybe it makes sense not to destroy the object for every image) and a bit of good old trial & error.
The changes were:
Init the canvas only once, use global vars to make it persistent
For every image, call the display function and keep calling root.update() until the required timeout is reached
So, the prior function gets divided, and it looks like:
global canvas, root
global w, h
def init_image():
global canvas, root
global w, h
root = tkinter.Tk()
w, h = root.winfo_screenwidth(), root.winfo_screenheight()
root.overrideredirect(1)
root.geometry("%dx%d+0+0" % (w, h))
root.focus_set()
root.attributes("-topmost", True)
canvas = tkinter.Canvas(root, width=w, height=h, highlightthickness=0)
canvas.pack()
canvas.configure(background='black')
return
def show_center(pil_image, msDelay):
global canvas, root
global w, h
image = ImageTk.PhotoImage(pil_image)
imagesprite = canvas.create_image(w / 2, h / 2, image=image)
inicio = int(time.time() * 1000)
while 1:
root.update()
if (int(time.time() * 1000) - inicio) > msDelay:
break
return
Function init_image() is called once at beginning, and function show_center() is called for every image as the original post.
I hope this can be useful to anybody trying to accomplish the same.
[SOLVED]
Please don't judge the code. I just need to fix one bug with capturing canvas into file.
As you can see I've tried multiple solutions and still while saving file is all-white...
Do you have any soulutions to that?
This app is to generate randomly dots on canvas with option to save it.
it works when number of dots is above 40000 and rectangle is black.
But otherwise is white.
HEIGTH = 207
WIDTH = 207
def snapsaveCanvas():
fname = filename.get()
my_window.update_idletasks()
my_window.update()
canvas.update()
canvas.update_idletasks()
ps = canvas.postscript(colormode='color')
img = Image.open(io.BytesIO(ps.encode('utf-8')))
img.save(fname + ".bmp", 'bmp')
print("done")
canvas = tk.Canvas(frame2, width=HEIGTH, height=WIDTH, background='white')
canvas.grid(row=0, column=0)
canvas.create_rectangle(4, 4, 206, 206)
Ok, thanks for links.
I've tested several methods and one of them works.
taken from How can I convert canvas content to an image?
import win32gui
def snapsaveCanvas():
fileName = filename.get()
canvas.update()
canvas.update_idletasks()
HWND = canvas.winfo_id() # get the handle of the canvas
rect = win32gui.GetWindowRect(HWND) # get the coordinate of the canvas
im = ImageGrab.grab(rect).save(fileName + ".bmp")
print("done")
I am trying to simply get some image on canvas, but even though I don't get any errors, my canvas stays white, no picture shows up. I know it will be some stupid misteak, but I can't find it.
def obrazek():
mesic = tkinter.PhotoImage(file="moon-0.gif")
canvas.create_image(700, 500, image = mesic)
rodic = tkinter.Tk()
rodic.title(u"Slunce a Měsíc")
rodic.geometry("+1000+300")
canvas = tkinter.Canvas(rodic, width=1400, height=800,)
canvas.pack()
obrazek()
tkinter.mainloop()
Output is whitescreen.
Keep a reference to the image by returning from the function
Then call pack() after the function obrazek
I started to learn Python one month ago. Sorry if my question is not good, this is my first question here.
I have made a small game using tkinter, but I have a problem.
I made a big label with a picture on it as background. Whenever I make more labels with text on them, the text will have gray background. However what I want is for every text to have the picture that I already placed as background.
Here's some code to explain it:
from tkinter import*
x=Tk()
x.geometry("1000x1000")
z=PhotoImage(file="D:\\Blue.gif")
v=Label(x,text="hi",font=100,fg="red",compound=CENTER,image=z,width=1000,height=1000)
v.place(x=0,y=0)
v1=Label(x,text="OO",font=100,fg="red")
v1.place(x=300,y=400)
x.mainloop()
The v label works very well as long as I use compound with it. It shows the picture with the text "hi" on it.
However I want the v1 label to have the same background as v, instead of gray background.
All widgets have background - they can't be transparent.
You can use tk.Canvas to put text without background on image or transparent image on text.
effbot.org: Canvas, PhotoImage
#!/usr/bin/env python3
import tkinter as tk
from PIL import Image, ImageTk
# --- constants ---
WIDTH = 800
HEIGHT = 600
# --- main ---
root = tk.Tk()
c = tk.Canvas(root, width=WIDTH, height=HEIGHT)
c.pack()
# only GIF and PGM/PPM
#photo = tk.PhotoImage(file='test.gif')
# other formats
image = Image.open('test_transparent.png')
photo = ImageTk.PhotoImage(image)
# use in functions - solution for "garbage collector" problem
c.image = photo
i = c.create_image((WIDTH//2, HEIGHT//2), image=photo)
t = c.create_text((WIDTH//2, HEIGHT//2), text='Hello World')
root.mainloop()
Change order and you get image on text
t = c.create_text((WIDTH//2, HEIGHT//2), text='Hello World')
i = c.create_image((WIDTH//2, HEIGHT//2), image=photo)
test_transparent.png (image with transparent background)
My Problem:
As soon as I convert the input GIF Image into a RGBA to process it, I'm enjoying quality loss on the image. (You can see the both pictures below the code)
The code shown below "works" just I don't got a clue, why i loose quality.
Any suggestions??
My Code:
from PIL import ImageTk, Image as Im
from Tkinter import *
class App:
def __init__(self):
self.root = Tk()
self.root.geometry('1171x900')
self.maskPng = Im.open("Images/mask.gif")
self.maskPng = self.maskPng.convert("RGBA")
self.mask = ImageTk.PhotoImage(self.maskPng)
self.show = Canvas(self.root, width = 1170, height = 780)
self.show.pack()
self.show.create_image(0, 0, image = self.mask, anchor = NW)
self.speedBTN = Button(self.root, text = "hello", command = self.speed).pack(side = "right")
self.changed = False
def speed(self):
self.speedImg = Im.open("Images/speed/3_1.gif")
self.speedImg = self.speedImg.convert("RGBA")
self.maskPng.paste(self.speedImg, (0,0), self.speedImg)
self.maskPng.save("Images/speed/test.gif", "GIF")
self.render()
def render(self):
self.mask = ImageTk.PhotoImage(Im.open('Images/speed/test.gif'))
self.show.create_image(0,0, image = self.mask, anchor = NW)
self.show.image = self.mask
def draw(self):
self.root.mainloop()
main = App()
main.root.mainloop()
Images:
This is my mask.gif:
This is my 3_1.gif:
(What it white on stackoverflow is transparent in the gif)
This is the grainy, harrowing result:
(I marked the most "grainy" areas with red rectangles)
Thanks for your help! :)
Facing the same issue but I might have solution for your use case.
It doesn't seem like you actually do anything with the image so why don't you use the base64 encoded image and read it directly like below:
import Tkinter as tk # Python 2
import tkinter as tk # Python 3
# base64 encoded image data
image = ''
root = tk.Tk()
# Pass the base64 encoded data directly to your PhotoImage()
root.image = tk.PhotoImage(data=image)
# Display image in any way you need.
label = tk.Label(root, image=root.image, bg='white')
root.overrideredirect(True)
root.geometry("+250+250")
root.lift()
root.wm_attributes("-topmost", True)
root.wm_attributes("-disabled", True)
root.wm_attributes("-transparentcolor", "white")
label.pack()
label.mainloop()
The question is old so it's unlikely that you are still working on this but I hope it will help someone else.
The issue seems to be only with RGBA, RGB works just fine but it obviously doesn't have Alpha channel. The code above will render image in its original form.
My problem is slightly different but relates to this issue. I'm trying to manipulate pixels so I have to load it as RGBA, process it and save again.
Final Edit:
After filling the issue report on github I got reply from the author.
https://github.com/python-pillow/Pillow/issues/3059
The relevant part is :
It's quantization issue.
This gives the same result:
(Image.open('./37943912.gif')
.convert('RGBA')
.quantize()
.save('_out.png'))
This is now verified and should be considered a final answer.