jpg in Postgres Datenbank zu QImage

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
myoggradio
User
Beiträge: 14
Registriert: Freitag 20. November 2020, 12:24

Guten Tag,
ich habe Bilder im jpg format in einer Postgres Datenbank als bytea abgespeichert.
Wie bekommt man nun diese Daten in eine QImage um sie anzeigen zu können?
Ausprobiert habe ich folgendes, aber es kommt:
TypeError: QImage(): argument 1 has unexpected type 'memoryview'

Code: Alles auswählen

    def listener(self):
        row = self.currentRow()
        name = self.kommentare[row][0]
        print(name)
        content = []
        try:
            connection = psycopg2.connect(user = "user",
                                  password = "xxx",
                                  host = "115.115.115.115",
                                  port = "5432",
                                  database = "scanner")

            cursor = connection.cursor()
            sql = "select content from images"
            sql += " where name = %s"
            cursor.execute(sql,[name])
            records = cursor.fetchall()
            for row in records:
                content.append(row)   
        except (Exception, psycopg2.Error) as error :
            print ("Error PostgreSQL", error)
        finally:
            if(connection):
                cursor.close()
                connection.close()
        bild = content[0][0]  
        self.b = Bild(bild)
        self.b.show()
class Bild(QWidget):
    def __init__(self,bild):
        QWidget.__init__(self)
        self.bild = bild 
        self.initUI()
    def initUI(self):
        self.sid = QImage(self.bild)
        self.w = self.sid.width()
        self.h = self.sid.height()
        self.setGeometry(50,50,600,900)
        self.setWindowTitle("Bild")
    def paintEvent(self,event):
        painter = QPainter()
        painter.begin(self)
        self.drawBild(painter)
        painter.end()
    def drawBild(self,painter):
        painter.drawImage(5,5,self.sid)   

__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Deine Fehlerbehandlung ist Fehlerhaft. Wenn du einen Datenbankfehler bekommst, veruchst du danach trotzdem ein Bild-Objekt zu erzeugen. Das ist zwar nicht ursaechlich fuer das Problem hier, aber Grund fuer spaetere Probleme.

Du bindest auch zu viele Attribute an deine Bild-Klasse. Die Daten brauchst du nach dem erzeugen von QImage nicht mehr, aber die leben als self.bild munter weiter. Stattdessen solltest du

- initUI wegwerfen.
- die darin enthaltenen Statements direkt in __init__ platzieren
- bild (was besser bild_daten hiesse) einfach direkt verwenden

Ohne es jetzt selbst auszuprobieren ist es ein bisschen schwierig, dir da verlaesslich etwas zu sagen, aber ich wuerde raten, mal ein QImage zu erzeugen, und dann eine der Methoden hier https://doc.qt.io/qt-5/qimage.html#loadFromData zu verwenden. Falls das immer noch Typfehler wirft, dann mal das memoryview-Objekt zu einem bytes-Objekt zu wandeln. https://docs.python.org/3/library/stdty ... ew.tobytes
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Der ``finally``-Zweig ist auch Murks. ``if connection`` ist immer wahr beziehungsweise ein `NameError` falls `connection` überhaupt nicht existiert wenn der Verbindungsaufbau zu einer Ausnahme geführt hat. Und falls die Verbindung existiert ist dadurch nicht garantiert, dass `cursor` existiert. Das kann da auch zu einem `NameError` führen.

Am besten macht man das mit ``with`` und `contextlib.closing()`.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
myoggradio
User
Beiträge: 14
Registriert: Freitag 20. November 2020, 12:24

Die loadFromData Methoden funktionieren bei mir nicht. Bin wohl zu dusselig.
Habe jetzt die Daten in eine Datei geschrieben und sie von da aus ins QImage befördert.
Das klappt wenigstens.

Code: Alles auswählen

class Bild(QWidget):
    def __init__(self,bild):
        QWidget.__init__(self)
        with open("temp.jpg","wb") as file:
            file.write(bild)
            file.close()
        self.sid = QImage("temp.jpg").scaled(595,842)
        self.setGeometry(50,50,600,900)
        self.setWindowTitle("Bild")
    def paintEvent(self,event):
        painter = QPainter()
        painter.begin(self)
        painter.drawImage(5,5,self.sid)   
        painter.end()
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Durch das with-statement brauchst du kein close. Und du solltest deine temporaere Datei mit dem tempfile-Modul erstellen, sonst bekommst du ggf. Probleme.
Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@myoggradio: Was heisst ”funktionieren nicht”? Bei mir funktioniert das. Auch mit einem `memoryview()`. Was kann man da denn falsch machen bei dem Methodenaufruf?

Code: Alles auswählen

#!/usr/bin/env python3
from pathlib import Path

from PyQt5.QtGui import QImage


def main():
    image_data = memoryview(Path("test.jpg").read_bytes())

    image = QImage()
    if not image.loadFromData(image_data):
        raise ValueError("Can't load image from data.")
    
    print(image.size())


if __name__ == "__main__":
    main()
Ausgabe:

Code: Alles auswählen

PyQt5.QtCore.QSize(1920, 1080)
Was zu dem "test.jpg" passt was ich da verwendet habe.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
myoggradio
User
Beiträge: 14
Registriert: Freitag 20. November 2020, 12:24

@__blackjack__
Habe deinen Code eingebaut. Jetzt funktioniert es auch bei mir. Kann gar nicht mehr sagen, was ich da falsch gemacht hatte.

Jedenfalls vielen Dank Euch beiden für Eure Mühe :-)
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

Noch Anmerkungen zur Datenbankabfrage:
host und port und password gehören nicht mitten in einen Listener, sondern als Konstanten an den Anfang der Datei. Das Erzeugen einer Connection am besten in eine Funktion auslagern.
fetchall liefert als Ergebnis schon eine Liste, das mit einer for-Schleife nochmal in eine Liste umzuwandeln ist unnötig.
Wenn man aber nur ein Element von der SQL-Abfrage erwartet, ist fetchone das richtige.

Code: Alles auswählen

DB_USER = "user"
DB_PASSWORD = "xxx"
DB_HOST = "115.115.115.115"
DB_PORT = "5432"
DB_DATABASE = "scanner"

def get_db_connection():
    return psycopg2.connect(user=DB_USER, password=DB_PASSWORD,
        host=DB_HOST, port=DB_PORT, database=DB_DATABASE)

[...]
        try:
            with contextlib.closing(get_db_connection()) as connection:
                with contextlib.closing(conncetion.cursor()) as cursor:
                    cursor.execute("SELECT content FROM images WHERE name = %s", [name])
                    content = cursor.fetchone()
        except Exception as error:
            print("Error PostgreSQL", error)
        else:
            if content is None:
                print("Bild nicht gefunden")
            else:
                bild = content[0]
[...]
Antworten