Machen Sie einen Screenshot über ein Python-Skript unter Linux

81

Ich möchte einen Screenshot über ein Python-Skript machen und ihn unauffällig speichern.

Ich interessiere mich nur für die Linux-Lösung und sollte jede X-basierte Umgebung unterstützen.

skyronic
quelle
Gibt es einen Grund, warum Sie Scrot nicht verwenden können ?
Mark
Ich bin gespannt auf die Leistung der unten vorgeschlagenen Methoden.
JDong
4
Neuer Link: manpages.ubuntu.com/manpages/karmic/man1/scrot.1.html (@ArtOfWarfare)
Mark
@Mark -: - / Leider kommt Scrot nicht mit OS X (Ich weiß, das war eine Linux-Frage. Normalerweise kann alles, was für Linux gilt, auch wörtlich für OS X gelten.)
ArtOfWarfare
Ahh richtig, nun, es ist Screencapture unter OS X.
Mark

Antworten:

65

Dies funktioniert ohne Verwendung von scrot oder 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."

Ausgeliehen von http://ubuntuforums.org/showpost.php?p=2681009&postcount=5

rostig
quelle
Dies funktioniert in GUI-basierten Anwendungen mit Glade nicht und Sie können diesen Code schnell verbessern.
Subodh Ghulaxe
Wenn ich diesen Code ausführe (unter Verwendung von Linux Mint 16 in Virtualbox), ist das resultierende Bild vollständig schwarz. Hast du eine Idee warum?
Bab
Manchmal ist die Codierung der Farben deaktiviert. Es ist ziemlich nervig. Überprüfen Sie, ob github.com/JDong820/neobot/blob/master/Linux/Robot/screen.py Ihnen hilft. Beachten Sie den Aufruf von get_rowstride.
JDong
48

Stellen Sie alle Antworten in einer Klasse zusammen. Gibt das PIL-Bild aus.

#!/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
quelle
Ich weiß nicht, ob seit diesem Beitrag eine Änderung an wxWidgets vorgenommen wurde, aber die getScreenByWxMethode schlägt mit fehl wx._core.PyNoAppError: The wx.App object must be created first!. Komischerweise funktioniert der Code einwandfrei, wenn Sie ihn Zeile für Zeile in die Python-Shell eingeben, aber in einem Skript schlägt er fehl.
CadentOrange
Sie sollten Ihren Code testen! Oder nicht Ihr, wenn Sie veröffentlichen es ... In getScreenByWxsollten Sie a) ersetzen myBitmapdurch bmpund b) Speichern wx.App()in eine Variable. In getScreenByGtkersetzen (pb != None)zu pb is None. Verwenden Sie Qt nicht. Sie können also nicht zweimal erstellen. QApplicationIhre App stürzt ab, wenn Sie versuchen, sie zum zweiten Mal zu erstellen.
Jury
40

Nur der Vollständigkeit halber: Xlib - Aber es ist etwas langsam, wenn der gesamte Bildschirm erfasst wird:

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()

Man könnte versuchen, einige Typen in die Engpassdateien in PyXlib zu werfen und sie dann mit Cython zu kompilieren. Das könnte die Geschwindigkeit etwas erhöhen.


Bearbeiten: Wir können den Kern der Funktion in C schreiben und ihn dann in Python von ctypes verwenden. Hier ist etwas, das ich zusammen gehackt habe:

#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);
}

Und dann die Python-Datei:

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
quelle
3
Dies ist Gold wert, wenn nicht mindestens mehr Stimmen als die anderen Antworten. Solide Arbeit und eine einheimische auch! Prost!
Torxed
1
Für diejenigen, die einen schnellen Weg suchen: Dieser Ansatz dauert durchschnittlich ~ 25 ms für ein Bild der Größe 1000 x 1000.
DiCaprio
1
@JHolta, kennen Sie eine Möglichkeit, die Qualität des aufgenommenen Bildes zu ändern? (um noch schneller zu werden)
DiCaprio
1
Nee. Derzeit wird nur das Desktop-Image so kopiert, wie es ist. Jede Transformation des Images verursacht einen Overhead. Ohne die tatsächliche Qualität Ihres Desktops zu beeinträchtigen, haben Sie bei dieser Idee kein Glück. Wie auch immer, der aktuelle Overhead liegt vermutlich am Python-Ende der Dinge, wo wir einen Puffer vorab zuweisen (kann wahrscheinlich in c erfolgen), und im anderen langsamen Teil, wo PIL diesen Puffer liest, können beide optimiert werden, und vielleicht am C-Ende der Dinge platziert.
JHolta
6
Das funktioniert super, aber ich musste #include <X11/Xutil.h>stattdessen #include <X11/Xlib.h>. Auch zum Kompilieren musste ich so ans -lX11Ende gehen : gcc -shared -O3 -Wall -fPIC -Wl,-soname,prtscn -o prtscn.so prtscn.c -lX11.
Josh
18

Dieser funktioniert unter X11 und vielleicht auch unter Windows (jemand, bitte überprüfen). Benötigt PyQt4 :

import sys
from PyQt4.QtGui import QPixmap, QApplication
app = QApplication(sys.argv)
QPixmap.grabWindow(QApplication.desktop().winId()).save('test.png', 'png')
Juliano
quelle
2
Bitte beachten Sie die Lizenzierung von PyQt, die restriktiver ist als Python und Qt. riverbankcomputing.co.uk/software/pyqt/license
user120242
Es ist die einzige Lösung, die auf meinen Linux-Installationen "out-of-the-box" ausgeführt wird. Ich weiß nicht warum, aber ich habe PyQt4 überall, während PyWX, PyGtk, ImageGrab fehlen. - Vielen Dank :).
Grzegorz Wierzowiecki
Der Code hat gerade funktioniert (unter Windows 7 x64 - Python 2.7.5; Pythonxy-Verteilung). JPEG auch verfügbar (zB ... speichern ('d: /test.jpg', 'JPEG'))
Mohamad Fakih
15

Ich habe ein Wrapper-Projekt ( Pyscreenshot ) für Scrot, Imagemagick, Pyqt, WX und Pygtk. Wenn Sie eine davon haben, können Sie sie verwenden. Alle Lösungen sind in dieser Diskussion enthalten.

Installieren:

easy_install pyscreenshot

Beispiel:

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
quelle
ImportError: Name gtkpixbuf
tommy.carstensen
es gibt mir diesen Fehler:pyscreenshot.tempexport.RunProgError: No protocol specified giblib error: Can't open X display. It *is* running, yeah?" timeout_happened=False>
Jasar Orion
9

Plattformübergreifende Lösung mit 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)
Schneeball
quelle
Verweise mit Kommentaren, Erklärungen und Kontext im Python-Code. blog.pythonlibrary.org/2010/04/16/… oder blog.pythonlibrary.org/2010/04/16/…
Civilian
6

Sie können dies verwenden

import os
os.system("import -window root screen_shot.png")
Jack Sparrow
quelle
Dies ist ein guter Ansatz, sobald Sie das Bild von einem Hintergrundprogramm erhalten können. Es ist jedoch gut zu wissen, dass eine Ausnahme zurückgegeben wird, wenn das Programm fokussiert ist.
Lucas Araújo
3

etwas spät, aber egal, man ist einfach

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

Ich konnte unter Linux keinen Screenshot mit Pyscreenshot oder Scrot machen, da die Ausgabe pyscreenshotnur eine PNG-Bilddatei mit schwarzem Bildschirm war.

Aber Gott sei Dank gab es eine andere sehr einfache Möglichkeit, Screenshots unter Linux aufzunehmen, ohne etwas zu installieren. Geben Sie einfach den folgenden Code in Ihr Verzeichnis ein und führen Sie ihn auspython demo.py

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

Es gibt auch viele verfügbare Optionen für 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
quelle
2

Für diese Autopy gibt es ein Python-Paket

Das Bitmap-Modul kann zum Screen Grabbing (bitmap.capture_screen) verwendet werden. Es ist eine Multiplateform (Windows, Linux, Osx).

Ouille
quelle
1

Aus diesem Thread :

 import os
 os.system("import -window root temp.png")
verstanden
quelle
0

Das ist eine alte Frage. Ich möchte es mit neuen Tools beantworten.

Funktioniert mit Python 3 (sollte mit Python 2 funktionieren, aber ich habe es nicht getestet) und PyQt5.

Minimales Arbeitsbeispiel. Kopieren Sie es in die Python-Shell und erhalten Sie das Ergebnis.

from PyQt5.QtWidgets import QApplication
app = QApplication([])
screen = app.primaryScreen()
screenshot = screen.grabWindow(QApplication.desktop().winId())
screenshot.save('/tmp/screenshot.png')
rominf
quelle
Haben Sie die durchschnittliche Zeit für die Ausführung dieser Funktion? Nur Interesse, wenn es sich lohnt
DiCaprio
1
@ Mrenny 300 ms (für vollständigen Code), 165 ms (letzte drei Codezeilen).
Rominf
-3

Versuch es:

#!/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
quelle
3
Was ist das für ein Mist? Die Hälfte der Importe ist nutzlos, es gibt eine whileSchleife, die niemals beendet wird (und 1stattdessen verwendet True), if (pb != None):statt nur if pb:einige sinnlose Rohzeichenfolgen hat.
ArtOfWarfare