0

Try to capture the image of a widget in tkinter window by following code, but just get the screen area under tkinter window.

import ctypes
import platform
from time import sleep
import tkinter as tk

from PIL import ImageGrab

if platform.system() == "Windows" and platform.release() == "10":
    ctypes.windll.shcore.SetProcessDpiAwareness(1)

root = tk.Tk()
root.geometry("+0+0")

var = tk.StringVar()
var.set("Hello World")
entry = tk.Entry(root, width=40, bg='green', takefocus=False, textvariable=var)
entry.pack()

root.update()
# sleep(1)

x, y = entry.winfo_rootx(), entry.winfo_rooty()
width, height = entry.winfo_width(), entry.winfo_height()

im = ImageGrab.grab(bbox=(x, y, x+width, y+height))    # Grab image on screen
im.show()

root.mainloop()

It will be OK if line # sleep(1) replaced by sleep(1). Had been tried method update or update_idletasks, all got same results.

Is there any method to finalize tkinter window immediately ? So I don't need to sleep one second each time to grap image of a widget. Or I get wrong way to go it, any suggestion is welcome.

Jason Yang
  • 11,284
  • 2
  • 9
  • 23
  • 1
    Put it inside a function and then `root.after(1000,func)`. It is just due to the fact that it happens fast but the window is not fully shown in that time. – Delrius Euphoria Jul 14 '21 at 06:25
  • It is same, I still need to wait 1 second ... after widget finalized. I have lot of widgets to capture images initially, then need to capture and move image to follow movement of mouse. So lomg time to wait or to sleep may delay the action. – Jason Yang Jul 14 '21 at 06:32
  • So you are asking for some method to speed up the window showing up? – Delrius Euphoria Jul 14 '21 at 06:36
  • Remember that tkinter, like all GUI frameworks, is event based. Your windows are not drawn when they are created. Instead, the creation just sends a message. The purpose of the mainloop is to read any pending messages and dispatch them for processing. So, NOTHING gets drawn until you get into mainloop. So, you need to do some timer callbacks to make sure the windows are drawn. – Tim Roberts Jul 14 '21 at 06:37
  • @TimRoberts I think window is supposed to be _drawn_ with `root.update`, it is just that it takes time to pop the window up, and `PIL` grabs the coordinates quickly while the window is still almost being _drawn_. – Delrius Euphoria Jul 14 '21 at 06:41
  • @TimRoberts I tested it with `sleep(3);print('mainloop'); root.mainloop() ` after `root.update()`, window did finalize and shown, then 'mainloop' printed after 3 seconds. – Jason Yang Jul 14 '21 at 06:50
  • Is there any method to finalize tkinter window **immediately** ? It looks like that methods update and update_idletasks still wait some time or idle to update GUI of window. – Jason Yang Jul 14 '21 at 06:53
  • Did you look into the function `root.wait_visibility(w)`? It's supposed to wait until widget w becomes visible. I've never used it but it might be worth a try. – Paul Cornelius Jul 14 '21 at 07:23
  • Code will wait and block at `root.wait_visibility()` for nothing changed. – Jason Yang Jul 14 '21 at 07:27
  • You're hacking here, trying a bunch of random stuff and hoping something sticks. That's poor programming. Learn to do event-driven programming as it is designed to be used, and you won't have to re-do it later. – Tim Roberts Jul 14 '21 at 17:10
  • No, it's not true. Just try to show my question in short and executable code. Actually it will be event-drivern for mouse clicking event. It will work like a tooltip, but I want drag a widget in a new generated window and I cannot wait too long time before it. – Jason Yang Jul 14 '21 at 17:33
  • Are you still waiting for a better answer? If not, would appreciate marking the one below as correct :D – Delrius Euphoria Jul 16 '21 at 20:19
  • I didn't get the correct answer for this issue, it still wait time to grab widget. – Jason Yang Jul 17 '21 at 04:28
  • @JasonYang Because there is no way to do so...? – Delrius Euphoria Jul 26 '21 at 11:10

1 Answers1

2

It is obvious right? It needs to take some time to show the window but the screenshot is captured quite quickly. What do I suggest? Put it inside a function and take advantage of the after method here.

def screenshot():
    im = ImageGrab.grab(bbox=(x, y, x+width, y+height))    # Grab image on screen
    im.show()

root.after(1000,screenshot) # Depending on the time required to take show the window
Delrius Euphoria
  • 14,910
  • 3
  • 15
  • 46