Search code examples
tkinterwidgetscreenshotpython-3.9

How do you take a screenshot of a particular widget in Tkinter?


How do you take a screenshot of a particular widget? I keep on getting "how to take screenshot of whole screen" or "how to take screenshot of the window" when searching, but I want to know how to take screenshot of a widget, a Frame, to be exact. I'm afraid that this forum would require me to give them a code snippet, when in reality I don't know what to do. So I'm just asking if this is possible, and how to do it.

PS: I would really appreciate if it is possible to just give a function the widget variable, and then it would go searching for that widget and then take a precise screenshot of it. I know that there is pyautogui which searches for images, I just don't know what I need to do exactly, since this frame isn't an image and it always changes from now and then, so the automation might get confused.


Solution

  • PS: I would really appreciate if it is possible to just give a function the widget variable, and then it would go searching for that widget and then take a precise screenshot of it.

    Nope, there is no function that would take the screenshot of the widget. What you can do is use PIL to take the screenshot of the image. For that you need the coordinates of the frame, which you can get dynamically using methods of widgets.

    First you need to install PIL, so:

    pip install Pillow 
    

    If on Linux, then PIL.ImageGrab will not work, so install another library for taking screenshot, instead of PIL:

    pip install pyscreenshot
    

    Here is a modular code that you can use with any widget by passing the widget as an argument to the function:

    from PIL import ImageGrab # If on Linux, use `import pyscreenshot as ImageGrab` after installing it 
    
    def capture(wid,file_name='img',file_format='png'):
        """Take screenshot of the passed widget"""
    
        x0 = wid.winfo_rootx()
        y0 = wid.winfo_rooty()
        x1 = x0 + wid.winfo_width()
        y1 = y0 + wid.winfo_height()
        
        im = ImageGrab.grab(bbox=(x0, y0, x1, y1)) # bbox means boundingbox, which is shown in the image below
        im.save(f'{file_name}.{file_format}')  # Can also say im.show() to display it
    

    Now all you need is to implement this function into your code, here is an example:

    from tkinter import *
    from PIL import ImageGrab # If on Linux, use `import pyscreenshot as ImageGrab` after installing it
    
    root = Tk()
    
    def capture(wid,file_name='img',file_format='png'):
        """Take screenshot of the passed widget"""
    
        x0 = wid.winfo_rootx()
        y0 = wid.winfo_rooty()
        x1 = x0 + wid.winfo_width()
        y1 = y0 + wid.winfo_height()
    
        im = ImageGrab.grab((x0, y0, x1, y1))
        im.save(f'{file_name}.{file_format}')  # Can also say im.show() to display it
    
    frame = Frame(root,bg='red',width=100,height=185)
    frame.pack()
    frame.pack_propagate(0) # Make it not auto-resize according to widgets
    
    for i in range(5):
        Button(frame,text=f'Button {i}').pack()
    
    Button(root,text='Take screenshot',command=lambda: capture(frame,img_name='frame',img_format='png')).pack()
    
    root.mainloop()
    

    If you are wondering what the coordinates passed into bbox refers to, then: