4

I have created a GUI app in Python tkinter for analyzing data in my laboratory. There are a number of buttons, figures, and canvas widgets. It would be helpful to take a screenshot of the entire window ('root') using just a single button that saves the filename appropriately. Example using Mac's built-in "screenshot" app here.

Related questions here, here, and here, but none worked successfully. The final link was almost successful, however the image that is saved is my computer's desktop background. My computer is a Mac, MacOS Monterey 12.0.1.

'root' is the tkinter window because

root = tk.Tk()

appears at the beginning of the script, analogous to 'window' in the example here. I'm using PIL.ImageGrab in the code sample below.

This is the current code, which takes an unhelpful screenshot of my desktop background,

def screenshot():
    # retrieve the time string to use as a filename
    file_name = root.time_string[-6:]
    full_file_name = file_name + '_summary' + '.png'
    x = root.winfo_rootx() + root.winfo_x()
    y = root.winfo_rooty() + root.winfo_y()
    x1 = x + root.winfo_width()
    y1 = y + root.winfo_height()
    ImageGrab.grab().crop((x, y, x1, y1)).save(full_file_name)

I create the button like so:

screenshot_btn = tk.Button(root, text='Screenshot', command=lambda: screenshot(), font=('Verdana', 24), state=DISABLED)

And I place the button in 'root' like this:

screenshot_btn.grid(row=11, column=3)

[This is my first post at stackoverflow. I apologize in advance if I did not follow all the guidelines perfectly on the first try. Thanks for your patience.]

martineau
  • 119,623
  • 25
  • 170
  • 301
mathman
  • 43
  • 4
  • 1
    What did you get by `ImageGrab.grab().save(full_file_name)` ? Do you have multiple screens ? What Python version do you run? Which version of PIL? – Thingamabobs Jan 14 '22 at 21:09
  • 1
    Check this out: https://github.com/BoboTiG/python-mss/issues/134#issuecomment-567543451 – Delrius Euphoria Jan 14 '22 at 22:00
  • 1
    @CoolCloud Yes this was exactly the problem. I adjusted the security settings to allow PyCharm access to Screen Recording and it worked! Thank you! – mathman Jan 17 '22 at 20:53

2 Answers2

1

First, I didn't have an issue with the grab showing my desktop, but it was showing an improperly cropped image.

I have found a hacky solution. The issues appears to be with the resolution. So the dimensions need some scaling.

What I did was get the output from ImageGrab.grab().save(full_file_name) ( no cropping ) and measure the size of the desired image area in pixels. These dimensions will be called x_pixels and y_pixels.

Then I measured that same area on the actual window in screen units. I did this by bringing up the mac screenshot tool which shows the dimensions of an area. I then call these dimensions x_screen and y_screen. Then I modified your screenshot function as follows.

def screenshot():
    # retrieve the time string to use as a filename
    file_name = root.time_string[-6:]
    full_file_name = file_name + '_summary' + '.png'
    x = root.winfo_rootx()
    y = root.winfo_rooty()
    x1 = x + root.winfo_width()
    y1 = y + root.winfo_height()
    x_pixels = 337
    y_pixels = 79
    x_screen = 171
    y_screen = 41
    x = x*(x_pixels/x_screen)
    y = y*(y_pixels/y_screen)
    x1 = x1*(x_pixels/x_screen)
    y1 = y1*(y_pixels/y_screen)
    ImageGrab.grab().crop((x, y, x1, y1)).save(full_file_name)

Notice I also removed +root.winfo_x() and +root.winfo_y()

The result is shown below. It's not perfect, but I believe if I more carefully measured the pixels at the bounds of the screenshot and window the scaling would be improved.

Example Screenshot

nikost
  • 788
  • 6
  • 14
  • Thank you @nikost . One I figured out the main problem - giving Screen Recording permissions to PyCharm - I encountered the cropping issue you describe here. Your solution worked. – mathman Jan 17 '22 at 21:00
0

Do you by chance have a "Retina" monitor? ImageGrab fails to take into account the 144 DPI image it collects from the Mac's screencapture command. You can compensate for this by multiplying all the x, y, x1, y1 values by 2 (but you may still miss a bit if you need to account for the Mac window titlebar).

Alternatively, use the pyscreenshot package (or steal its code...). It uses the "-R" option of screencapture to get the proper bounds for the image. Works on extended monitors like mine where the x co-ord is < 0 ('cause it's to the left of the main monitor).

resnbl
  • 191
  • 2
  • 4