SQL Datenbank Abfrage in sekundären Fenster

Fragen zu Tkinter.
Antworten
dark_soul
User
Beiträge: 4
Registriert: Sonntag 25. November 2018, 23:04

Hallo erstmal
Ich bin neu in diesem Forum und hätte da ein Problem und hoffe, dass ihr mir bei der Lösung vielleicht behilflich sein könntet.
Ich habe mir eine SQL Datenbank angelegt und möchte die darin enthaltenen Werte über die GUI ausgeben.
Dafür habe ich ein Entry, in dem Main Window angelegt, dass nach ähnlichen Datensätzen sucht und die Werte aus dem Tabel in Labels ausgibt.

Mein Problem ist jetzt dass ich in einem sekundären Window eine spezifischere Abfrage machen will, dafür habe ich ein neues Fenster erzeugt und in diesem Fenster Entrys und Labels angelegt.
Jedoch funktioniert die Abfrage in dem sekundären Fenster nicht so wie in dem Hauptfenster, jedoch komme ich nicht darauf was ich anders machen könnte.

Unterhalb habe ich den kompletten Code für die GUI eingefügt.
Ich hoffe ihr könnt mir Behilflich sein.
Danke schon mal im voraus.

Code: Alles auswählen

import tkinter, sqlite3

def ende():
    main.destroy()

def neues_fenster():
    neu = tkinter.Toplevel(main)
    neu.wm_geometry("1000x750")

    lb21 = tkinter.Label (neu, text = "Produktnummer")
    lb21["font"] = "Arial 12"
    lb21.place(x = 25, y = 25)

    lb22 = tkinter.Label (neu, text = "EAN")
    lb22["font"] = "Arial 12"
    lb22.place(x = 215, y = 25)

    lb23 = tkinter.Label (neu, text = "Fliesenname")
    lb23["font"] = "Arial 12"
    lb23.place(x = 350, y = 25)

    lb24 = tkinter.Label (neu, text = "Fliesengröße")
    lb24["font"] = "Arial 12"
    lb24.place(x = 525, y = 25)

    lb25 = tkinter.Label (neu, text = "Farbe")
    lb25["font"] = "Arial 12"
    lb25.place(x = 705, y = 25)

    lb26 = tkinter.Label (neu, text = "Fliesenstärke")
    lb26["font"] = "Arial 12"
    lb26.place(x = 850, y = 25)

    e21 = tkinter.Entry(neu)
    e21["width"] = 20
    e21.place(x = 20, y = 50)

    e22 = tkinter.Entry(neu)
    e22["width"] = 20
    e22.place(x = 170, y = 50)

    e23 = tkinter.Entry(neu)
    e23["width"] = 25
    e23.place(x = 320, y = 50)

    e24 = tkinter.Entry(neu)
    e24["width"] = 20
    e24.place(x = 510, y = 50)

    e25 = tkinter.Entry(neu)
    e25["width"] = 20
    e25.place(x = 665, y = 50)

    e26 = tkinter.Entry(neu)
    e26["width"] = 20
    e26.place(x = 835, y = 50)
    
    suche_starten = tkinter.Button(neu, text = "Suche Starten", command = erweiterte_suche)
    suche_starten.place(x = 100, y = 200)

    lb31 = tkinter.Label (neu)
    lb31["font"] = "Arial 12"
    lb31.place(x = 25, y = 75)

    lb32 = tkinter.Label (neu)
    lb32["font"] = "Arial 12"
    lb32.place(x = 215, y = 75)

    lb33 = tkinter.Label (neu)
    lb33["font"] = "Arial 12"
    lb33.place(x = 350, y = 75)

    lb34 = tkinter.Label (neu)
    lb34["font"] = "Arial 12"
    lb34.place(x = 525, y = 75)

    lb35 = tkinter.Label (neu)
    lb35["font"] = "Arial 12"
    lb35.place(x = 705, y = 75)

    lb36 = tkinter.Label (neu)
    lb36["font"] = "Arial 12"
    lb36.place(x = 850, y = 75)

    new = tkinter.Button(main, text = "Erweiterte Suche", command = neues_fenster)
    new.place(x= 400, y = 50)


    
   

def erweiterte_suche():
    lb31['text'] = "hallo"
    connection = sqlite3.connect("fliesen_43.db")
    cursor = connection.cursor()
    fliesenstärke = e26.get()
    sql = "SELECT * FROM fliesen WHERE fliesenstärke LIKE '%" +farbe+ "%'"
    cursor.execute(sql)
    for dsatz in cursor:
        lb31['text'] = dsatz[0]
        lb32['text'] = dsatz[1]
        lb33['text'] = dsatz[2]
        lb34['text'] = dsatz[4]
        lb35['text'] = dsatz[6]
        lb36['text'] = dsatz[7]

    
def search():
    connection = sqlite3.connect("fliesen_43.db")
    cursor = connection.cursor()
    

    eingabe = e1.get()
    sql = "SELECT * FROM fliesen WHERE fliesenname LIKE '%" +eingabe+ "%'"
    cursor.execute(sql)
    for dsatz in cursor:
        
        lb7['text'] = dsatz[2]
        lb8['text'] = dsatz[4]
        lb9['text'] = dsatz[6]
        lb10['text'] = dsatz[12]
        lb11['text'] = dsatz[13]
        lb12['text'] = dsatz[14]




main = tkinter.Tk()

main.wm_geometry("1900x1050")

e1 = tkinter.Entry(main)
e1["width"] = 30
e1.place(x = 150, y = 50)

lb1 = tkinter.Label (main, text = "Fliesenname:")
lb1["font"] = "Arial 12"
lb1.place(x = 25, y = 100)

lb2 = tkinter.Label (main, text = "Fliesengröße: ")
lb2["font"] = "Arial 12"
lb2.place(x = 25, y = 150)

lb3 = tkinter.Label (main, text = "Fliesenfarbe:")
lb3["font"] = "Arial 12"
lb3.place(x = 25, y = 200)

lb4 = tkinter.Label (main, text = "Gang:")
lb4["font"] = "Arial 12"
lb4.place(x = 25, y = 250)

lb5 = tkinter.Label (main, text = "Koje:")
lb5["font"] = "Arial 12"
lb5.place(x = 25, y = 300)


lb6 = tkinter.Label (main, text = "Stellplatz:")
lb6["font"] = "Arial 12"
lb6.place(x = 25, y = 350)

lb7 = tkinter.Label (main) 
lb7["font"] = "Arial 12"
lb7.place(x = 200, y = 100)

lb8 = tkinter.Label (main)
lb8["font"] = "Arial 12"
lb8.place(x = 200, y = 150)

lb9 = tkinter.Label (main)
lb9["font"] = "Arial 12"
lb9.place(x = 200, y = 200)

lb10 = tkinter.Label (main)
lb10["font"] = "Arial 12"
lb10.place(x = 200, y = 250)

lb11 = tkinter.Label(main)
lb11["font"] = "Arial 12"
lb11.place(x = 200, y = 300)
        
lb12 = tkinter.Label(main)
lb12["font"] = "Arial 12"
lb12.place(x = 200, y = 350)


bende = tkinter.Button(main, text = "Ende", command = ende)
bende.place(x = 25, y = 400)

suchen = tkinter.Button(main, text = "Suchen", command = search)
suchen.place(x = 325, y = 50)

new = tkinter.Button(main, text = "Erweiterte Suche", command = neues_fenster)
new.place(x= 400, y = 50)




main.mainloop()
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

Zuerst das wichtigste:
Niemals in SQL-Statements Parameter hineinformatieren! Für diese Fälle kennt `execute` ein weiteres Argument und Platzhalter.

Was tut denn konkret nicht? Gibt es eine Fehlermeldung? Was ist das Verhalten und was wünscht Du Dir statt dessen?

Statt Variablen durchzunummerieren solltest Du aussagekräftige Namen verwenden; oder Listen, wenn es sich um eine tabellenartige Struktur handelt.
Alles was eine Funktion braucht, sollte sie über ihre Argumente bekommen. Für `erweiterte_suche` sind das z.B. e26, lb31, lb32, lb33, lb34, lb35, lb36. An der Aufzählung fällt schon auf, dass das alles ziemlich kryptisch und unübersichtlich ist. Für komplexere GUI-Programme kommt man eigentlich nicht um Klassendefinitionen herum.

Bei SELECT immer die konkreten Spaltennamen angeben, das macht einen unabhängig von eventuellen Tabellenänderungen. Wie es aussieht brauchst Du auch nur einen kleinen Teil aller Spalten. Da könnte man auch nochmal schauen, ob das Datenbankdesign nicht besser gewählt werden kann, bei mindestens 15 Spalten, was ziemlich viel ist.

`font` kann auch als Parameter beim Erzeugen eines Labels angegeben werden.
Alles ab Zeile 128 gehört auch in eine Funktion, die man üblicherweise `main` nennt und am Ende des Programms per

Code: Alles auswählen

if __name__ == '__main__':
    main()
aufgerufen wird. Dann kommt man auch nicht in Verlegenheit, in `search` einfach so von Variablen außen zu benutzen.
dark_soul
User
Beiträge: 4
Registriert: Sonntag 25. November 2018, 23:04

Dankeschön für die schnelle Hilfe und die guten Ratschläge ;)

Ich werde wahrscheinlich ein wenig Zeit brauchen um das umzusetzen, da ich mich wiedermal nach ein paar Jahren mit Python befasse und ich mich mal wieder einarbeiten muss.

Zu der Datenbank, die würde ich nicht ändern,. weil ich die ganzen Parameter brauche ich benutze derzeit nur noch nicht alle weil ich als erstes einmal die Funktionalität des Programms sicherstellen möchte und der Rest ist dann eh nur noch Schreibarbeit.

Also was konkret nicht funktioniert ist, dass ich bei dem Button mit dem Text "erweiterte Suche" ein neues Fenster erzeuge worin Labels und Entrys enthalten sind. Die Labels logischerweise, damit man weiß welche Eingabe in das zugehörige Entry gehört und die Entrys um die Datensätze der Datenbank mit dem eingegebenen abzugleichen und entsprechende Einträge dann darzustellen. Jedoch bekomme ich es nicht einmal hin, dass ein einziger Datensatz abgerufen und dargestellt wird in dem Fenster. Nun ist meine Frage worin denn das Problem besteht, weil in dem main Window habe ich die gleiche Methode benutzt um die Abfrage durchzuführen und in diesem hat das auch Funktioniert.

Ich hoffe diese Erläuterung meines Problems ist besser.

Nochmal danke für die Hilfe.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi dark_soul

Hier etwas zum experimentieren. Was hältst du vom neuen Fensterlayout?:

Code: Alles auswählen

import sqlite3
import tkinter as tk

APP_TITLE = "My DB Query"
APP_XPOS = 100
APP_YPOS = 100
APP_WIDTH = 350
APP_HEIGHT = 200


class ExtSearchWindow(tk.Toplevel):
    
    def __init__(self, app):
        self.app = app
        
        tk.Toplevel.__init__(self, app.main_win)
        self.title("Erweiterte Suche")
        self.protocol("WM_DELETE_WINDOW", self.app.close_extended_search)
        self.geometry("+{}+{}".format(
            self.app.main_win.winfo_rootx()+20,
            self.app.main_win.winfo_rooty()+20))
        
        self.entry_values = list()
        self.extended_search_values = list()
        self.build()
        
    def build(self):
        main_frame = tk.Frame(self)
        main_frame.pack(fill='both', expand=True, padx=10, pady=10)
 
        top_container = tk.Frame(main_frame)
        top_container.pack(fill='both', pady=4)

        middle_container = tk.Frame(main_frame)
        middle_container.pack(fill='both', pady=4)

        bottom_container = tk.Frame(main_frame)
        bottom_container.pack(pady=4)
        
        elements_frame = tk.Frame(top_container)
        elements_frame.pack(expand=True)
        for row, element_name in enumerate(self.app.ELEMENTS_EXT):
            tk.Label(elements_frame, text=element_name
            ).grid(row=row, column=0, padx=4, pady=(2, 0), sticky='e')
            entry_var = tk.StringVar(self.app.main_win, 'leer')
            tk.Entry(elements_frame, textvariable=entry_var,
                width=20).grid(row=row, column=1)
            self.entry_values.append(entry_var)
            extended_search_value = tk.StringVar(self.app.main_win, 'leer')
            tk.Label(elements_frame, textvariable=extended_search_value,
                width=20, bg='gray90').grid(row=row, column=2, padx=4, pady=2,
                    sticky='e')
            self.extended_search_values.append(extended_search_value)
        
        tk.Button(bottom_container, text="Suche Starten",
            command=self.extended_search).pack(side='left')

        tk.Button(bottom_container, text="Schliessen",
            command=self.app.close_extended_search).pack(side='left')

    def extended_search(self):
        print("Erweiterte Suche")
        
        connection = sqlite3.connect("fliesen_43.db")
        cursor = connection.cursor()
        
        entry_index = self.app.ELEMENTS_EXT.index("Fliesenstärke")
        fliesenstärke = self.entry_values[entry_index]
        
        sql = "SELECT * FROM fliesen WHERE fliesenstärke LIKE '%" +farbe+ "%'"
        cursor.execute(sql)

        dsatz_Indices = [0, 1, 2, 3, 4, 7]            
        for index, dsatz in enumerate(cursor):
            self.extended_search_values.set(dsatz[dsatz_Indices[index]])

        
class Application(object):
    ELEMENTS = ["Fliesenname:", "Fliesengröße:", "Fliesenfarbe:", "Gang:",
        "Koje:", "Stellplatz:"]
    ELEMENTS_EXT = ["Produktnummer", "EAN", "Fliesenname", "Fliesengröße",
        "Farbe", "Fliesenstärke"]
        
    def __init__(self, main_win):
        self.main_win = main_win
        main_win.protocol("WM_DELETE_WINDOW", self.close)
        
        self.element_values = list()
        self.ext_search_win = None
        self.build()
        
    def build(self):
        main_frame = tk.Frame(self.main_win)
        main_frame.pack(fill='both', expand=True, padx=6, pady=6)
        
        top_container = tk.Frame(main_frame)
        top_container.pack(fill='both', pady=4)

        middle_container = tk.Frame(main_frame)
        middle_container.pack(fill='both', pady=4)

        bottom_container = tk.Frame(main_frame)
        bottom_container.pack(fill='both', pady=4)

        self.search_var = tk.StringVar(self.main_win, 'Suchbegriff ?')
        tk.Entry(top_container, textvariable=self.search_var, width=30
            ).pack(side='left')

        tk.Button(top_container, text="Suchen",
            command=self.search).pack(side='left', padx=4)

        tk.Button(top_container, text="Erweiterte Suche",
            command=self.open_extended_search).pack(side='left', padx=4)
    
        elements_frame = tk.Frame(middle_container)
        elements_frame.pack(expand=True)
        for row, element_name in enumerate(self.ELEMENTS):
            tk.Label(elements_frame, text=element_name
            ).grid(row=row, column=0, padx=4, pady=(2, 0), sticky='e')
            value_var = tk.StringVar(self.main_win, 'leer')
            tk.Label(elements_frame, textvariable=value_var, bg='gray90',
                width=20).grid(row=row, column=1)
            self.element_values.append(value_var)

        tk.Button(bottom_container, text="Ende", command=self.close).pack()

    def search(self):
        print(self.search_var.get())
        
        connection = sqlite3.connect("fliesen_43.db")
        cursor = connection.cursor()
        return
        eingabe = self.search_var.get()
        sql = "SELECT * FROM fliesen WHERE fliesenname LIKE '%" +eingabe+ "%'"
        cursor.execute(sql)
        
        dsatz_Indices = [2, 4, 6, 12, 13, 14]
        for index, dsatz in enumerate(cursor):
            self.element_values[index].set(dsatz[dsatz_Indices[index]])
                
    def open_extended_search(self):
        if self.ext_search_win == None:   
            self.ext_search_win = ExtSearchWindow(self)
            
    def close_extended_search(self):
        for index, value_var in enumerate(
            self.ext_search_win.extended_search_values):
            elemetname = self.ELEMENTS_EXT[index]
            print("Suchwert für {}".format(elemetname),value_var.get())
            
        self.ext_search_win.destroy()
        self.ext_search_win = None
        
    def close(self):
        self.main_win.destroy()

            
def main():
    main_win = tk.Tk()
    main_win.title(APP_TITLE)
    main_win.geometry("+{}+{}".format(APP_XPOS, APP_YPOS))
    #main_win.geometry("{}x{}".format(APP_WIDTH, APP_HEIGHT))
    
    app = Application(main_win)
    
    main_win.mainloop()
 
 
if __name__ == '__main__':
    main()
Viel Spass
Gruss wuf :-)
Take it easy Mates!
dark_soul
User
Beiträge: 4
Registriert: Sonntag 25. November 2018, 23:04

Ich bedanke mich schon mal dafür, dass du dir die Arbeit gemacht hast um mir zu helfen.
Die Programmstruktur sieht aufjedenfall professioneller aus als mein ;)

Jedoch hätte ich ein anliegen.
Ich sitze gerade daran die Funktionalität der Codezeilen herauszufinden.

Derzeit verwirrt mich vor allem folgender Codeblock:

Code: Alles auswählen

def search(self):
def search(self):
        print(self.search_var.get())
        
        connection = sqlite3.connect("fliesen_43.db")
        cursor = connection.cursor()
        return
        eingabe = self.search_var.get()
        sql = "SELECT * FROM fliesen WHERE fliesenname LIKE '%" +eingabe+ "%'"
        cursor.execute(sql)
        
        dsatz_Indices = [2, 4, 6, 12, 13, 14]
        for index, dsatz in enumerate(cursor):
            self.element_values[index].set(dsatz[dsatz_Indices[index]])
Und zwar scheint dieser Codeblock nicht den gewünschten Zweck zu erfüllen.
Ich habe nämlich die GUI als erstes ausprobiert, jedoch gibt der Button "Suchen" keine Ergebnisse aus der Datenbank aus.
Ich habe bisschen rumprobiert woran es denn liegt, aber bisher ist das einzige was mir aufgefallen ist, dass sobald man das return aus dem Codeblock entfernt zumindest der Fliesenname ausgegeben wird.
Ich durchschaue den Code jedoch noch immer nicht so ganz also falls du mir das erklären könntest wäre es toll.

Nochmals Danke für die große Hilfe ;)
__deets__
User
Beiträge: 14522
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das Return hat da auch nichts verloren - das ist bestimmt nur ein Test-Überbleibsel.

Das zusammengestoppelte SQL danach ist so nicht gut. Es mag oberflächlich funktionieren, öffnet aber Tür & Tor für Angriffe auf die Datenbank. Das mag bei einem lokalen Programm theoretisch verschmerzbar sein. Aber das sich so anzugewöhnen ist ein Fehler.

Stattdessen die Parameter-Variante benutzen.

Code: Alles auswählen

sql = "SELECT * FROM fliesen WHERE fliesenname LIKE ?”
cursor.execute(sql, “%{}%”.format(eingabe))
Sirius3
User
Beiträge: 17737
Registriert: Sonntag 21. Oktober 2012, 17:20

@dark_soul: zusätzlich zu dem was __deets__ geschrieben hat: statt über `dsatz_Indices` zu gehen, um die Spalten auszuwählen, solltest Du nur die Spalten in SELECT angeben, die Du wirklich brauchst. Wobei ich hier sehe, dass Du die "zweite" Spalte aus dem ersten Datensatz, die "vierte" Spalte aus dem zweiten Datensatz nimmst, ist irgendwie totaler Quatsch.

Sollte wohl eher so aussehen:

Code: Alles auswählen

    def search(self):
        connection = sqlite3.connect("fliesen_43.db")
        cursor = connection.cursor()
        eingabe = self.search_var.get()
        sql = "SELECT BLA2, BLUB4, TAMTAM6, ... FROM fliesen WHERE fliesenname LIKE ?"
        cursor.execute(sql, ["%{}%".format(eingabe)])
        rows = cursor.fetchall()
        if len(rows) > 1:
            print("Was ist in diesem Fall zu tun?")
        elif not rows:
            print("nichts gefunden")
        else:
            for element, value un zip(self.element_values, rows[0]):
                element.set(value)
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi dark_soul

Wie __deets__ schon erwähnte ist das return nur ein Test-Überbleibsel da mir die Datenbank fliesen_43.db mit der Tabelle fliesen fehlte um den Code richtig auszutesten. Kannst du also weglassen.

Gruss wuf :-)
Take it easy Mates!
Antworten