Fixed: Solution at the end.
I am trying to export/save a tkinter canvas by using the ImageGrab.grab() method. However the saved image is always off by a bunch of pixels. I tried many exemples as well as my own tries.
Question: Can you try to run this code and tell me if you get the whole visible canvas saved (make sure to change the uploaded image (line 17). How can I save the whole visible canva?
Here is a code exemple that you can try (note that here I tried the crop method). At the end of it you can see multiple exemples that I have tried.
from tkinter import *
from PIL import ImageTk, Image
import tkinter as tk
import pyscreenshot as ImageGrab
def exportCanvas(event=1): #try whatever you want here
print("exporting....") #to know that the function has started
ImageGrab.grab().crop((canvas.winfo_rootx(), canvas.winfo_rooty() ,canvas.winfo_rootx()+canvas.winfo_width(), canvas.winfo_rooty()+canvas.winfo_height())).save("testImageGrab.png")
print("... export ended.") #to know that the function has finished
#Create root and canvas
root = tk.Tk()
canvas = tk.Canvas(root, width=400, height=400, background="bisque")
canvas.pack(fill=BOTH, expand=1)
#Plot an image !!!!! Change text.png" to an image you have in your repertory !!!!!
imageTemp=Image.open("text.png")
schematicImage=ImageTk.PhotoImage(imageTemp)
imageAffichee=canvas.create_image(0,0,image=schematicImage)
#Bind q key to start the exporting/saving of the canvas
root.bind("<Key-q>",exportCanvas)
root.mainloop()
"""
Things that I have tried but doesn't get the right part of the screen
1: f.leno (https://stackoverflow.com/questions/47653748/performing-imagegrab-on-tkinter-screen)
box = (canvas.winfo_rootx(),canvas.winfo_rooty(),canvas.winfo_rootx()+canvas.winfo_width(),canvas.winfo_rooty() + canvas.winfo_height())
grab = ImageGrab.grab(bbox = box)
grab.save("testImageGrab.png")
2: B.Jenkins (https://stackoverflow.com/questions/9886274/how-can-i-convert-canvas-content-to-an-image)
x=root.winfo_rootx()+canvas.winfo_x()
y=root.winfo_rooty()+canvas.winfo_y()
x1=x+canvas.winfo_width()
y1=y+canvas.winfo_height()
ImageGrab.grab().crop((x,y,x1,y1)).save("testImageGrab.png")
3: Alon (https://stackoverflow.com/questions/9886274/how-can-i-convert-canvas-content-to-an-image)
ImageGrab.grab(bbox=(
canvas.winfo_rootx(),
canvas.winfo_rooty(),
canvas.winfo_rootx() + canvas.winfo_width(),
canvas.winfo_rooty() + canvas.winfo_height()
)).save("testImageGrab.png")
4: Mike - SMT (https://stackoverflow.com/questions/54637592/how-to-screenshot-the-entire-tkinter-canvas-as-png-or-pdf)
x = root.winfo_rootx() + canvas.winfo_x()
y = root.winfo_rooty() + canvas.winfo_y()
xx = x + canvas.winfo_width()
yy = y + canvas.winfo_height()
ImageGrab.grab(bbox=(x, y, xx, yy)).save("testImageGrab.png")
"""
This code is expected to get the whole visible canvas saved, however this is what I get: Result
This is my whole screen:WholeScreen
Additionnaly I made this program that should save the selected area of the canva. It works by saving the whole screen (this is working, I tried it individually) then cropping by selection. The event.x and event.y should return the x and y value of the mouse position on the screen, not on the canva which is waht we are looking for when cropping a screen shoot of the whole screen.
from tkinter import *
from PIL import ImageTk, Image
import tkinter as tk
import pyscreenshot as ImageGrab
def qPressed(event=1):
global started
started = 1
#change cursor style so that we know we are about to start the area selection
root.config(cursor="cross")
def B1Motion (event=1):
global started, initPos, x, y
if started:
if (initPos==0):
#get the initial values (x and y pos of the first mouse 1 click)
x=event.x
y=event.y
initPos=1
def B1Release (event=1):
global started, initPos, x, y
if started:
if initPos:
#to know that the function has started
print("exporting....") #to know that the function has started
#grab screen, crop based on x and y of first time mouse 1 was pressed after q was pressed and current x and y of mouse
ImageGrab.grab().crop((x, y, event.x, event.y)).save("testImageGrab.png")
#to know that the function has finished
print("... export ended.")
#reset the variables and others
x=0
y=0
initPos=0
started=0
root.config(cursor="arrow")
#Create root and canvas
root = tk.Tk()
canvas = tk.Canvas(root, width=400, height=400, background="bisque")
canvas.pack(fill=BOTH, expand=1)
#Variables
started=0
initPos=0
#Plot an image !!!!! Change text.png" to an image you have in your repertory !!!!!
imageTemp=Image.open("text.png")
schematicImage=ImageTk.PhotoImage(imageTemp)
imageAffichee=canvas.create_image(0,0,image=schematicImage)
#Binds
root.bind("<Key-q>",qPressed)
root.bind("<B1-Motion>",B1Motion)
root.bind("<ButtonRelease-1>",B1Release)
root.mainloop()
Thank you in advance for your kind answers. <3
As stated by H3coder, my settings had "application zoom" at 150%. I have been able to fix my issue by simply multiplying my values by 1.5 It would be nice to fetch the "application zoom" value directly from the computer, I ll look into that later. So this code works if you don't have a random zoom.