screenshot als Bild in PDF speichern

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das was du als Screenshot bezeichnest ist ein Python Objekt. Aus dem Modul pyautogui. Das damit ein anderes Modul nicht viel anfangen kann, ist nicht weiter verwunderlich. Dein Auto kann wahrscheinlich auch nix mit dem Motor des Nachbarn anfangen.

Womit wer anders etwas anfangen kann, sind generische Bildformate. Das KANN PNG sein. Oder auch RGB (zb als numpy Array). Man muss also mindestens einen Weg finden, das Objekt in eines dieser Formate zu überführen.

Und dann kommt es noch darauf an, wie die Bibliothek ein genehmes Format annimmt. Es kann sein, das es nur ein Dateipfad ist. Dann kommst du ums Speichern nicht herum. Oder es erlaubt (in Python oft) die Angabe als „file like object“. Also etwas, das sich so verhält, wie eine Datei. Und da kommt dann io.BytesIO zum tragen. Damit „simuliert“ Python eine Datei im Speicher.

Du kannst also versuchen, das Screenshot objekt in ein BytesIO Objekt zu „speichern“ - dann ist es aber nur im Hauptspeicher. Und das dann zu „laden“ mit der PDF Bibliothek.
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das was du als Screenshot bezeichnest ist ein Python Objekt. Aus dem Modul pyautogui. Das damit ein anderes Modul nicht viel anfangen kann, ist nicht weiter verwunderlich. Dein Auto kann wahrscheinlich auch nix mit dem Motor des Nachbarn anfangen.

Womit wer anders etwas anfangen kann, sind generische Bildformate. Das KANN PNG sein. Oder auch RGB (zb als numpy Array). Man muss also mindestens einen Weg finden, das Objekt in eines dieser Formate zu überführen.

Und dann kommt es noch darauf an, wie die Bibliothek ein genehmes Format annimmt. Es kann sein, das es nur ein Dateipfad ist. Dann kommst du ums Speichern nicht herum. Oder es erlaubt (in Python oft) die Angabe als „file like object“. Also etwas, das sich so verhält, wie eine Datei. Und da kommt dann io.BytesIO zum tragen. Damit „simuliert“ Python eine Datei im Speicher.

Du kannst also versuchen, das Screenshot objekt in ein BytesIO Objekt zu „speichern“ - dann ist es aber nur im Hauptspeicher. Und das dann zu „laden“ mit der PDF Bibliothek.
UniversalBastler
User
Beiträge: 17
Registriert: Samstag 1. August 2020, 12:08

Sorry Leute,
bei stackoverflow habe ich den Hinweis gefunden und wie ich es mir schon gedacht habe ist die Lösung ganz einfach.

Ich weise mit

screenshot1 = pyautogui.screenshot("seiteL.png" ,region=(80,48, 880, 960))

gleich den Namen (seiteL.png) zu und dann mit

pdf.image("seiteL.png" ,10 ,40, 200)

schreibe ich den screenshot auf die PDF Seite.
Ich brauche nichts mehr zwischen zu speichern und alles ist gut.
Nochmals vielen Dank

Nachfolgend für alle die es interessiert der vereinfachte Teilcode

Code: Alles auswählen

import pyautogui
from time import sleep
from fpdf import FPDF 

pdf = FPDF()#PDF wird erstellt

screenshot1 = pyautogui.screenshot("seiteL.png" ,region=(80,48, 880, 960))#screeshot der ersten Seite (linke Hälfte)
sleep(1) #warten bist erledigt

pdf.add_page() #Seite wird dem PDF zugefügt
pdf.image("seiteL.png" ,10 ,40, 200) #screenshot (jetzt seiteL.png) wird auf der PDF Seite positioniert

sleep(1)

pdf.output("PDF-Test3.pdf", "F") #fertiges PDF wird gespeichert
print("Fertig")
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Dann speichert pyautogui halt direkt. Es ist aber immer noch zwischengespeichert. Und das PNG fliegt irgendwo rum.
Sirius3
User
Beiträge: 18260
Registriert: Sonntag 21. Oktober 2012, 17:20

Du speicherst ja trotzdem zwischen, auch wenn das nun die Funktion screenshot direkt für dich erledigt. Aber wenn du meinst, dass das dir Lösung ist, die du gesucht hast, dann ist ja gut. Jetzt muss nur noch der überflüssige Code weg:

Code: Alles auswählen

import pyautogui
from fpdf import FPDF 

pdf = FPDF()

pyautogui.screenshot("seiteL.png" ,region=(80,48, 880, 960))#screeshot der ersten Seite (linke Hälfte)

pdf.add_page()
pdf.image("seiteL.png" ,10 ,40, 200) #screenshot (jetzt seiteL.png) wird auf der PDF Seite positioniert

pdf.output("PDF-Test3.pdf", "F") #fertiges PDF wird gespeichert
print("Fertig")
UniversalBastler
User
Beiträge: 17
Registriert: Samstag 1. August 2020, 12:08

Okay, das mit dem Zwischenspeichern war vielleicht von mir falsch ausgedrückt.
Natürlich muss der screenshot bzw. das Objekt oder Bild irgendwo im Speicher landen sonst könnte man es ja nicht in die PDF Schreiben.
Es ging aber darum, das nicht jedes mal auf die Festplatte zu schreiben.

@Sirius3
vielen Dank für die weitere Bereinigung des Codes. Zumindest das erste sleep(1) hätte ich gedacht, das es gebraucht wird weil angeblich der screenshot durchaus mehrere hundert Millisekunden brauchen kann. Aber es scheint auch so zu gehen :D
Dass ich die Variable screenshot1 gar nicht brauche erscheint mir (hinterher) auch klar.
Auf jeden Fall wieder was dazu gelernt :geek:
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du *schreibst* das doch jetzt auf die Festplatte. Du instruierst pyautogui das fuer dich zu machen, aber das ist auch der einzige Unterschied. Das wird *immer* noch auf die Platte geschrieben. Und vor allem auch in einen relativen Pfad, das heisst, dass die Datein irgendwo landen - und nicht kontrolliert zB im TEMP-Verzeichnis. Wenigstens dahingehend wuerde ich das Programm noch ueberarbeiten.
UniversalBastler
User
Beiträge: 17
Registriert: Samstag 1. August 2020, 12:08

Du *schreibst* das doch jetzt auf die Festplatte.
Ich habe es gerade auch festgestellt. :o
Aber das macht dann doch pyautogui.screenshot() selbstständig oder?
Na gut, dann soll es halt so sein :cry:
Wo das hingeschrieben wird habe ich im Griff, da kann ich einfach den Pfad mit angeben.
Sirius3
User
Beiträge: 18260
Registriert: Sonntag 21. Oktober 2012, 17:20

Wie schon geschrieben, muss man tief in die Klasse eingreifen, um das zu ändern.

Code: Alles auswählen

import pyautogui
from io import BytesIO
from fpdf import FPDF

class Fpdf(FPDF):
    def load_resource(self, reason, file):
        if hasattr(file, "read"):
            return file
        return FPDF.load_resource(self, reason, file)

pdf = Fpdf()
image = BytesIO()
screenshot1 = pyautogui.screenshot(region=(80,48, 880, 960))#screeshot der ersten Seite
screenshot1.save(inage, fornat="png")
image.seek(0)
pdf.add_page() #Seite wird dem PDF zugefügt
pdf.image(image,10, 40, 200, type="png") #screenshot wird auf der PDF Seite positioniert

pdf.output("PDF-Test3.pdf", "F") #fertiges PDF wird gespeichert
print("Fertig")
[/quote]
UniversalBastler
User
Beiträge: 17
Registriert: Samstag 1. August 2020, 12:08

Sorry, soweit bin ich noch nicht.
Trotzdem Danke :mrgreen:
Benutzeravatar
noisefloor
User
Beiträge: 4186
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

ReportLab ist mächtiger, weil es (viel) mehr Möglichkeiten hat. Aber deswegen ist es nicht wirklich komplizierter:

Code: Alles auswählen

>>> import pyautogui
>>> from reportlab.platypus import SimpleDocTemplate, Image
>>> from reportlab.lib.pagesizes import A4
>>> from io import BytesIO
>>> image = BytesIO()
>>> screenshot1 = pyautogui.screenshot(region=(80,48, 200, 200))
>>> screenshot1.save(image, format='png')
>>> doc = SimpleDocTemplate("image.pdf", pagesize=A4)
>>> story=[]
>>> story.append(Image(image))
>>> doc.build(story)
Das PDF mit den Screenshot ist dann im Verzeichnis, wo das Skript liegt, als "image.pdf" gespeichert.

Gruß, noisefloor
Benutzeravatar
__blackjack__
User
Beiträge: 14020
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@noisefloor: Kleine Korrektur: Das PDF ist dann im aktuellen Arbeitsverszeichnis gespeichert. Ob das das gleiche ist in dem das Skript liegt, hängt davon ab wie/von wo man das startet.
„A life is like a garden. Perfect moments can be had, but not preserved, except in memory. LLAP” — Leonard Nimoy's last tweet.
UniversalBastler
User
Beiträge: 17
Registriert: Samstag 1. August 2020, 12:08

Hallo,
Vielen Dank aber ihr bringt mich an meine Grenzen :oops:
Also mit BytesIO und reportlap muss ich mich dann doch mal beschäftigen.
Ich bin eigentlich davon ausgegangen, dass mit screenshot1.save(image, format='png') das png Bild auf die Festplatte gespeichert wird, finde es aber nicht.
Oder liegt das an dem BytesIO ? Gut, muss ich mich erst einarbeiten.
Das reportlab muss ich auch tiefer eintauchen, das Bild (Screenshot eines halben Bildschirm's) kann sicher auch hier so skaliert werden, dass es auf eine A4 Seite passt.
Benutzeravatar
noisefloor
User
Beiträge: 4186
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Ich bin eigentlich davon ausgegangen, dass mit screenshot1.save(image, format='png') das png Bild auf die Festplatte gespeichert wird, finde es aber nicht.
Tut's auch. Das Bild sollte da liegen, wo das aktuelle Arbeitsverzeichnis des Skripts ist.
Oder liegt das an dem BytesIO ? Gut, muss ich mich erst einarbeiten.
Nein. Mit BytesIO erzeugst du ein "file-like object", also ein Objekt, was sich wie eine Datei verhält (aber nicht unbedingt eine ist). Mit BytesIO sparst du dir den Zwischenschritt des Speicherns auf ein Laufwerk, das Bild bzw. die "Datei" wird im Speicher gehalten.
Das reportlab muss ich auch tiefer eintauchen, das Bild (Screenshot eines halben Bildschirm's) kann sicher auch hier so skaliert werden, dass es auf eine A4 Seite passt.
Ja, aber nicht über ReportLab. Die Platypus.Image Klasse kennt keine Größenangaben. Das musst du vorher machen. PyAutoGUI.screenshot liefert dir ja ein PIL Objekt, das hat Methoden, um das Bild zu skalieren:

Code: Alles auswählen

>> from pyautogui import screenshot
>>> image = screenshot()
>>> image
<PIL.PngImagePlugin.PngImageFile image mode=RGB size=1366x768 at 0x7F6286F1C610>
>>> image.size
(1366, 768)
>>> image = image.resize((800, 450))
>>> image.size
(800, 450)
Gruß, noisefloor
Sirius3
User
Beiträge: 18260
Registriert: Sonntag 21. Oktober 2012, 17:20

@UniversalBastler: image ist ein BytesIO-Objekt, `save` speichert also nur was im Speicher und nicht auf Festplatte. Das ist doch das, was Du möchtest.

@noisefloor: resize verändert doch nur die Pixelanzahl und hat erstmal nichts damit zu tun, wie groß das Bild dann auf der PDF-Seite dargestellt wird.
Benutzeravatar
noisefloor
User
Beiträge: 4186
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

was ich oben bzgl. des Skalierens in `platypus.Image`gesagt habe ist auch nicht korrekt. Die Objekt-Klasse vom Image kennt Größenangaben:

Code: Alles auswählen

>>> story.append(Image(image, 800, 600))
skaliert das Bild direkt. Wobei man auch direkt auf eine bestimmte Größe in z.B. cm skalieren kann:

Code: Alles auswählen

...
from reportlab.lib.units import cm
from reportlab.platypus import Image
...
story.append(Image(image, 5*cm, 3*cm))
...
würde das Bild auf 5x3cm im PDF skalieren.

Gruß, noisefloor
UniversalBastler
User
Beiträge: 17
Registriert: Samstag 1. August 2020, 12:08

skaliert das Bild direkt. Wobei man auch direkt auf eine bestimmte Größe in z.B. cm skalieren kann:
Danke, das hilft mir ein ganzes Stück weiter.
In Verbindung mit bytesIO habe ich mein Programm jetzt fast komplett umgeschrieben und es funktioniert bis hierhin.
Jetzt muss ich nur noch beide Bildschirmhälften nacheinander scannen, und das in eine Schleife einbauen damit es mehrere Bildschirme/Seiten nacheinander bearbeitet.
Dazu muss ich aber erst in reportlab etwas tiefer eintauchen. Ich habe mich schon mal umgeschaut, aber nicht wirklich viel gefunden was ich mit meinen nur rudimentären Englischkenntnissen problemlos erfassen könnte. Hat jemand eine Quelle, ein Buch oder eine Webseite wo reportlab gut und möglichst in deutsch beschrieben ist? fpdf habe ich zum Beispiel im Buch "Routineaufgaben mit Python automatisieren" kennengelernt und daher auch anfangs eingesetzt.

P.S. wiki.ubuntuusers.de/ReportLab/ kenne ich natürlich.
Benutzeravatar
noisefloor
User
Beiträge: 4186
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

also ich kenne kein Buch. Die Doku von ReportLab ist sehr umfangreich und es gibt im Netz sehr sehr viele Beispiele und Codeschnipsel zu Reportlab.

Platypus, was ja die "high level" API zum Bauen von PDFs mit ReporLab ist, nutzt man normalerweise so, dass man eine Liste (in vielen Beispielen "story" benannt) hat, in die man alle Elemente, die das PDF haben soll, packt. "Elemente" sind hier die Klassen, die Platypus so kennt, also Paragraphen, Bilder, etc. Die Elemente sind im Jargon von Platypus als "Flowables" bezeichnet.

Ein 2-seitiges PDF baust du so:

Code: Alles auswählen

>>> import pyautogui
>>> from reportlab.platypus import SimpleDocTemplate, Image, PageBreak
>>> from reportlab.lib.pagesizes import A4
>>> from io import BytesIO
>>> image_left = BytesIO()
>>> image_right = BytesIO()
>>> screenshot_left = pyautogui.screenshot(region=(80,48, 200, 200))
>>> screenshot_right = pyautogui.screenshot(region=(300,300, 200, 200))
>>> screenshot_left.save(image_left, format='png')
>>> screenshot_right.save(image_rigth, format='png')
>>> doc = SimpleDocTemplate("image.pdf", pagesize=A4)
>>> story=[]
>>> story.append(Image(image_left, 150, 150))
>>> story.append(PageBreak())
>>> story.append(Image(image_right, 150, 150))
>>> doc.build(story)
Den `PageBreak`kannst / solltest du sicherheitshalber einbauen, damit garantiert eine da ist. enn Das 1. Bild so groß ist, dass das 2. Bild nicht mehr auf die 1. Seite des PDFs passt, dann wird natürlich automatisch ein Seitenumbruch eingebaut.

Den obigen, linearen Code solltest du dann natürlich sinnvoll in Funktionen packen, wenn du Screenshots von mehreren Bildschirmen brauchst.

Gruß, noisefloor
Sirius3
User
Beiträge: 18260
Registriert: Sonntag 21. Oktober 2012, 17:20

@noisefloor: statt Code zu kopieren und nur leicht zu ändern, schreibt man sich eine Funktion. Bei einer fixen Anzahl an Elementen für eine Liste schreibt man diese gleich in die eckigen Klammern:

Code: Alles auswählen

import pyautogui
from reportlab.platypus import SimpleDocTemplate, Image, PageBreak
from reportlab.lib.pagesizes import A4
from io import BytesIO

def create_screenshot(region):
    screenshot = pyautogui.screenshot(region=region)
    image = BytesIO()
    screenshot.save(image, format='png')
    return Image(image, 150, 150)

story = [
    create_screenshot(region=(80,48, 200, 200)),
    PageBreak(),
    create_screenshot(region=(300,300, 200, 200)),
]
doc = SimpleDocTemplate("image.pdf", pagesize=A4)
doc.build(story)
Benutzeravatar
noisefloor
User
Beiträge: 4186
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

@Sirius3: genau das habe ich ja auch im letzten Satz im vorherigen Post geschrieben... Basierend auf dem, was der TE geschrieben hat, kann es durchaus auch Sinn machen, das Bauen des PDFs in eine Funktion zu packen.

Gruß, noisefloor
Antworten