[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")
Related
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!
I am preparing data for deep running. So I have to get certain pixel coordinates for each picture. Only one coordinate per photo is required.
So when I use PIL to input one click, I try to implement coordinates so that I can go to the next picture.
However, when I write the code as below, the coordinates are output in duplicate to only one picture, and the next picture does not appear on the screen.
How can I make sure that only one coordinate is recorded on a single picture?
from PIL import Image, ImageTk
import tkinter
import os
URL = './SavedImage/'
imgList = os.listdir(URL)
print(imgList)
width = 852
height = 480
stepW = 852/4
stepH = 480/5
def callback(event):
print("clicked at: ", event.x, event.y)
window = tkinter.Tk(className='pla')
for file in sorted(imgList):
a=True
image = Image.open(os.path.join(URL, file))
print(image)
canvas = tkinter.Canvas(window, width=image.size[0], height=image.size[1])
canvas.pack()
image_tk = ImageTk.PhotoImage(image)
canvas.create_image(image.size[0]//2, image.size[1]//2, image=image_tk)
canvas.bind("<Button-1>", callback)
tkinter.mainloop()
I am not 100% sure I understand what you need but it looks like to me you are trying to get one set of cord's for each image in a list of images.
I would do this by creating a function and a tracking variable to loop through each image on at a time and on click update a new list with the image and cord's then loop to next image.
Let me know if you have any questions.
Example:
from PIL import Image, ImageTk
import tkinter
import os
URL = './SavedImage/'
imgList = os.listdir(URL)
width = 852
height = 480
stepW = 852/4
stepH = 480/5
tracker = 0
list_images_with_cords = [] # added list for final results
def callback(event):
# Added global's.
global tracker,list_images_with_cords
# Used to append final results to list.
list_images_with_cords.append([imgList[tracker], event.x, event.y])
# This tracker lets us go through each item on the list.
tracker += 1
# After appending list go to next image.
open_next()
window = tkinter.Tk(className='pla')
# Creates just one canvas that we can update later.
canvas = tkinter.Canvas(window)
canvas.pack()
def open_next():
# Adding global's.
global image, canvas, image_tk, tracker
# Clearing canvas before drawing new image.
canvas.delete("all")
# Checking for valid index in list.
if tracker < len(imgList):
image = Image.open(os.path.join(URL, imgList[tracker]))
# use config() to update canvas.
canvas.config(width=image.size[0], height=image.size[1])
image_tk = ImageTk.PhotoImage(image)
canvas.create_image(image.size[0]//2, image.size[1]//2, image=image_tk)
canvas.bind("<Button-1>", callback)
else:
# This else statement is just for when we run out of images.
# It will display all the results in a textbox.
canvas.destroy()
txt = tkinter.Text(window, width=25)
txt.pack()
for item in list_images_with_cords:
txt.insert("end", "{}\n\n".format(item))
open_next()
tkinter.mainloop()
I'm looking for a way to move multiple images together with the background. Moving the background image works fine, but I can't figure out how to add two images on top (they disappear immediately) and then move together with the background. I guess there is an easy way to do that?
I appreciate any hint!
from tkinter import *
import time
tk = Tk()
w = 1000
h = 800
pos = 0
canvas = Canvas(tk, width=w, height=h)
canvas.pack()
tk.update()
background_image = PhotoImage(file="bg.gif")
background_label = Label(tk, image=background_image)
background_label.place(x=0, y=0)
tk.update()
def addImages(files):
for f in files:
image = PhotoImage(file=f)
label = Label(tk, image=image)
label.place(x=files[f][0],y=files[f][1])
tk.update()
def move(xPos):
pos = background_label.winfo_x() - xPos
while background_label.winfo_x() > pos:
background_label.place(x=background_label.winfo_x()-25)
tk.update()
time.sleep(0.001)
img = {"file1.gif": [10,10], "file2.gif": [50,50]}
addImages(img)
move(100)
tk.mainloop()
I'm having difficulty in understanding your code. Why create a canvas and then not use it? You have also littered your code with tk.update(), most of which are unnecessary. But, the described problem is because you create the labels inside a function and the association between label and image gets garbage collected when the function exits. You have to explicitly remember this association:
def addImages(files):
for f in files:
image = PhotoImage(file=f)
label = Label(tk, image=image)
label.image = image # Lets the label remember the image outside the function
label.place(x=files[f][0],y=files[f][1])
If you are then going to move these labels you might want to keep some kind of reference to them or you won't be able to address them.
Complete example
I changed tk to root because tk is the name usually used as alias for tkinter (eg. import tkinter as tk) which gets confusing.
I'm creating a image_list to hold references to the labels containing images. Later I use the list to loop through the labels and move them.
After I have built the GUI I wait 1000 milliseconds before starting the move function. Also I move the images just 1 pixel at a time to clearer see the action.
from tkinter import *
import time
root = Tk()
root.geometry('800x600') # Setting window size instead of usin canvas to do that
pos = 0
background_image = PhotoImage(file="bg.gif")
background_label = Label(root, image=background_image)
background_label.place(x=0, y=0)
image_list = [] # List for holding references to labels with images
def addImages(files):
for f in files:
image = PhotoImage(file=f)
label = Label(root, image=image)
label.image = image # Remember the image outside the function
label.place(x=files[f][0],y=files[f][1])
image_list.append(label) # Append created Label to the list
def move(xPos):
pos = background_label.winfo_x() - xPos
while background_label.winfo_x() > pos:
background_label.place(x=background_label.winfo_x()-1)
for image in image_list: # Loop over labels in list
image.place(x=image.winfo_x()-1) # Move the label
root.update()
time.sleep(0.001)
img = {"file1.gif": [10,10], "file2.gif": [50,50]}
addImages(img)
root.after(1000, move, 100) # Waits 1 second and then moves images
root.mainloop()
By the way; after is a function much preferred over sleep as sleep suspends the program until its finished, whereas after works by making a call after a time and the program runs meanwhile. But if you are ok with that the program freezes during the move, then why not.
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.
I am beginning to learn Python after being trapped using VB6 forever. To help myself learn some gui with Tkinter, I'm making a simple slot machine. To animate the wheels, I have a ~300x2000 image that has all of the pictures I intend to use displayed as a strip. About 6 pictures right now. I just show part of the image (wheel) at a time to make it spin. Anyway, I'm having trouble with the part of the code where I come to the end of the image and need to transition from the end of the image back to the beginning (wheel spins down, so the start is the bottom and end is the top).
For some reason, I can't get the images to crop and display correctly, even though I think my script makes sense logically. My code is posted below. You can make an image around the resolution of 300x2000 or so to use with this to see the problem I am having. My code starts printing outside of the area that I want it to show (outside the showsize variable) and I can't figure out why.
Any help with this problem would be much appreciated. It seems that the crops don't cut the images short enough, but all the information that I found about it make me think my script should be working just fine. I've tried to annotate my code to explain what's going on.
from Tkinter import *
from PIL import Image, ImageTk
def main():
root = Tk()
root.title = ("Slot Machine")
canvas = Canvas(root, width=1500, height=800)
canvas.pack()
im = Image.open("colors.png")
wheelw = im.size[0] #width of source image
wheelh = im.size[1] #height of source image
showsize = 400 #amount of source image to show at a time - part of 'wheel' you can see
speed = 3 #spin speed of wheel
bx1 = 250 #Box 1 x - where the box will appear on the canvas
by = 250 #box 1 y
numberofspins = 100 #spin a few times through before stopping
cycle_period = 0 #amount of pause between each frame
for spintimes in range(1,numberofspins):
for y in range(wheelh,showsize,-speed): #spin to end of image, from bottom to top
cropped = im.crop((0, y-showsize, wheelw, y)) #crop which part of wheel is seen
tk_im = ImageTk.PhotoImage(cropped)
canvas.create_image(bx1, by, image=tk_im) #display image
canvas.update() # This refreshes the drawing on the canvas.
canvas.after(cycle_period) # This makes execution pause
for y in range (speed,showsize,speed): #add 2nd image to make spin loop
cropped1 = im.crop((0, 0, wheelw, showsize-y)) #img crop 1
cropped2 = im.crop((0, wheelh - y, wheelw, wheelh)) #img crop 2
tk_im1 = tk_im2 = None
tk_im1 = ImageTk.PhotoImage(cropped1)
tk_im2 = ImageTk.PhotoImage(cropped2)
#canvas.delete(ALL)
canvas.create_image(bx1, by, image=tk_im2) ###THIS IS WHERE THE PROBLEM IS..
canvas.create_image(bx1, by + y, image=tk_im1) ###PROBLEM
#For some reason these 2 lines are overdrawing where they should be. as y increases, the cropped img size should decrease, but doesn't
canvas.update() # This refreshes the drawing on the canvas
canvas.after(cycle_period) # This makes execution pause
root.mainloop()
if __name__ == '__main__':
main()
It's simpler, and much faster and smoother if you don't recreate your images on every cycle. Here's my solution using canvas.move(). Notice that I moved the canvas.create_image calls outside of the for loop. I also put the code in a class and added a 'spin' button, and added something to make it exit without errors.
from Tkinter import *
from PIL import Image, ImageTk
import sys
class SlotMachine():
def __init__(self):
root = Tk()
root.title = ("Slot Machine")
self.canvas = Canvas(root, width=1200, height=800)
self.canvas.grid(column=0,row=0)
button = Button(root, text="Spin!", width=20, command = self.spin)
button.grid(column=0,row=1)
self.alive = True
root.protocol("WM_DELETE_WINDOW", self.die)
root.mainloop()
def spin(self):
im = Image.open("colors.png")
wheelw = im.size[0] #width of source image
wheelh = im.size[1] #height of source image
showsize = 400 # amount of source image to show at a time -
# part of 'wheel' you can see
speed = 3 #spin speed of wheel
bx1 = 250 #Box 1 x - where the box will appear on the canvas
by = 250 #box 1 y
numberofspins = 100 #spin a few times through before stopping
cycle_period = 3 #amount of pause between each frame
tk_im1 = ImageTk.PhotoImage(im)
tk_im2 = ImageTk.PhotoImage(im)
im1id = self.canvas.create_image(bx1, by + showsize, image=tk_im1)
im2id = self.canvas.create_image(bx1, by + showsize + wheelh,
image=tk_im2)
for spintimes in range(1,numberofspins):
for y in range(wheelh,0,-speed):
if self.alive:
self.canvas.move(im1id, 0, -speed)
self.canvas.move(im2id, 0, -speed)
self.canvas.update()
self.canvas.after(cycle_period)
else:
sys.exit()
self.canvas.move(im1id, 0, wheelh)
self.canvas.move(im2id, 0, wheelh)
def die(self):
self.alive = False
if __name__ == '__main__':
mySlotMachine = SlotMachine()
This will place parts of the wheel outside of its box, but you can just put your slot machine texture on top of that.