Hilfe: PyQt5 Code verbessern/optimieren

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
luemmel76
User
Beiträge: 22
Registriert: Freitag 26. Februar 2016, 17:42
Wohnort: Südhessen

Samstag 23. Februar 2019, 12:37

Hallo zusammen,

ich hatte mal wieder ne Idee, ein Programm mit Datenbank in dem ich meine Kinobesuche erfassen kann.
Die SQLite Datenbank war ja schnell erstellt mithilfe des SQLite Browser Programms.
Die DB enthält die Tabellen Filme, Kinos, Säle, Kategorien, Freunde und Besuche.

Angefangen hab ich mal mit der Anzeige der Filme-Einträge, die enthält den Titel, Kinostart in DE, Beschreibung und ein Bild.

Das laden und anzeigen des ersten Eintrages funktioniert auch schon mal, nur mit dem durchblättern komm ich jetzt nicht mehr weiter, mir scheint der generierte py-Code aus der ui-Datei ist nicht so optimal oder ich hab an den falschen Stellen den Code erweitert...

Wäre sehr nett wenn hier der ein oder andere nicht nur Tipps geben könnte sondern vielleicht auch einfach den Code mal in groben Zügen ändert.

Leider lässt sich hier ja keine Datei anhängen, daher hier der Aufbau der Tabelle Filme in der kino.db:

Code: Alles auswählen

CREATE TABLE "Filme" (
	"Film_ID"	INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE,
	"Titel"	TEXT,
	"Beschreibung"	TEXT,
	"Kinostart DE"	TEXT,
	"Plakat"	BLOB
);
Und hier meine main.py:

Code: Alles auswählen

# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'main.ui'
#
# Created by: PyQt5 UI code generator 5.12
#
# WARNING! All changes made in this file will be lost!

from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import QDate, QTime, QDateTime, Qt
import sqlite3

class Ui_Form(object):
    def open(self): # OPEN DATABASE
        try:
            conn = sqlite3.connect('kino.db')
            #cursor = conn.cursor()
            print(sqlite3.version)
        except sqlite3.Error as e:
            print("Failed connecting to database...")
        
        # Get last Row
        query = "SELECT * FROM Filme"
        self.cur = conn.cursor()
        self.cur.execute(query)
        last_row = self.cur.fetchall()
        print(len(last_row))
        
        # Start with first entry
        filmid = 0
        query = "SELECT * FROM Filme"
        c = conn.execute(query)
        result = c.fetchall()
        # 0 = erste ID [ID][Titel][Beschreibung][Kinostart][Poster]
        # print("Titel :", result[filmid][1])
        # print("Datum :", result[filmid][3])
        
        # # Date format
        # # now = QDateTime.currentDateTimeUtc()
        # now = QDateTime(2019, 2, 21, 20, 31, 16, 992)
        # unix_time = now.toSecsSinceEpoch()+3600 # +3600 = +1h
        # d = QDateTime.fromSecsSinceEpoch(unix_time)
        # print("Date Time: " ,d.toString("dd.MM.yyyy hh:mm:ss t"))
        # print("UnixTime: " ,unix_time)
        # print("now: " ,now)
        
        # Get column data
        filmtitel = result[filmid][1]
        filmbeschreibung = result[filmid][2]
        filmstart = result[filmid][3]
        poster_data = result[filmid][4]
        qimg = QtGui.QImage.fromData(poster_data)
        poster = QtGui.QPixmap.fromImage(qimg)
        eintrag = result[filmid][0]
        eintraege = len(last_row)
        
        # View column data
        ui.txt_title.setText(filmtitel)
        ui.txt_description.setText(filmbeschreibung)
        ui.txt_cinedate.setText(QDate.fromString(filmstart, "yyyy-MM-dd").toString("dd.MM.yyyy"))
        ui.view_poster.setPixmap(poster)
        ui.txt_entry.setText(str(eintrag))
        ui.txt_entrys.setText(str(eintraege))
        
        if str(eintrag) == '1':
            ui.btn_first.setEnabled(False)
            ui.btn_prev.setEnabled(False)
        else:
            ui.btn_first.setEnabled(True)
            ui.btn_prev.setEnabled(True)
        
        if str(eintrag) == str(eintraege):
            ui.btn_next.setEnabled(False)
            ui.btn_last.setEnabled(False)
        else:
            ui.btn_next.setEnabled(True)
            ui.btn_last.setEnabled(True)
    
    # def first(self):
        # filmid = 0
    
    # def prev(self):
        # filmid-1
    
    def next(self):
        # filmid + 1
        print("btn_next.clicked")
    
    # def last(self):
        # filmid = str(eintraege)
    
        # self.conn.close()
    
    def setupUi(self, Form):
        # FORM
        Form.setObjectName("Form")
        Form.setWindowTitle("Kinofilme")
        Form.resize(640, 480)
        # LABELS
        self.lbl_title = QtWidgets.QLabel(Form)
        self.lbl_title.setGeometry(QtCore.QRect(10, 10, 71, 16))
        self.lbl_title.setObjectName("lbl_title")
        self.lbl_title.setText("Titel")
        self.lbl_cinedate = QtWidgets.QLabel(Form)
        self.lbl_cinedate.setGeometry(QtCore.QRect(10, 40, 71, 16))
        self.lbl_cinedate.setObjectName("lbl_cinedate")
        self.lbl_cinedate.setText("Kinostart")
        self.lbl_description = QtWidgets.QLabel(Form)
        self.lbl_description.setGeometry(QtCore.QRect(10, 70, 71, 16))
        self.lbl_description.setObjectName("lbl_description")
        self.lbl_description.setText("Beschreibung")
        self.lbl_slash = QtWidgets.QLabel(Form)
        self.lbl_slash.setGeometry(QtCore.QRect(300, 440, 31, 20))
        self.lbl_slash.setAlignment(QtCore.Qt.AlignCenter)
        self.lbl_slash.setObjectName("lbl_slash")
        self.lbl_slash.setText("/")
        # FIELDS
        self.txt_title = QtWidgets.QTextBrowser(Form)
        self.txt_title.setGeometry(QtCore.QRect(90, 10, 261, 21))
        self.txt_title.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.txt_title.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.txt_title.setObjectName("txt_title")
        self.txt_cinedate = QtWidgets.QTextBrowser(Form)
        self.txt_cinedate.setGeometry(QtCore.QRect(90, 40, 81, 21))
        self.txt_cinedate.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.txt_cinedate.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.txt_cinedate.setObjectName("txt_cinedate")
        self.txt_description = QtWidgets.QTextBrowser(Form)
        self.txt_description.setGeometry(QtCore.QRect(10, 90, 341, 321))
        self.txt_description.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
        self.txt_description.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.txt_description.setObjectName("txt_description")
        self.view_poster = QtWidgets.QLabel(Form)
        self.view_poster.setScaledContents(True)
        self.view_poster.setGeometry(QtCore.QRect(365, 10, 261, 401))
        self.view_poster.setObjectName("view_poster")
        self.txt_entry = QtWidgets.QTextBrowser(Form)
        self.txt_entry.setGeometry(QtCore.QRect(250, 440, 51, 21))
        self.txt_entry.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.txt_entry.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.txt_entry.setObjectName("txt_entry")
        self.txt_entrys = QtWidgets.QTextBrowser(Form)
        self.txt_entrys.setGeometry(QtCore.QRect(330, 440, 51, 21))
        self.txt_entrys.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.txt_entrys.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.txt_entrys.setObjectName("txt_entrys")
        # BUTTONS
        self.btn_first = QtWidgets.QPushButton(Form)
        self.btn_first.setGeometry(QtCore.QRect(50, 440, 75, 23))
        self.btn_first.setObjectName("btn_first")
        self.btn_first.setText("Erster")
        self.btn_prev = QtWidgets.QPushButton(Form)
        self.btn_prev.setGeometry(QtCore.QRect(150, 440, 75, 23))
        self.btn_prev.setObjectName("btn_prev")
        self.btn_prev.setText("Vorheriger")
        self.btn_next = QtWidgets.QPushButton(Form)
        self.btn_next.setGeometry(QtCore.QRect(410, 440, 75, 23))
        self.btn_next.setObjectName("btn_next")
        self.btn_next.setText("Nächster")
        self.btn_last = QtWidgets.QPushButton(Form)
        self.btn_last.setGeometry(QtCore.QRect(510, 440, 75, 23))
        self.btn_last.setObjectName("btn_last")
        self.btn_last.setText("Letzter")
        
        self.open()
        
        # self.btn_first.clicked.connect(self.first)
        # self.btn_prev.clicked.connect(self.prev)
        self.btn_next.clicked.connect(self.next)
        # self.btn_last.clicked.connect(self.last)
        
        QtCore.QMetaObject.connectSlotsByName(Form)



if __name__ == "__main__":
    import sys
    app = QtWidgets.QApplication(sys.argv)
    Form = QtWidgets.QWidget()
    ui = Ui_Form()
    ui.setupUi(Form)
    Form.show()
    sys.exit(app.exec_())
Gruß
Thilo
Sirius3
User
Beiträge: 9709
Registriert: Sonntag 21. Oktober 2012, 17:20

Samstag 23. Februar 2019, 12:53

@luemmel76: Es ist unüblich aus ui-Dateien py-Code zu erzeugen und zu verändern, daher auch die Warnung ganz oben. Am besten lädt man ui-Dateien per loadui direkt.
Die "Fehlerverarbeitung" in ›open‹ ist Quatsch und kann weg. Nachdem der except-Block abgearbeitet ist, sollte das Programm wieder in einem Zustand sein, mit dem man weiter arbeiten kann. Bei Dir endet das aber nur in einem NameError zwei Zeilen später.

Der Kommentar »Get last Row« ist falsch und die Zeilen Code machen genau das selbe, wie der Code unter dem ebenfalls falschen Kommentar »Start with first entry«.

Das setzen der Einträge in »open« machen das ja nur zu dem Zeitpunkt, wo open abgearbeitet wird. Dass Du später irgendwie irgendwo filmid änderst, hat keinen Einfluß auf irgendwas.

Lösung ist, entweder alle Filme sich zu merken und die txt_xxx jeweils bei Änderung von filmid neu zu schreiben, oder gleich einen dieser Widgets von Qt zu nehmen, die eine Datenbankanbindung haben.
Benutzeravatar
__blackjack__
User
Beiträge: 3102
Registriert: Samstag 2. Juni 2018, 10:21

Samstag 23. Februar 2019, 16:50

@luemmel76: SQL beachtet keine Gross-/Kleinschreibung von Namen, ausser das einige DBMS das manchmal doch machen. Darum ist es sicherer einfach alle Namen ausschliesslich klein zu schreiben. Wenn man dann noch keine Leerzeichen in Spaltennamen verwendet, sondern nur Namen die auch in Python gültige und den Konventionen entsprechende Namen wären, macht man es sich für ORMs wie SQLAlchemy deutlich einfacher.

Da so eine CREATE-Anweisung genau *ein* ”Objekt” beschreibt und man in typischen Diagrammen wie Entity-Relationshipt- oder UML-Diagrammen das auch als ein einzelnes Exemplar sieht, würde ich die Tabelle(n) auch in der Einzahl benennen, also "Film" statt "Filme" (bzw. "film" meinen letzten Absatz folgend).

UNIQUE ist überflüssig, das steckt schon in PRIMARY KEY mit drin. Und willst Du wirklich AUTOINCREMENT bei SQLite? Warum?

Die "film_id" würde ich nur "id" nennen. Das es die vom Film ist, wird ja schon aus dem Tabellennamen klar. "film.film_id" ist redundant. "film.id" reicht aus.

Mindestens den "titel" würde ich ja noch NOT NULL deklarieren, eventuell auch die Beschreibung. Dein Code käme auch mit einem "kinostart_de" nicht klar der NULL ist. Sollte er vielleicht‽ Oder Alternativ sollte auch die Spalte als NOT NULL deklariert werden.

Was kann "kinostart_de" für Werte annehmen? Ich Tippe mal auf Datumsangeben? Dafür gibt es bessere Datentypen als TEXT. DATE zum Beispiel. (Da muss man dann bei dem `sqlite`-Modul auf eine Umwandlung zwischen der Datenbank und Python achten, oder einfach gleich SQLAlchemy verwenden, was das netterweise schon macht.)

Warum das "de" am Ende von "kinostart_de"? Wenn es da mehr Länder geben sollte, dann wäre das eher eine eigene Tabelle mit Kinostarts für verschiedene Filme + Länder.

Wenn es nur diesen einen Kinostart pro Film gibt, dann ist das "de" im Namen überflüssig.

Beim SELECT sollte man kein * angeben. Das macht den Code schwerer verständlich und fehleranfällig wenn Änderungen an den Feldern in der Datenbank vorgenommen werden. Besser explizit angeben welche Spalten selektiert werden sollen.

Das was Du irreführenderweise `filmid` nennst ist einfach nur ein Index in die Liste. Das muss nichts mit dem Inhalt von "film_id" in den Datensätzen zu tun haben. Die IDs müssen die nicht lückenlos vergeben sein, und selbst wenn sie es wären, garantiert ein SELECT ohne ORDER BY keinerlei Reihenfolge.
“In computing, invariants are ephemeral.” – Alan J. Perlis
luemmel76
User
Beiträge: 22
Registriert: Freitag 26. Februar 2016, 17:42
Wohnort: Südhessen

Montag 25. Februar 2019, 01:22

Danke euch beiden,

hab zwar nicht alles verstanden bzw. nachvollziehen können, auch bekomme ich die Beispiele in Qt Creator nicht ans laufen weil er dauernd bei den Kits hängenbleibt und die überfordern mich einfach.

Hab dann noch mal weiter gegoogelt und eine Seite gefunden die in einzelnen Kapiteln zeigt wie man so ein MainWindow aufbaut und mit Leben füllt. Das war dann die Basis für die jetzige Version die erst mal so funktioniert wie ich mir das vorgestellt habe. Der nächste Schritt ist dann z.B. Tabs einzubauen um in einem 2. Widget die DB weiter mit Inhalt zu füllen, statt über SQLiteBrowser.

hier der code der test.py (läuft momentan nur wenn mindestens 1 Datensatz manuell eingefügt wurde):

Code: Alles auswählen

#! /usr/bin/env python3
#  -*- coding:utf-8 -*-
###############################################################

import sys
from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import QCoreApplication, QDate
from PyQt5.QtGui import *
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QPushButton, QLineEdit, QLabel, QTextBrowser
import sqlite3

class Window(QMainWindow):

    def __init__(self):
        super(Window, self).__init__()
        self.resize(640, 480)
        self.setWindowTitle("Filme")
        self.setWindowIcon(QIcon('kino.png'))
        self.index = 1
        conn = sqlite3.connect('kino.db')
        conn.execute('CREATE TABLE IF NOT EXISTS film (id INTEGER PRIMARY KEY, titel TEXT NOT NULL, beschreibung TEXT, kinostart TEXT, poster BLOB)')
        self.cur = conn.cursor()
        self.cur.execute("SELECT COUNT(*) FROM film")
        count = self.cur.fetchall()
        # print((count)[0][0])
        self.index_last = count[0][0]
        self.home()


    def home(self):
        font = QFont()
        font.setBold(True)
        # LABELS
        self.lbl_title = QLabel(self)
        self.lbl_title.setGeometry(10, 10, 70, 23)
        self.lbl_title.setText("Titel")
        
        self.lbl_cinedate = QLabel(self)
        self.lbl_cinedate.setGeometry(10, 40, 70, 23)
        self.lbl_cinedate.setText("Kinostart")
        
        self.lbl_description = QLabel(self)
        self.lbl_description.setGeometry(10, 70, 70, 23)
        self.lbl_description.setText("Beschreibung")
        
        self.lbl_slash = QLabel(self)
        self.lbl_slash.setGeometry(200, 440, 30, 23)
        self.lbl_slash.setAlignment(QtCore.Qt.AlignCenter)
        self.lbl_slash.setText("/")
        # FIELDS
        self.txt_title = QLineEdit("", self)
        self.txt_title.setGeometry(90, 10, 261, 23)
        self.txt_title.setReadOnly(True)
        self.txt_title.setFont(font)        
        
        self.txt_cinedate = QLineEdit("", self)
        self.txt_cinedate.setGeometry(90, 40, 81, 23)
        self.txt_cinedate.setReadOnly(True)
                
        self.txt_description = QTextBrowser(self)
        self.txt_description.setGeometry(10, 90, 341, 321)
        self.txt_description.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
        self.txt_description.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.txt_description.setReadOnly(True)
                
        self.view_poster = QLabel(self)
        self.view_poster.setScaledContents(True)
        self.view_poster.setGeometry(365, 10, 261, 401)
        
        self.txt_entry = QLineEdit("1", self)
        self.txt_entry.setGeometry(150, 440, 50, 23)
        self.txt_entry.setAlignment(QtCore.Qt.AlignCenter)
        
        self.txt_entrys = QLineEdit(str(self.index_last), self)
        self.txt_entrys.setGeometry(230, 440, 50, 23)
        self.txt_entrys.setAlignment(QtCore.Qt.AlignCenter)
        # BUTTONS
        self.btn_first = QPushButton("|<", self)
        self.btn_first.setGeometry(10, 440, 50, 23)
        self.btn_first.setFont(font)
        self.btn_first.clicked.connect(self.first)
        
        self.btn_prev = QPushButton("<", self)
        self.btn_prev.setGeometry(80, 440, 50, 23)
        self.btn_prev.setFont(font)
        self.btn_prev.clicked.connect(self.prev)
        
        self.btn_next = QPushButton(">", self)
        self.btn_next.setGeometry(300, 440, 50, 23)
        self.btn_next.setFont(font)
        self.btn_next.clicked.connect(self.next)
        
        self.btn_last = QPushButton(">|", self)
        self.btn_last.setGeometry(370, 440, 50, 23)
        self.btn_last.setFont(font)
        self.btn_last.clicked.connect(self.last)
        
        btn_quit = QPushButton("Ende", self)
        btn_quit.setGeometry(580, 440, 50, 23)
        btn_quit.setFont(font)
        btn_quit.clicked.connect(self.close_application)
        
        self.button_state()
        self.query()
        
        self.show()

    def first(self):
        self.index = 1
        self.txt_entry.setText(str(self.index))
        self.button_state()
        self.query()

    def prev(self):
        self.index -= 1
        self.txt_entry.setText(str(self.index))
        self.button_state()
        self.query()

    def next(self):
        self.index += 1
        self.txt_entry.setText(str(self.index))
        self.button_state()
        self.query()

    def last(self):
        self.index = self.index_last
        self.txt_entry.setText(str(self.index))
        self.button_state()
        self.query()
        
        
    def button_state(self):
        if self.index <= 1:
            self.btn_first.setEnabled(False)
            self.btn_prev.setEnabled(False)
        else:
            self.btn_first.setEnabled(True)
            self.btn_prev.setEnabled(True)
        
        if self.index >= self.index_last:
            self.btn_next.setEnabled(False)
            self.btn_last.setEnabled(False)
        else:
            self.btn_next.setEnabled(True)
            self.btn_last.setEnabled(True)

    def query(self):
        self.cur.execute("SELECT * FROM film WHERE rowid = ?", (self.index,))
        result = self.cur.fetchone()
        title = result[1]
        descr = result[2]
        start = result[3]
        cinedate = QDate.fromString(start, "yyyy-MM-dd").toString("dd.MM.yyyy")
        img = result[4]
        qimg = QtGui.QImage.fromData(img)
        poster = QtGui.QPixmap.fromImage(qimg)
        # print("\n", start, "\n", title, "\n", descr, sep="")
        self.txt_title.setText(title)
        self.txt_cinedate.setText(cinedate)
        self.txt_description.setText(descr)
        self.view_poster.setPixmap(poster)
    
    def close_application(self):
        print("see you next time!!!")
        sys.exit()
    
def run():
    app = QApplication(sys.argv)
    GUI = Window()
    sys.exit(app.exec_())


run()
Gruß
Thilo
Benutzeravatar
__blackjack__
User
Beiträge: 3102
Registriert: Samstag 2. Juni 2018, 10:21

Montag 25. Februar 2019, 12:21

@luemmel76: Sternchenimporte sind Böse™. Du weisst nicht was Du Dir damit alles in das Modul holst, und damit ist es schwerer nachzuvollziehen wo was her kommt und es besteht die Gefahr von Namenskollisionen.

Das was Du da mit den Index versuchst wird nicht funktionieren. Auch bei der ROWID ist nicht garantiert das die bei 0 anfängt und ohne Lücken aufsteigend vergeben ist. Du wirst letztendlich nicht darum herum kommen eine Liste mit allen IDs abzufragen und im Speicher zu halten. Von der ROWID würde ich die Finger lassen, die Datensätze selbst haben ja eine ID und das ist auch Standard-SQL.

Cursor sind eigentlich etwas kurzlebiger gedacht. Du willst da eher die Verbindung merken und Cursor nur bei bedarf erstellen und nicht vergessen den nach gebrauch auch wieder zu schliessen. Am besten mit ``with`` und `functools.closing()`. Zudem würde ich mir auch bei Lesezugriffen ein `commit()` auf der Verbindung angewöhnen. Und beim Schliessen des Programms dann auch die DB-Ressources explizit freigeben. Also in Deinem Fall den Cursor schliessen, beziehungsweise wenn man Cursor nur für kurze Operationen erstellt, am Ende die Verbindung schliessen.

Apropos Ende: Das bitte nicht mit `sys.exit()` erzwingen, sondern die Anwendung normal beenden damit das `sys.exit()` in der `run()`-Funktion ausgeführt wird und Qt auch die Chance bekommt seine Aufräumarbeiten durchzuführen. Normales beenden bedeutet in der Regel einfach das letzte Fenster schliessen.

`home()` ist ein komischer Name für eine Methode die die GUI/den Fensterinhalt erstellt. Ich würde das zudem nicht in eine eigene Methode auslagern.

`setGeometry()` ist keine gute Idee. Damit funktioniert die GUI im Zweifelsfall nur auf dem Rechner auf dem Du das entworfen/getestet hast und kann bei anderen Einstellungen falsch aussehen oder sogar völlig unbrauchbar werden. Qt hat dafür Layout-Klassen, damit immer alle Widgets so angeordnet werden wie man das gerne hätte und das unabhängig von so Sachen wie Bildschirmauflösungen und Schriftgrössen.

Bei mir passt zum Beispiel der Platz für das Label „Beschreibung“ nicht und es wird abgeschnitten:
Bild

Bei der Abfrage der DB hast Du immer noch ein ``SELECT *``.

Ich würde um das Posterbild einen Rahmen setzen oder ein generisches Posterbild verwenden wenn in der DB kein Poster hinterlegt ist. Sonst fragt man sich warum da so viel freier Platz neben der Beschreibung ist.
“In computing, invariants are ephemeral.” – Alan J. Perlis
luemmel76
User
Beiträge: 22
Registriert: Freitag 26. Februar 2016, 17:42
Wohnort: Südhessen

Mittwoch 27. Februar 2019, 01:08

Hallo blackjack

habe jetzt einiges geändert/ergänzt:
- cur.close() und conn.commit() eingebaut
- keine SELECT * mehr drinn
- statt ROWID jetzt die ID
- gibt es noch keine DB, wird sie angelegt mit der Tabelle film und da es dann noch keine Einträge gibt ist der index = 0 und die Query wird nicht ausgeführt
- wenn Titel, Kinostart, Beschreibung oder Poster fehlen, werden Platzhalter angezeigt
- statt sys.exit() gibts jetzt ein self.conn.close() und self.close()

Bei der Layout-Geschichte bin ich noch nicht durchgestiegen...

und hier wieder der ganze code:

Code: Alles auswählen

#! /usr/bin/env python3
#  -*- coding:utf-8 -*-
###############################################################

import sys
from PyQt5 import QtCore, QtGui
from PyQt5.QtCore import QCoreApplication, QDate
from PyQt5.QtGui import *
from PyQt5.QtWidgets import QApplication, QWidget, QMainWindow, QPushButton, QLineEdit, QLabel, QTextBrowser, QFrame
import sqlite3

class Window(QMainWindow):

    def __init__(self):
        super(Window, self).__init__()
        self.resize(640, 480)
        self.setWindowTitle("Filme")
        self.setWindowIcon(QIcon('kino.png'))
        self.setAcceptDrops(True)
        # self.index = 1
        self.conn = sqlite3.connect('kino.db')
        self.conn.execute('CREATE TABLE IF NOT EXISTS film (id INTEGER PRIMARY KEY, titel TEXT NOT NULL, beschreibung TEXT, kinostart TEXT, poster BLOB)')
        self.cur = self.conn.cursor()
        self.cur.execute("SELECT COUNT(id) FROM film")
        count = self.cur.fetchall()
        self.index_last = count[0][0]
        
        if self.index_last is 0:
            self.index = 0
        else:
            self.index = 1
            
        self.cur.close()
        self.conn.commit()
        self.home()

    def home(self):
        font = QFont()
        font.setBold(True)
        # LABELS
        self.lbl_title = QLabel(self)
        self.lbl_title.setGeometry(10, 10, 80, 23)
        self.lbl_title.setText("Titel")
        
        self.lbl_cinedate = QLabel(self)
        self.lbl_cinedate.setGeometry(10, 40, 80, 23)
        self.lbl_cinedate.setText("Kinostart")
        
        self.lbl_description = QLabel(self)
        self.lbl_description.setGeometry(10, 70, 341, 23)
        self.lbl_description.setText("Beschreibung")
        
        self.lbl_slash = QLabel(self)
        self.lbl_slash.setGeometry(200, 440, 30, 23)
        self.lbl_slash.setAlignment(QtCore.Qt.AlignCenter)
        self.lbl_slash.setText("/")
        # FIELDS
        self.txt_title = QLineEdit("kein Film vorhanden", self)
        self.txt_title.setGeometry(90, 10, 261, 23)
        self.txt_title.setReadOnly(True)
        self.txt_title.setFont(font)        
        
        self.txt_cinedate = QLineEdit("leer", self)
        self.txt_cinedate.setGeometry(90, 40, 81, 23)
        self.txt_cinedate.setReadOnly(True)
                
        self.txt_description = QTextBrowser(self)
        self.txt_description.setText("keine Beschreibung vorhanden")
        self.txt_description.setGeometry(10, 90, 341, 321)
        self.txt_description.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOn)
        self.txt_description.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
        self.txt_description.setReadOnly(True)
                
        self.view_poster = QLabel("kein Bild vorhanden", self)
        self.view_poster.setScaledContents(True)
        self.view_poster.setGeometry(365, 10, 261, 401)
        self.view_poster.setAlignment(QtCore.Qt.AlignCenter)
        self.view_poster.setFrameShape(QFrame.StyledPanel)
        
        self.txt_entry = QLineEdit(str(self.index), self)
        self.txt_entry.setGeometry(150, 440, 50, 23)
        self.txt_entry.setAlignment(QtCore.Qt.AlignCenter)
        
        self.txt_entrys = QLineEdit(str(self.index_last), self)
        self.txt_entrys.setGeometry(230, 440, 50, 23)
        self.txt_entrys.setAlignment(QtCore.Qt.AlignCenter)
        # BUTTONS
        self.btn_first = QPushButton("|<", self)
        self.btn_first.setGeometry(10, 440, 50, 23)
        self.btn_first.setFont(font)
        self.btn_first.clicked.connect(self.first)
        
        self.btn_prev = QPushButton("<", self)
        self.btn_prev.setGeometry(80, 440, 50, 23)
        self.btn_prev.setFont(font)
        self.btn_prev.clicked.connect(self.prev)
        
        self.btn_next = QPushButton(">", self)
        self.btn_next.setGeometry(300, 440, 50, 23)
        self.btn_next.setFont(font)
        self.btn_next.clicked.connect(self.next)
        
        self.btn_last = QPushButton(">|", self)
        self.btn_last.setGeometry(370, 440, 50, 23)
        self.btn_last.setFont(font)
        self.btn_last.clicked.connect(self.last)
        
        btn_quit = QPushButton("Ende", self)
        btn_quit.setGeometry(580, 440, 50, 23)
        btn_quit.setFont(font)
        btn_quit.clicked.connect(self.close_application)
        
        self.button_state()
        
        if self.index is not 0:
            self.query()
        
        self.show()

    def first(self):
        self.index = 1
        self.txt_entry.setText(str(self.index))
        self.button_state()
        self.query()

    def prev(self):
        self.index -= 1
        self.txt_entry.setText(str(self.index))
        self.button_state()
        self.query()

    def next(self):
        self.index += 1
        self.txt_entry.setText(str(self.index))
        self.button_state()
        self.query()

    def last(self):
        self.index = self.index_last
        self.txt_entry.setText(str(self.index))
        self.button_state()
        self.query()
        
    def button_state(self):
        if self.index <= 1:
            self.btn_first.setEnabled(False)
            self.btn_prev.setEnabled(False)
        else:
            self.btn_first.setEnabled(True)
            self.btn_prev.setEnabled(True)
        
        if self.index >= self.index_last:
            self.btn_next.setEnabled(False)
            self.btn_last.setEnabled(False)
        else:
            self.btn_next.setEnabled(True)
            self.btn_last.setEnabled(True)

    def query(self):
        self.cur = self.conn.cursor()
        self.cur.execute("SELECT titel, beschreibung, kinostart, poster FROM film WHERE id = ?", (self.index,))
        result = self.cur.fetchone()
        title = result[0]
        descr = result[1]
        start = result[2]
        cinedate = QDate.fromString(start, "yyyy-MM-dd").toString("dd.MM.yyyy")
        img = result[3]
        qimg = QtGui.QImage.fromData(img)
        poster = QtGui.QPixmap.fromImage(qimg)
        
        if title is None:
            self.txt_title.setText("kein Film vorhanden")
        else:
            self.txt_title.setText(title)
        
        if start is None:
            self.txt_cinedate.setText("leer")
        else:
            self.txt_cinedate.setText(cinedate)
        
        if descr is None:
            self.txt_description.setText("keine Beschreibung vorhanden")
        else:
            self.txt_description.setText(descr)
        
        if img is None:
            self.view_poster.setText("kein Bild vorhanden")
        else:
            self.view_poster.setPixmap(poster)
            
        self.cur.close()
        self.conn.commit()
    
    def close_application(self):
        print("see you next time!!!")
        # sys.exit()
        self.conn.close()
        self.close()
    
def run():
    app = QApplication(sys.argv)
    GUI = Window()
    sys.exit(app.exec_())

run()
Gruß
Thilo
Sirius3
User
Beiträge: 9709
Registriert: Sonntag 21. Oktober 2012, 17:20

Mittwoch 27. Februar 2019, 08:22

Sternchenimporte sind böse™. Dateinamen sollten nicht irgendwo im Code stehen, sondern als Konstanten am Anfang. Relative Pfade sind relativ blöd, weil man das Programm dann von einem festen Verzeichnis aus starten muß. Wenn es nur ein Ergebnis geben kann, wie bei count, ist fetchall falsch, fetchone richtig.
`is` prüft Objektidentität und ist bei Zahlen der falsche Vergleich. Ein magischer Index 0 ist eigentlich auch gar nicht nötig, weil Du einfach bei der query-Abfrage prüfen kannst (und solltest), ob der Index im gültigen Bereich liegt. Das `commit` in __init__ ist unnötig, weil Du keine Änderungen an einer Tabelle vornimmst. Einfach immer irgendwo zur Sicherheit commit aufzurufen, würde ich sogar als Programmierfehler ansehen. Da `cur` ein kurzlebiges Objekt ist, ist es quatsch, das als Attribut zu speichern.
`home` ist ein komischer Name für eine Funktion, die ein Fenster aufbaut. Dass man das eigentlich über loadui macht, habe ich glaube ich schonmal geschrieben.
`first`, `prev`, `next` und `last` unterscheiden sich nur in einer Zeile und sollten zu einer Funktion zusammengefasst werden.
In `query` benutze Unpacking, um `result` auf Variablen aufzuteilen. `kinostart` sollte ein TIMESTAMP sein und kein String. Auch hier ist das `commit` unsinnig.
`run` sollte in einem `if __name__ == '__main__':`-Block aufgerufen werden.
Benutzeravatar
__blackjack__
User
Beiträge: 3102
Registriert: Samstag 2. Juni 2018, 10:21

Mittwoch 27. Februar 2019, 08:55

@Sirius3: Das mit `commit()` sehe ich anders. Das ist nicht nur für Veränderungen sondern auch bei Abfragen um zu sagen das die Transaktion zu ende ist. Das kann auch bei Leseoperationen relevant sein, weil solange die Transaktion läuft, ein DBMS das „repeatable read“ garantiert eventuell Ressourcen belegen muss um das zu garantieren, die freigegeben werden können sobald auch Leser bescheid sagen, dass sie fertig sind.
“In computing, invariants are ephemeral.” – Alan J. Perlis
luemmel76
User
Beiträge: 22
Registriert: Freitag 26. Februar 2016, 17:42
Wohnort: Südhessen

Mittwoch 27. Februar 2019, 13:34

Sternchenimporte sind böse™.
Fällt mir jetzt erst auf was ihr beiden damit meint, werd es noch korrigieren.
Dateinamen sollten nicht irgendwo im Code stehen, sondern als Konstanten am Anfang.
Lässt sich ja leicht ändern.
Relative Pfade sind relativ blöd, weil man das Programm dann von einem festen Verzeichnis aus starten muß.
Den Satz versteh ich nicht ganz, es wird im aktuellen Verzeichnis in dem die .py liegt nach der Datei geschaut, es ist also wurscht (m.M.) in welchem Verzeichnis die .py liegt solange die dazugehörigen Dateien ebenfalls dort liegen.
Wenn es nur ein Ergebnis geben kann, wie bei count, ist fetchall falsch, fetchone richtig.
Werd ich auch noch ändern.
`is` prüft Objektidentität und ist bei Zahlen der falsche Vergleich. Ein magischer Index 0 ist eigentlich auch gar nicht nötig, weil Du einfach bei der query-Abfrage prüfen kannst (und solltest), ob der Index im gültigen Bereich liegt.
Schau ich mir bei Gelegenheit an
Das `commit` in __init__ ist unnötig, weil Du keine Änderungen an einer Tabelle vornimmst. Einfach immer irgendwo zur Sicherheit commit aufzurufen, würde ich sogar als Programmierfehler ansehen.
Da scheiden sich wohl die (Programmier)geister, gibt es da ein allgemein gültiges Regelwerk in deutsch?
`home` ist ein komischer Name für eine Funktion, die ein Fenster aufbaut. Dass man das eigentlich über loadui macht, habe ich glaube ich schonmal geschrieben.
Wenn ich mal passende, einfache und vor allem funktionierende Beispiele finde, probiere ich es umzusetzen.
`first`, `prev`, `next` und `last` unterscheiden sich nur in einer Zeile und sollten zu einer Funktion zusammengefasst werden.
Und wie, oder macht man das mit If, Elif und Else ?
In `query` benutze Unpacking, um `result` auf Variablen aufzuteilen.
Schaue ich mir an wie das geht.
`kinostart` sollte ein TIMESTAMP sein und kein String. Auch hier ist das `commit` unsinnig.
Wollte es ja ursprünglich schon als unixtimestamp machen, hatte aber zuerst die kino.db mit der film-Tabelle im SQLiteBrowser aufgebaut und schon reichlich mit Daten gefüttert, dabei war es für mich als Mensch einfacher das Datum im YYYY-MM-DD Format einzugeben.
`run` sollte in einem `if __name__ == '__main__':`-Block aufgerufen werden.
Ok, ändere ich noch.


Gruß
Thilo
__deets__
User
Beiträge: 5359
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mittwoch 27. Februar 2019, 14:46

Du glaubst nur, das die Dateien relativ zur .py-Datei gesucht werden. Das stimmt nicht. Sie werden relativ zum working directory des Prozesses gesucht. Wenn du dich beim Programmstart *in* dem Verzeichnis befindest, in dem die .py-Datei liegt, klappts.
Wenn du aber zB

Code: Alles auswählen

cd c:\irgend\ein\pfad
c:\pfad\zum\skript.py  # absoluter Pfad
zum starten benutzt, dann kracht es.
luemmel76
User
Beiträge: 22
Registriert: Freitag 26. Februar 2016, 17:42
Wohnort: Südhessen

Mittwoch 27. Februar 2019, 15:53

__deets__ hat geschrieben:
Mittwoch 27. Februar 2019, 14:46
Du glaubst nur, das die Dateien relativ zur .py-Datei gesucht werden. Das stimmt nicht. Sie werden relativ zum working directory des Prozesses gesucht. Wenn du dich beim Programmstart *in* dem Verzeichnis befindest, in dem die .py-Datei liegt, klappts.
Wenn du aber zB

Code: Alles auswählen

cd c:\irgend\ein\pfad
c:\pfad\zum\skript.py  # absoluter Pfad
zum starten benutzt, dann kracht es.
Ach so ist das zu verstehen, so habe ich da jetzt nicht gedacht, rufe das ganze ja direkt in der working directory zum testen auf bzw. starte zukünftig die mit pyinstaller erstellte exe.
__deets__
User
Beiträge: 5359
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mittwoch 27. Februar 2019, 16:53

Und gerade bei lezterem hast du darueber keine Kontrolle, und musst *unbedingt* die Pfade zusammenbauen, zB mit pathlib:

Code: Alles auswählen

resource = pathlib.Path(__file__).parent / "resource.datei"
Antworten