Tomar una captura de pantalla a través de un script de Python en Linux

81

Quiero tomar una captura de pantalla a través de una secuencia de comandos de Python y guardarla discretamente.

Solo estoy interesado en la solución Linux y debería admitir cualquier entorno basado en X.

skyronic
fuente
¿Alguna razón por la que no puedes usar scrot ?
Mark
Tengo curiosidad por comprobar el rendimiento de los métodos sugeridos a continuación.
JDong
4
Nuevo enlace: manpages.ubuntu.com/manpages/karmic/man1/scrot.1.html (@ArtOfWarfare)
Mark
@Mark -: - / Lamentablemente, Scrot no viene con OS X (lo sé, esta era una pregunta de Linux. Normalmente, lo que se aplica a Linux también se puede aplicar a OS X textualmente).
ArtOfWarfare
Ahh, bien, es una captura de pantalla en OS X.
Mark

Respuestas:

65

Esto funciona sin tener que usar scrot o ImageMagick.

import gtk.gdk

w = gtk.gdk.get_default_root_window()
sz = w.get_size()
print "The size of the window is %d x %d" % sz
pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,False,8,sz[0],sz[1])
pb = pb.get_from_drawable(w,w.get_colormap(),0,0,0,0,sz[0],sz[1])
if (pb != None):
    pb.save("screenshot.png","png")
    print "Screenshot saved to screenshot.png."
else:
    print "Unable to get the screenshot."

Tomado de http://ubuntuforums.org/showpost.php?p=2681009&postcount=5

Oxidado
fuente
Esto no funciona en aplicaciones basadas en GUI que usan glade y rápidamente, ¿puedes mejorar este código?
Subodh Ghulaxe
Cuando ejecuto este código (usando linux mint 16 en virtualbox) la imagen resultante es completamente negra. ¿Tienes idea de por qué?
bab
A veces, la codificación de los colores está apagada. Es bastante molesto. Vea si github.com/JDong820/neobot/blob/master/Linux/Robot/screen.py es de alguna ayuda para usted; observe la llamada a get_rowstride.
JDong
48

Recopile todas las respuestas en una clase. Emite una imagen PIL.

#!/usr/bin/env python
# encoding: utf-8
"""
screengrab.py

Created by Alex Snet on 2011-10-10.
Copyright (c) 2011 CodeTeam. All rights reserved.
"""

import sys
import os

import Image


class screengrab:
    def __init__(self):
        try:
            import gtk
        except ImportError:
            pass
        else:
            self.screen = self.getScreenByGtk

        try:
            import PyQt4
        except ImportError:
            pass
        else:
            self.screen = self.getScreenByQt

        try:
            import wx
        except ImportError:
            pass
        else:
            self.screen = self.getScreenByWx

        try:
            import ImageGrab
        except ImportError:
            pass
        else:
            self.screen = self.getScreenByPIL


    def getScreenByGtk(self):
        import gtk.gdk      
        w = gtk.gdk.get_default_root_window()
        sz = w.get_size()
        pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,False,8,sz[0],sz[1])
        pb = pb.get_from_drawable(w,w.get_colormap(),0,0,0,0,sz[0],sz[1])
        if pb is None:
            return False
        else:
            width,height = pb.get_width(),pb.get_height()
            return Image.fromstring("RGB",(width,height),pb.get_pixels() )

    def getScreenByQt(self):
        from PyQt4.QtGui import QPixmap, QApplication
        from PyQt4.Qt import QBuffer, QIODevice
        import StringIO
        app = QApplication(sys.argv)
        buffer = QBuffer()
        buffer.open(QIODevice.ReadWrite)
        QPixmap.grabWindow(QApplication.desktop().winId()).save(buffer, 'png')
        strio = StringIO.StringIO()
        strio.write(buffer.data())
        buffer.close()
        del app
        strio.seek(0)
        return Image.open(strio)

    def getScreenByPIL(self):
        import ImageGrab
        img = ImageGrab.grab()
        return img

    def getScreenByWx(self):
        import wx
        wx.App()  # Need to create an App instance before doing anything
        screen = wx.ScreenDC()
        size = screen.GetSize()
        bmp = wx.EmptyBitmap(size[0], size[1])
        mem = wx.MemoryDC(bmp)
        mem.Blit(0, 0, size[0], size[1], screen, 0, 0)
        del mem  # Release bitmap
        #bmp.SaveFile('screenshot.png', wx.BITMAP_TYPE_PNG)
        myWxImage = wx.ImageFromBitmap( myBitmap )
        PilImage = Image.new( 'RGB', (myWxImage.GetWidth(), myWxImage.GetHeight()) )
        PilImage.fromstring( myWxImage.GetData() )
        return PilImage

if __name__ == '__main__':
    s = screengrab()
    screen = s.screen()
    screen.show()
Alex Snet
fuente
No sé si ha habido un cambio en wxWidgets desde esta publicación, pero el getScreenByWxmétodo falla wx._core.PyNoAppError: The wx.App object must be created first!. Curiosamente, el código funciona bien si lo ingresa línea por línea en el shell de Python, pero falla en un script.
CadentOrange
¡Deberías probar tu código! O no tu, si lo estás publicando ... En getScreenByWxdebes a) reemplazar myBitmappor bmpyb) guardar wx.App()en una variable. En getScreenByGtkreemplazo (pb != None)de pb is None. Y no use Qt, así que, no puede crear dos veces QApplication, su aplicación se bloqueará al intentar crearla por segunda vez.
Jurado
42

Solo para completar: Xlib, pero es algo lento cuando se captura toda la pantalla:

from Xlib import display, X
import Image #PIL

W,H = 200,200
dsp = display.Display()
root = dsp.screen().root
raw = root.get_image(0, 0, W,H, X.ZPixmap, 0xffffffff)
image = Image.fromstring("RGB", (W, H), raw.data, "raw", "BGRX")
image.show()

Se podría intentar introducir algunos tipos en los archivos de cuello de botella en PyXlib y luego compilarlos usando Cython. Eso podría aumentar un poco la velocidad.


Editar: Podemos escribir el núcleo de la función en C y luego usarlo en python desde ctypes, aquí hay algo que pirateé juntos:

#include <stdio.h>
#include <X11/X.h>
#include <X11/Xlib.h>
//Compile hint: gcc -shared -O3 -lX11 -fPIC -Wl,-soname,prtscn -o prtscn.so prtscn.c

void getScreen(const int, const int, const int, const int, unsigned char *);
void getScreen(const int xx,const int yy,const int W, const int H, /*out*/ unsigned char * data) 
{
   Display *display = XOpenDisplay(NULL);
   Window root = DefaultRootWindow(display);

   XImage *image = XGetImage(display,root, xx,yy, W,H, AllPlanes, ZPixmap);

   unsigned long red_mask   = image->red_mask;
   unsigned long green_mask = image->green_mask;
   unsigned long blue_mask  = image->blue_mask;
   int x, y;
   int ii = 0;
   for (y = 0; y < H; y++) {
       for (x = 0; x < W; x++) {
         unsigned long pixel = XGetPixel(image,x,y);
         unsigned char blue  = (pixel & blue_mask);
         unsigned char green = (pixel & green_mask) >> 8;
         unsigned char red   = (pixel & red_mask) >> 16;

         data[ii + 2] = blue;
         data[ii + 1] = green;
         data[ii + 0] = red;
         ii += 3;
      }
   }
   XDestroyImage(image);
   XDestroyWindow(display, root);
   XCloseDisplay(display);
}

Y luego el archivo python:

import ctypes
import os
from PIL import Image

LibName = 'prtscn.so'
AbsLibPath = os.path.dirname(os.path.abspath(__file__)) + os.path.sep + LibName
grab = ctypes.CDLL(AbsLibPath)

def grab_screen(x1,y1,x2,y2):
    w, h = x2-x1, y2-y1
    size = w * h
    objlength = size * 3

    grab.getScreen.argtypes = []
    result = (ctypes.c_ubyte*objlength)()

    grab.getScreen(x1,y1, w, h, result)
    return Image.frombuffer('RGB', (w, h), result, 'raw', 'RGB', 0, 1)

if __name__ == '__main__':
  im = grab_screen(0,0,1440,900)
  im.show()
JHolta
fuente
3
Esto vale oro, si no al menos más votos positivos que las otras respuestas. ¡Trabajo sólido y nativo también! ¡Salud!
Torxed
1
para aquellos que buscan una manera rápida: este enfoque toma ~ 25 ms en promedio para una foto de tamaño 1000 x 1000.
DiCaprio
1
@JHolta, ¿conoces alguna forma de cambiar la calidad de la imagen capturada? (para acelerar aún más)
DiCaprio
1
¡No! Actualmente solo se está copiando la imagen del escritorio tal como está, cualquier transformación de la imagen producirá una sobrecarga. Entonces, sin reducir la calidad real de su escritorio, no tiene suerte con esa idea. De todos modos, la sobrecarga actual está presumiblemente en el extremo de Python, donde preasignamos un búfer (se puede hacer probablemente en c), y la otra parte lenta donde PIL lee en este búfer, ambos pueden optimizarse y tal vez colocado en el extremo C de las cosas.
JHolta
6
Esto funciona muy bien, pero tuve que #include <X11/Xutil.h>hacerlo en lugar de hacerlo #include <X11/Xlib.h>. También para la compilación, que tenía que mover -lX11a finales de esta manera: gcc -shared -O3 -Wall -fPIC -Wl,-soname,prtscn -o prtscn.so prtscn.c -lX11.
Josh
18

Este funciona en X11, y quizás también en Windows (alguien, por favor verifique). Necesita PyQt4 :

import sys
from PyQt4.QtGui import QPixmap, QApplication
app = QApplication(sys.argv)
QPixmap.grabWindow(QApplication.desktop().winId()).save('test.png', 'png')
Juliano
fuente
2
Tome nota de la licencia de PyQt, que es más restrictiva que Python y Qt. riverbankcomputing.co.uk/software/pyqt/license
user120242
Es la única solución que se ejecuta en mis instalaciones de Linux "lista para usar". No sé por qué, pero tengo PyQt4 en todas partes, mientras que carece de PyWX, PyGtk, ImageGrab. - Gracias :).
Grzegorz Wierzowiecki
El código acaba de funcionar (en Windows 7 x64 - Python 2.7.5; distribución de Pythonxy). Jpeg también disponible (por ejemplo ... .save ( 'd: /test.jpg', 'jpeg'))
Mohamad Fakih
15

Tengo un proyecto de envoltura ( captura de pantalla ) para scrot, imagemagick, pyqt, wx y pygtk. Si tiene uno de ellos, puede usarlo. Todas las soluciones se incluyen en esta discusión.

Instalar en pc:

easy_install pyscreenshot

Ejemplo:

import pyscreenshot as ImageGrab

# fullscreen
im=ImageGrab.grab()
im.show()

# part of the screen
im=ImageGrab.grab(bbox=(10,10,500,500))
im.show()

# to file
ImageGrab.grab_to_file('im.png')
ponty
fuente
ImportError: no se puede importar el nombre gtkpixbuf
tommy.carstensen
me da este error:pyscreenshot.tempexport.RunProgError: No protocol specified giblib error: Can't open X display. It *is* running, yeah?" timeout_happened=False>
Jasar Orion
9

Solución multiplataforma usando wxPython :

import wx
wx.App()  # Need to create an App instance before doing anything
screen = wx.ScreenDC()
size = screen.GetSize()
bmp = wx.EmptyBitmap(size[0], size[1])
mem = wx.MemoryDC(bmp)
mem.Blit(0, 0, size[0], size[1], screen, 0, 0)
del mem  # Release bitmap
bmp.SaveFile('screenshot.png', wx.BITMAP_TYPE_PNG)
Bola de nieve
fuente
Referencias, con comentarios, explicación y contexto dentro del código Python. blog.pythonlibrary.org/2010/04/16/… o blog.pythonlibrary.org/2010/04/16/…
Civil
5

Puedes usar esto

import os
os.system("import -window root screen_shot.png")
Jack Sparrow
fuente
Este es un buen enfoque una vez que puede obtener la imagen de un programa en segundo plano, pero es bueno saber que si el programa está enfocado, devolverá una excepción.
Lucas Araújo
3

Un poco tarde, pero no importa, es fácil.

import autopy
import time
time.sleep(2)
b = autopy.bitmap.capture_screen()
b.save("C:/Users/mak/Desktop/m.png")
pkm
fuente
3

No pude tomar una captura de pantalla en Linux con pyscreenshot o scrot porque la salida de pyscreenshotera solo un archivo de imagen png de pantalla negra.

pero gracias a Dios había otra forma muy fácil de hacer capturas de pantalla en Linux sin instalar nada. simplemente ponga el código debajo en su directorio y ejecútelo conpython demo.py

import os
os.system("gnome-screenshot --file=this_directory.png")

también hay muchas opciones disponibles para gnome-screenshot --help

Application Options:
  -c, --clipboard                Send the grab directly to the clipboard
  -w, --window                   Grab a window instead of the entire screen
  -a, --area                     Grab an area of the screen instead of the entire screen
  -b, --include-border           Include the window border with the screenshot
  -B, --remove-border            Remove the window border from the screenshot
  -p, --include-pointer          Include the pointer with the screenshot
  -d, --delay=seconds            Take screenshot after specified delay [in seconds]
  -e, --border-effect=effect     Effect to add to the border (shadow, border, vintage or none)
  -i, --interactive              Interactively set options
  -f, --file=filename            Save screenshot directly to this file
  --version                      Print version information and exit
  --display=DISPLAY              X display to use
cyera
fuente
2

Hay un paquete de Python para este Autopy

El módulo de mapa de bits puede capturar pantalla (bitmap.capture_screen) Es multiplataforma (Windows, Linux, Osx).

ouille
fuente
1

De este hilo :

 import os
 os.system("import -window root temp.png")
entendido
fuente
1

para ubuntu este trabajo para mí, puede tomar una captura de pantalla de la ventana de selección con esto:

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gdk
from gi.repository import GdkPixbuf
import numpy as np
from Xlib.display import Display

#define the window name
window_name = 'Spotify'

#define xid of your select 'window'
def locate_window(stack,window):
    disp = Display()
    NET_WM_NAME = disp.intern_atom('_NET_WM_NAME')
    WM_NAME = disp.intern_atom('WM_NAME') 
    name= []
    for i, w in enumerate(stack):
        win_id =w.get_xid()
        window_obj = disp.create_resource_object('window', win_id)
        for atom in (NET_WM_NAME, WM_NAME):
            window_name=window_obj.get_full_property(atom, 0)
            name.append(window_name.value)
    for l in range(len(stack)):
        if(name[2*l]==window):
            return stack[l]

window = Gdk.get_default_root_window()
screen = window.get_screen()
stack = screen.get_window_stack()
myselectwindow = locate_window(stack,window_name)
img_pixbuf = Gdk.pixbuf_get_from_window(myselectwindow,*myselectwindow.get_geometry()) 

para transformar pixbuf en una matriz

def pixbuf_to_array(p):
    w,h,c,r=(p.get_width(), p.get_height(), p.get_n_channels(), p.get_rowstride())
    assert p.get_colorspace() == GdkPixbuf.Colorspace.RGB
    assert p.get_bits_per_sample() == 8
    if  p.get_has_alpha():
        assert c == 4
    else:
        assert c == 3
    assert r >= w * c
    a=np.frombuffer(p.get_pixels(),dtype=np.uint8)
    if a.shape[0] == w*c*h:
        return a.reshape( (h, w, c) )
    else:
        b=np.zeros((h,w*c),'uint8')
        for j in range(h):
            b[j,:]=a[r*j:r*j+w*c]
        return b.reshape( (h, w, c) )

beauty_print = pixbuf_to_array(img_pixbuf)
Matheus K.
fuente
0

Es una vieja pregunta. Me gustaría responderla usando nuevas herramientas.

Funciona con python 3 (debería funcionar con python 2, pero no lo he probado) y PyQt5.

Ejemplo de trabajo mínimo. Cópielo en el shell de Python y obtenga el resultado.

from PyQt5.QtWidgets import QApplication
app = QApplication([])
screen = app.primaryScreen()
screenshot = screen.grabWindow(QApplication.desktop().winId())
screenshot.save('/tmp/screenshot.png')
Rominf
fuente
¿Tiene el tiempo promedio para que se complete esta función? solo interés si vale la pena
DiCaprio
1
@Mrlenny 300 ms (para código completo), 165 ms (últimas tres líneas de código).
rominf
-3

Intentalo:

#!/usr/bin/python

import gtk.gdk
import time
import random
import socket
import fcntl
import struct
import getpass
import os
import paramiko     

while 1:
    # generate a random time between 120 and 300 sec
    random_time = random.randrange(20,25)
    # wait between 120 and 300 seconds (or between 2 and 5 minutes) 

    print "Next picture in: %.2f minutes" % (float(random_time) / 60)

    time.sleep(random_time)
    w = gtk.gdk.get_default_root_window()   
    sz = w.get_size()
    print "The size of the window is %d x %d" % sz
    pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,False,8,sz[0],sz[1])
    pb = pb.get_from_drawable(w,w.get_colormap(),0,0,0,0,sz[0],sz[1])
    ts = time.asctime( time.localtime(time.time()) )
    date = time.strftime("%d-%m-%Y")
    timer = time.strftime("%I:%M:%S%p")
    filename = timer
    filename += ".png"

    if (pb != None):
        username = getpass.getuser() #Get username
        newpath = r'screenshots/'+username+'/'+date #screenshot save path
        if not os.path.exists(newpath): os.makedirs(newpath)
        saveas = os.path.join(newpath,filename)
        print saveas
        pb.save(saveas,"png")
    else:
        print "Unable to get the screenshot."
Anand
fuente
3
¿Qué es esta mierda? La mitad de las importaciones son inútiles, hay un whilebucle que nunca sale (y usa en 1lugar de True), tiene en if (pb != None):lugar de solo if pb:, tiene algunas cadenas sin procesar sin sentido.
ArtOfWarfare