TableWidget aktualisiert Daten aus Datenbank nicht

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Mars82
User
Beiträge: 13
Registriert: Freitag 19. November 2021, 23:46

Hi,

ich habe eine einfache Datenbank und ein TableWidget das dessen Daten anzeigt. Wenn ich Daten per SQL Insert reinschreibe und mir diese über einen weiteren Button anzeige lasse
funktioniert es wunderbar. Wenn ich Daten aber lösche und dann wieder auf den Anzeige Button klicke wird die Änderung nicht angezeigt. Erst wenn ich das Programm schliesse und neu starte
sehe ich das die entsprechende Zeile gelöscht wurde. Woran kann das liegen? fehlt hier ein Signal? Hier mein Code:

Code: Alles auswählen

	class Frm_main(QMainWindow, Ui_txt_notizen):
    	def __init__(self):
        super().__init__()
        self.setupUi(self)
        self.table()      
        self.bt_speichern.clicked.connect(self.datenschreiben)
        self.load()
        self.bt_anzeigen.clicked.connect(self.load)
        self.bt_loeschen.clicked.connect(self.delete)
        
        def datenschreiben(self):
        n=self.txt_nr.text()
        s=self.txt_link.text()
        no=self.txt_notizen_2.text()
        connection = sqlite3.connect("transport.db")
        cursor = connection.cursor()
        sql = ("INSERT INTO ideen(nr, link, notizen) VALUES (?, ?, ?)")
        cursor.execute(sql, (n, s, no))
        connection.commit()
        connection.close()
        
        def load(self):
        connection = sqlite3.connect("transport.db")
        cursor = connection.cursor()
        sql = ("SELECT nr, link, notizen FROM ideen")
        self.txt_uebersicht.setRowCount(100)
        tableindex = 0
        for row in cursor.execute(sql):
            self.txt_uebersicht.setItem(tableindex, 0, QtWidgets.QTableWidgetItem(row[0]))
            self.txt_uebersicht.setItem(tableindex, 1, QtWidgets.QTableWidgetItem(row[1]))
            self.txt_uebersicht.setItem(tableindex, 2, QtWidgets.QTableWidgetItem(row[2]))
            tableindex+=1
        connection.commit()
        connection.close()
        
        def delete(self):
        data = self.txt_loeschen.text()
        connection = sqlite3.connect("transport.db")
        cursor = connection.cursor()
        sql = ("DELETE FROM ideen WHERE nr = ?")
        cursor.execute(sql, (data))
        connection.commit()
        connection.close()      
        
Für Hilfe bin ich sehr dankbar.

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

Die Datenbank die ganze Zeit zu öffnen und zu schließen ist unnötig. Den Dateinamen dann noch x-mal hart zu kodieren ist auch eine schlechte Idee.

Zu deinem Problem: du überschreibst ja nur die bestehenden Zeilen, aber löschst nicht die alten Daten aus den Widgets. Also stehen die da drin.

Richtig würde man das mit einem QAbstractItemModel machen.
Mars82
User
Beiträge: 13
Registriert: Freitag 19. November 2021, 23:46

Mit der ersten Zeile kann ich leider nicht viel anfangen. Ich dachte bei jedem SQL Zugriff öffne und schliesse ich die Datenbank. Meinst du mit deiner zweiten Zeile QSqlRelationalTableModel() ?

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

@Mars82: Die Einrückung ist kaputt, da ist die Frage ob das vielleicht einen Fehler im Original verschleiert.

Wonach entscheidest Du was Du explizit aus `QtWidgets` importierst und was Du über das Modul ansprichst? Das ist inkonsistent und man sollte entweder den einen *oder* den anderen Weg nehmen.

Der Code nutzt offenber generierten Quelltext. Das macht man nicht mehr. Man kann die *.ui-Datei vom Designer zur Laufzeit laden.

Der Name `Frm_main` entspricht weder den Python- noch den Qt-Namenskonventionen die sich bei Klassennamen einig sind: PascalCase.

Namen sollten keine kryptischen Abkürzungen enthalten, oder gar nur daraus bestehen. Zudem ist `Form` unüblich, sowohl in Python als auch in Qt.

Man muss auch nicht alles an einen Namen binden. Zum Beispiel die SQL-Anweisungen an den Namen `sql` der dann nur in der nächsten Zeile verwendet wird. Da kann man auch gleich den Wert hinschreiben, denn dass das SQL ist, sieht man am Inhalt.

Code und Daten sollten nicht wiederholt werden. Der Name der Datenbank sollte beispielweise einmal am Anfang als Konstante definiert werden, statt in jeder Methode erneut stehen.

Aber auch die Codewiederholung in jeder Methode die etwas mit der Datenbank macht sollte man vielleicht in eine Funktion heraus ziehen.

`nr` ist ja sehr wahrscheinlich eine Zahl, das sollte dann im Programm auch tatsächlich eine Zahl sein.

``setRowCount(100)`` dürfte in der Regel falsch sein. Da fragt man die Daten ab und trägt die tatsächlich vorhandene Anzahl an Datensätzen ein.

`tableindex` ist eher `row_index` und sollte nicht manuell hochgezählt werden. Dafür gibt es die `enumerate()`-Funktion.

Statt den Code für jede Spalte hinzuschreiben, würde sich auch hier eine Schleife anbieten.

``(data)`` ist exakt das gleiche wie ``data``, die Klammern bewirken da nichts. Insbesondere wird da kein Tupel mit einem Element erstellt. Darum nehme ich da lieber eine Liste, weil da eine einelementige Liste kein überraschender Sonderfall ist.

So wie es jetzt da steht, funktioniert es nur wenn `data` eine Zeichenkette mit einem einzigen Zeichen ist.

Zwischenstand (ungetestet):

Code: Alles auswählen

#!/usr/bin/env python3
import sqlite3
from contextlib import closing, contextmanager
from pathlib import Path

from PyQt5.QtWidgets import QMainWindow, QTableWidgetItem
from PyQt5.uic import loadUi

SELF_PATH = Path(__file__).absolute().parent
DB_FILENAME = "transport.db"  # TODO Wo liegt die Datei eigentlich?


@contextmanager
def get_cursor(db_path):
    with closing(
        sqlite3.connect(db_path, detect_types=sqlite3.PARSE_DECLTYPES)
    ) as connection:
        with closing(connection.cursor()) as cursor:
            try:
                yield cursor
            except:
                connection.rollback()
                raise
            else:
                connection.commit()


class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        loadUi(str(SELF_PATH / "txt_notizen.ui"), self)
        self.table()
        self.load()
        self.bt_speichern.clicked.connect(self.datenschreiben)
        self.bt_anzeigen.clicked.connect(self.load)
        self.bt_loeschen.clicked.connect(self.delete)

    def datenschreiben(self):
        with get_cursor(DB_FILENAME) as cursor:
            cursor.execute(
                "INSERT INTO ideen(nr, link, notizen) VALUES (?, ?, ?)",
                [
                    int(self.txt_nr.text()),
                    self.txt_link.text(),
                    self.txt_notizen_2.text(),
                ],
            )

    def load(self):
        with get_cursor(DB_FILENAME) as cursor:
            cursor.execute("SELECT nr, link, notizen FROM ideen")
            rows = cursor.fetchall()

        self.txt_uebersicht.setRowCount(len(rows))

        for row_index, row in enumerate(rows):
            for column_index, text in enumerate(map(str, row)):
                self.txt_uebersicht.setItem(
                    row_index, column_index, QTableWidgetItem(text)
                )

    def delete(self):
        with get_cursor(DB_FILENAME) as cursor:
            cursor.execute(
                "DELETE FROM ideen WHERE nr = ?",
                [int(self.txt_loeschen.text())],
            )
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
__deets__
User
Beiträge: 14529
Registriert: Mittwoch 14. Oktober 2015, 14:29

Mars82 hat geschrieben: Montag 6. Juni 2022, 20:56 Mit der ersten Zeile kann ich leider nicht viel anfangen. Ich dachte bei jedem SQL Zugriff öffne und schliesse ich die Datenbank. Meinst du mit deiner zweiten Zeile QSqlRelationalTableModel() ?
Nein. Man öffnet nicht jedes Mal. Man committed eine Transaktion um die Werte weg zu schreiben.

Das QSqlRelationalTableModel ist auch mög[ich, aber dann muss alles mit den Qt sql Modulen gemacht werden. Wenn die SQLite unterstützen sicher eine gute Idee. Man kann aber Modelle auch selbst Schreiben, und damit den View vernünftig updaten.
Mars82
User
Beiträge: 13
Registriert: Freitag 19. November 2021, 23:46

Hi,

besten Dank für eure Hilfe. Ich werde schauen wie ich es damit am besten umsetze.

Gruß Mars82
Antworten