2

I want to have a python script that can take a screenshot without saving it directly to the disk immediately. Basically is there a module with a function that returns the raw bytes that I can then write into a file by myself manually?

import some_screenshot_module
raw_data = some_screenshot_module.return_raw_screenshot_bytes()
f = open('screenshot.png','wb')
f.write(raw_data)
f.close()

I have already checked out mss, pyscreenshot and PIL yet I could not find what I needed. I found a function that looked like what I was looking for, called frombytes. However after retrieving the bytes from the frombytes function and saving it into a file I couldn't view it not as a .BMP,.PNG,.JPG. Is there a function that returns the raw bytes that I can save into a file by myself or perhaps a module with a function like that?

Tiger-222
  • 6,677
  • 3
  • 47
  • 60
eclipse
  • 197
  • 1
  • 5
  • 16

2 Answers2

7

As of MSS 3.1.2, with the commit dd5298, you can do that easily:

import mss
import mss.tools


with mss.mss() as sct:
    # Use the 1st monitor
    monitor = sct.monitors[1]

    # Grab the picture
    im = sct.grab(monitor)

    # Get the entire PNG raw bytes
    raw_bytes = mss.tools.to_png(im.rgb, im.size)

    # ...

The update is already available on PyPi.


Original answer

Using the MSS module, you can access to raw bytes:

import mss
import mss.tools


with mss.mss() as sct:
    # Use the 1st monitor
    monitor = sct.monitors[1]

    # Grab the picture
    im = sct.grab(monitor)

    # From now, you have access to different attributes like `rgb`
    # See https://python-mss.readthedocs.io/api.html#mss.tools.ScreenShot.rgb
    # `im.rgb` contains bytes of the screen shot in RGB _but_ you will have to
    # build the complete image because it does not set needed headers/structures
    # for PNG, JPEG or any picture format.
    # You can find the `to_png()` function that does this work for you,
    # you can create your own, just take inspiration here:
    # https://github.com/BoboTiG/python-mss/blob/master/mss/tools.py#L11

    # If you would use that function, it is dead simple:
    # args are (raw_data: bytes, (width, height): tuple, output: str)
    mss.tools.to_png(im.rgb, im.size, 'screenshot.png')

Another example using part of the screen: https://python-mss.readthedocs.io/examples.html#part-of-the-screen

Here is the documentation for more informations: https://python-mss.readthedocs.io/api.html

Tiger-222
  • 6,677
  • 3
  • 47
  • 60
  • What I want to do is to STORE the raw bytes in a variable. So instead of writing the raw bytes to a file I want the raw bytes to be stored in a variable – eclipse Dec 29 '17 at 05:46
  • By `raw bytes` do you mean screen shot data or the whole PNG structure including the screen shot data? – Tiger-222 Dec 29 '17 at 09:27
  • the screenshot data. It needs to be stored so I can use pure python to open a new .png file in write binary mode, write the data to the .png file, close it and when I open it I can view the screenshot – eclipse Dec 29 '17 at 13:39
  • It is exactly what `im.rgb` contains, it is `bytes` in RGB form. – Tiger-222 Dec 29 '17 at 14:28
  • but you can't open a file using the standard open() function in python and write the byes to a png file. You won't be able to open it as the file contents is invalid with the .png format – eclipse Dec 30 '17 at 12:21
  • OK, your question was not clear. I updated te answer to fit your needs :) – Tiger-222 Jan 05 '18 at 07:48
1

you can still use the pyscreenshot module and PIL with the grab_to_file function,just use named pipes instead of an actual file.

if you are on linux you can use os.mkfifo to create the pipe then open the fifo for read in one thread and have the pyscreenshot.grab_to_file be called in a different thread (it has to be different threads since opening the fifo for write blocks until another thread opens it for read and vice-versa)

here's a code snippet that would work:

import os
import multiprocessing
import pyscreenshot

fifo_name = "/tmp/fifo.png"


def read_from_fifo(file_name):
    f = open(file_name,"rb")
    print f.read()
    f.close()

os.mkfifo(fifo_name)
proc = multiprocessing.Process(target=read_from_fifo, args=(fifo_name,))
proc.start()

pyscreenshot.grab_to_file(fifo_name)

in this case i am just printing the raw bytes to the screen but you can do what ever you want with it

also note that even though the contents are never written to the disk there IS a temporary file on disk but the data is never buffered in it

AntiMatterDynamite
  • 1,495
  • 7
  • 17
  • It doesn't work on windows though, is there a way to replicate this on windows? – eclipse Dec 26 '17 at 01:20
  • windows also has a way of accessing pipes as files , try reading on how to handle them here https://msdn.microsoft.com/en-us/library/windows/desktop/aa365590(v=vs.85).aspx then you can use win32pipe to create the pipe , i can't test any code for this so i won't try to give a code example but it should be possible – AntiMatterDynamite Dec 27 '17 at 14:28
  • I managed to test the code on a linux VM and I get the error "file or stream is not seekable" – eclipse Dec 28 '17 at 04:30
  • im sorry i should have specified im using ubuntu and that means pyscreenshot uses gtk , if you use another distribution it might use other libraries – AntiMatterDynamite Dec 28 '17 at 07:57