0

I'm trying to take a screenshot with python. But every option I try only seems to return the desktop wallpaper and not the programs on top.

Here's a run through of what I've tried and how I'm doing it.

First I used autopy to try and get pixels from the screen using autopy.color.hex_to_rgb(autopy.screen.get_color(x, y)). But it was only telling me the pixels of the desktop background.

Then I tried PIL(Pillow) using this code:

from PIL import ImageGrab

im = ImageGrab.grab()
im.save('screenshot.png')

This only returned the desktop wallpaper.

Finally, I've tried using this script which I found on this thread.

import Quartz
import LaunchServices
from Cocoa import NSURL
import Quartz.CoreGraphics as CG


def screenshot(path, region = None):
    """region should be a CGRect, something like:

    >>> import Quartz.CoreGraphics as CG
    >>> region = CG.CGRectMake(0, 0, 100, 100)
    >>> sp = ScreenPixel()
    >>> sp.capture(region=region)

    The default region is CG.CGRectInfinite (captures the full screen)
    """

    if region is None:
        region = CG.CGRectInfinite

    # Create screenshot as CGImage
    image = CG.CGWindowListCreateImage(
        region,
        CG.kCGWindowListOptionOnScreenOnly,
        CG.kCGNullWindowID,
        CG.kCGWindowImageDefault)

    dpi = 72 # FIXME: Should query this from somewhere, e.g for retina displays

    url = NSURL.fileURLWithPath_(path)

    dest = Quartz.CGImageDestinationCreateWithURL(
        url,
        LaunchServices.kUTTypePNG, # file type
        1, # 1 image in file
        None
        )

    properties = {
        Quartz.kCGImagePropertyDPIWidth: dpi,
        Quartz.kCGImagePropertyDPIHeight: dpi,
        }

    # Add the image to the destination, characterizing the image with
    # the properties dictionary.
    Quartz.CGImageDestinationAddImage(dest, image, properties)

    # When all the images (only 1 in this example) are added to the destination, 
    # finalize the CGImageDestination object. 
    Quartz.CGImageDestinationFinalize(dest)


if __name__ == '__main__':
    # Capture full screen
    screenshot("/tmp/testscreenshot_full.png")

    # Capture region (100x100 box from top-left)
    region = CG.CGRectMake(0, 0, 100, 100)
    screenshot("/tmp/testscreenshot_partial.png", region=region)

Again it returns my desktop wallpaper.

Why is it doing this? How can I get a screenshot in the same way I would if I were to press 'cmd + shift + 3'.

1 Answers1

3

This is a privacy feature, macOS prevents arbitrary apps from viewing the contents of other app windows. To get permission, your app will need access to the screen recording permission in macOS preferences.

Since this is a Python script, I think the app you're running the script from needs to be given permission, so either Terminal or whatever IDE you're using.

Stephen Jennings
  • 12,494
  • 5
  • 47
  • 66
  • Excellent, this is exactly it! Thank you so much. I tried to do that first. The popup comes up every time I launch the program even when I add 'Python' and 'IDLE' to the permissions list. In the end it didn't work so I ended up clicking deny every time and didn't think it was making any difference. For some reason I can't get the permissions to work to allow it to screenshot from IDLE. I added terminal to the permissions and ran the program that way. Now it works! – Alfie Stoppani Aug 06 '21 at 21:58