PyQt6 QSqlDatbase - Verbindung

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
schloss5020
User
Beiträge: 4
Registriert: Dienstag 30. November 2021, 09:54

Ich will ein Programm auch in einem anderen Programm aufrufen, das die selbe Datenbank Sqlite benützt. Wenn ich nun einen eigenen Verbindungsname angebe, sehe ich keine Daten.
Wenn ich dieses Programm aus einem anderen Programm aufrufe, funktioniert es.
Ich bin ratlos

Code: Alles auswählen

import sys
import platform
import PyQt6.QtWidgets as qw
import PyQt6.QtSql as qs
import PyQt6.QtCore as qc
from PyQt6 import uic
import artikelart
__version__ = "1.0.0"

class Fenster(qw.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("Artikel")
        self.ui=uic.loadUi("Artikelstamm.ui", self)
        self.isModal()
        self.datenbank()
        self.modell()
        self.tabelle()
        self.zeileneu.clicked.connect(self.neuezeile)
        self.zeileloesch.clicked.connect(self.loeschen)
        self.teileart.clicked.connect(lambda:self.artikelartdialog("Teileart"))
        self.materialart.clicked.connect(lambda:self.artikelartdialog("Materialart"))
        self.materialform.clicked.connect(lambda:self.artikelartdialog("Materialform"))
        self.action_information.triggered.connect(self.helpAbout)
        self.beenden.clicked.connect(self.close)

    def datenbank(self):
        self.db = qs.QSqlDatabase.addDatabase('QSQLITE', "con1")
        self.db.setDatabaseName("Stücklisten.db")
        self.db.database("con1")
        if not self.db.open():
            error = self.db.lastError().text()
            qw.QMessageBox.critical(None, 'Datenbakverbindungsfehler',
                                    'Kann die Datenbank nicht öffnen: '
                                    f'{error}')
            sys.exit(1)
        required_tables = {'Teileart','Teilestamm','Materialart'}
        missing_tables = required_tables - set(self.db.tables())
        if missing_tables:
            qw.QMessageBox.critical(
                None, 'DB Fehler',
                'Tabellen fehlen, bitte reparieren Sie die DB: '
                f'{missing_tables}')
            sys.exit(1)

    def modell(self):
        self.model=qs.QSqlRelationalTableModel()
        self.model.setTable("Teilestamm")
        self.model.setRelation(2, qs.QSqlRelation('Teileart', 'Id', 'Teileartbezeichnung'))
        self.model.setRelation(3, qs.QSqlRelation('Materialart', 'Id', 'Materialbezeichnung'))
        self.model.setRelation(4, qs.QSqlRelation('Materialform', 'Id', 'Materialform'))
        self.model.select()

    def tabelle(self):
        self.tableView.setModel(self.model)
        self.tableView.setItemDelegate(qs.QSqlRelationalDelegate(self.tableView))
        stylesheet = "QHeaderView::section {Background-color:rgb(235,235,235);color:rgb(0,0,0); font: 15px}"
        self.tableView.setStyleSheet(stylesheet)
        self.tableView.setSortingEnabled(True)
        self.tableView.resizeColumnsToContents()
        self.tableView.verticalHeader().hide()
        # Sortieren nach Artikelnummer
        self.tableView.sortByColumn(0,qc.Qt.SortOrder.AscendingOrder)
        self.label=qw.QLabel()
        self.statusBar.addWidget(self.label)
        self.label.setText("Anzahl Sätze: " + str(self.model.rowCount()))

    def neuezeile(self):
        self.model.insertRow(self.tableView.currentIndex().row())
        self.label.setText("Anzahl Sätze: " + str(self.model.rowCount()))
    
    def loeschen(self):
        teilebezeichnung = self.model.record(self.tableView.currentIndex().row()).value(1)
        msg = qw.QMessageBox.warning(self, "Sicherheitsabfrage",
                    "Soll der Teilestamm " + teilebezeichnung + " wirklich geloescht werden?", qw.QMessageBox.StandardButton.Yes |
                    qw.QMessageBox.StandardButton.No)
        if msg == qw.QMessageBox.StandardButton.Yes: 
            self.model.removeRow(self.tableView.currentIndex().row())
            self.model.select()
            self.label.setText("Anzahl Sätze: " + str(self.model.rowCount()))
    def artikelartdialog(self, tabel):
        artikel = artikelart.Fenster(tabel)
        artikel.exec()
    def helpAbout(self):
        qw.QMessageBox.about(self, "Über Artikelstamm",
                """<b>Artikelstamm</b> v %s
                <p>Copyright &copy; 08.06.202 muster.
                All rights reserved.
                <p>Dies ist eine einfache
                Artikelstamm, Stücklistenverwaltung
                <p>Python %s - Qt %s - PyQt %s on %s""" % (
                __version__, platform.python_version(),
                qc.QT_VERSION_STR, qc.PYQT_VERSION_STR, platform.system()))

if __name__ == '__main__':
    app = qw.QApplication(sys.argv)                    
    fenster=Fenster()
    fenster.showMaximized()
    app.exec()
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@schloss5020: Der Fehler ist, dass nirgends die Verbindung zwischen Modell und der benannten Datenbankverbindung hergestellt wird. Das Modell nimmt dann einfach die Standarddatenbank.

Das `ui`-Attribut ist redundant und wird auch überhaupt gar nicht verwendet. *Entweder* bindet man das Ergebnis von `loadUi()` an ein Attribut und übergibt `self` *nicht*, oder man übergibt `self` und bindest das Ergebnis des Aufrufs nicht an ein weiteres Attribut. Es sollte nicht zwei Wege geben um an die gleichen Werte zu kommen.

`isModal()`-Aufruf in der `__init__()` ist sinnfrei. Das fragt diesen Status ab und macht dann überhaupt gar nichts mit dem Ergebnis.

`datenbank()`, `modell()`, und `tabelle()` sind keine guten Namen für Methoden, denn die Beschreiben keine Tätigkeiten. Es ist auch verwirrend, dass es da neben `datenbank` noch `db` und neben `modell` noch `model` als Attribute gibt. Die Methoden sollten auch besser nicht zur öffentlichen API gehören, denn die sind ja nur zum einmaligen Initialisieren da. Womit die vielleicht auch gar nicht existieren sollten, denn der Code könnte genau so gut in der `__init__()` stehen. Zudem ist `datenbank()` gar keine echte Methode, denn `db` muss überhaupt nicht an das Objekt gebunden werden, womit `self` gar nicht mehr verwendet wird.

Ob `modell()` eine Methode ist/sein muss, kann man so oder so beantworten. Man könnte da auch das Modell erstellen und konfigurieren, und es als Ergebnis zurückgeben und an `tabelle()` als Argument übergeben.

``db.database("con1")`` ist so sinnfrei wie das `isModal()`. Das liefert den gleichen Wert den `db` bereits hat — und macht dann damit absolut gar nichts.

`sys.exit()` hat da nichts zu suchen. Qt-Anwendungen haben einen vorgesehenen Lebenszyklus an dessen Ende noch Signale ausgelöst werden, die mit Aufräumroutinen reagiert werden kann.

Das `label`-Attribut sollte besser `statusLabel` oder so ähnlich heissen. Und da ist dreimal kopierter Code der den Inhalt setzt. Wenn man da mal was dran ändern will, muss man das drei mal machen, darf keine Kopie vergessen, und muss die auch gleichartig ändern.

``statusBar.addWidget(…`` ist falsch, da fehlt der Aufruf der `statusBar()`-Methode. Das sollte eigentlich eine Ausnahme ausgelöst haben‽

`artikelartdialog()` ist wieder kein guter Methodenname und `artikel` ist kein guter Name für ein Fenster. Das ist auch gar keine Methode. Sollte also entweder als Funktion aus der Klasse raus oder als `staticmethod()` definiert werden.

Ungetestet:

Code: Alles auswählen

#!/usr/bin/env python3
import platform
import sys

import artikelart
from PyQt6 import uic
from PyQt6.QtCore import PYQT_VERSION_STR, QT_VERSION_STR, Qt
from PyQt6.QtSql import (
    QSqlDatabase,
    QSqlRelation,
    QSqlRelationalDelegate,
    QSqlRelationalTableModel,
)
from PyQt6.QtWidgets import QApplication, QLabel, QMainWindow, QMessageBox

__version__ = "1.0.0"


class Fenster(QMainWindow):
    def __init__(self):
        super().__init__(windowTitle="Artikel")
        uic.loadUi("Artikelstamm.ui", self)
        #
        # TODO "con1" isn't a good name.
        #
        database = QSqlDatabase.addDatabase("QSQLITE", "con1")
        database.setDatabaseName("Stücklisten.db")
        if not database.open():
            error_text = database.lastError().text()
            QMessageBox.critical(
                None,
                "Datenbankverbindungsfehler",
                f"Kann die Datenbank nicht öffnen: {error_text}",
            )
            QApplication.instance().exit(1)
            return

        required_tables = {"Teileart", "Teilestamm", "Materialart"}
        missing_tables = required_tables - set(database.tables())
        if missing_tables:
            QMessageBox.critical(
                None,
                "DB Fehler",
                (
                    f"Tabellen fehlen, bitte reparieren Sie die DB:"
                    f" {missing_tables}"
                ),
            )
            QApplication.instance().exit(1)
            return

        self.model = QSqlRelationalTableModel(self, database)
        self.model.setTable("Teilestamm")
        self.model.setRelation(
            2, QSqlRelation("Teileart", "Id", "Teileartbezeichnung")
        )
        self.model.setRelation(
            3, QSqlRelation("Materialart", "Id", "Materialbezeichnung")
        )
        self.model.setRelation(
            4, QSqlRelation("Materialform", "Id", "Materialform")
        )
        self.model.select()

        self.tableView.setModel(self.model)
        self.tableView.setItemDelegate(QSqlRelationalDelegate(self.tableView))
        self.tableView.setStyleSheet(
            "QHeaderView::section {"
            "  Background-color:rgb(235,235,235); color:rgb(0,0,0); font: 15px;"
            "}"
        )
        self.tableView.setSortingEnabled(True)
        self.tableView.resizeColumnsToContents()
        self.tableView.verticalHeader().hide()
        #
        # Sortieren nach Artikelnummer.
        #
        self.tableView.sortByColumn(0, Qt.SortOrder.AscendingOrder)
        self.status_label = QLabel()
        self.statusBar().addWidget(self.status_label)
        self.update_status()

        self.zeileneu.clicked.connect(self.neue_zeile_hinzufuegen)
        self.zeileloesch.clicked.connect(self.loeschen)
        for table_name in ["Teileart", "Materialart", "Materialform"]:
            getattr(self, table_name.lower()).clicked.connect(
                lambda table_name=table_name: (
                    artikelart.Fenster(table_name).exec()
                )
            )
        self.action_information.triggered.connect(self.helpAbout)
        self.beenden.clicked.connect(self.close)

    def update_status(self):
        self.status_label.setText(f"Anzahl Sätze: {self.model.rowCount()}")

    def neue_zeile_hinzufuegen(self):
        self.model.insertRow(self.tableView.currentIndex().row())
        self.update_status()

    def loeschen(self):
        teilebezeichnung = self.model.record(
            self.tableView.currentIndex().row()
        ).value(1)
        button_id = QMessageBox.warning(
            self,
            "Sicherheitsabfrage",
            f"Soll der Teilestamm {teilebezeichnung} wirklich geloescht werden?",
            QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
        )
        if button_id == QMessageBox.StandardButton.Yes:
            self.model.removeRow(self.tableView.currentIndex().row())
            self.model.select()
            self.update_status()

    def helpAbout(self):
        QMessageBox.about(
            self,
            "Über Artikelstamm",
            (
                f"<b>Artikelstamm</b> v {__version__}"
                f"<p>Copyright &copy; 08.06.2023 muster. All rights reserved."
                f"<p>Dies ist eine einfache Artikelstamm, Stücklistenverwaltung"
                f"<p>Python {platform.python_version()}"
                f" - Qt {QT_VERSION_STR}"
                f" - PyQt {PYQT_VERSION_STR} on {platform.system()}"
            ),
        )


def main():
    app = QApplication(sys.argv)
    fenster = Fenster()
    fenster.showMaximized()
    app.exec()


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten