Combobox über sqlite füllen

Fragen zu Tkinter.
Antworten
kiaralle
User
Beiträge: 199
Registriert: Donnerstag 19. August 2021, 19:11

Hallo, ich stecke mal wieder fest.

Ich möchte die drei Combobox über eine Filterfunktion aus dem sqlite füllen.
Ich stoße aber programmiertechnisch an meine Grenzen.

Das auslesen der sqlite-DB versuche ich über eine Klasse zu lösen. Ob das sinn macht, sei mal noch in Frage gestellt. ich weiß es nicht.
Für mich ist erst mal klar, ich weiß so ungefähr und rudimentär was man mit einer Klasse machen kann.
Ich möchte nach einander die Combobox Hersteller, Model, Polpaare über die sqlite füllen.

Könnt ihr mich in die richtige Richtung schieben?

Wie kann ich aus den einzelnen Boxen die Variablen rüber schaufeln ohne das ich die Fehlermeldung bekomme, das die aufgerufenen Variable oder Funktion unbekannt ist.
Ich hab hier wahrscheinlich einen extrem fehlerhaften Aufbau in meinen Code.
Es müssen ja die StringVar() der einzelnen Combobox gesetz werden und mit .get() ausgelesen werden. Aber wie?


Code: Alles auswählen

from tkinter import SUNKEN, Frame, Menu, Toplevel,Label,Entry, W, messagebox, ttk, StringVar


import pyudev
import serial,sqlite3
from ttkthemes import ThemedTk
import sick_error, sick_device



verbindung = sqlite3.connect("servo.db", timeout=10)
db_abfrage = verbindung.cursor()



class Datenbank():
    def __init__(self, motorhersteller_combo, motormodel_combo, motor_polpaare_combo):
        self.motorhersteller_combo = motorhersteller_combo
        self.motormodel_combo = motormodel_combo
        self.motor_polpaare_combo = motor_polpaare_combo

        
    def motorhersteller_sqlite(self):    
        try:
            db_abfrage.execute("SELECT  DISTINCT  motorhersteller FROM motor_table")
            self.motorhersteller_combo['values'] = db_abfrage.fetchall()
            self.motormodel_sqlite()
        except verbindung.DatabaseError as error:
            print(error)        
        
    def motormodel_sqlite(self):     
        try:
            
            db_abfrage.execute("SELECT  DISTINCT  motormodel FROM motor_table WHERE motorhersteller = " + self.motorhersteller_combo.get() )
            self.motormodel_combo['values'] = db_abfrage.fetchall()            
        except verbindung.DatabaseError as error:
            print(error)
            
            
    def motor_polpaare_sqlite(self):
        try:
            db_abfrage.execute("SELECT  DISTINCT  motorpolpaare FROM motor_table WHERE motorhersteller = " + self.motormodel_combo.get())        
            self.motor_polpaare_combo['values'] = db_abfrage.fetchall()
        except verbindung.DatabaseError as error:
            print(error)        
        
        #self.motorhersteller_combo.after(1,self.motorhersteller_sqlite)
        


BACKGROUND_COLOR = "white"
NOTEBOOK_BACKGROUND_COLOR = "white"


def create_hyperface_window():
    

    
    window = Toplevel()
    window.title("Hyperface")
    window.geometry("800x200")
    xpos = 100
    ypos = 150
    window.geometry("+%d+%d" % (xpos, ypos))

    menu = Menu(window)
    window.config(menu=menu)
    window_menu = Menu(menu)
    menu.add_cascade(label="Datei", menu=window_menu)
    window_menu.add_command(label="Beenden", command=window.destroy)
  
    
def opengeberfenster():
    print("test")
    

    
def create_resolver_window():
    window = Toplevel()
    window.title("Resolver")
    window.geometry("800x200")
    xpos = 120
    ypos = 170
    window.geometry("+%d+%d" % (xpos, ypos))

    menu = Menu(window)
    window.config(menu=menu)
    window_menu = Menu(menu)
    menu.add_cascade(label="Datei", menu=window_menu)
    window_menu.add_command(label="Beenden", command=window.destroy)

    info_frame = Frame(window, borderwidth=1, relief=SUNKEN)
    info_frame.grid(column=0, row=0, sticky=W, padx=2, pady=2)

    reset_frame = Frame(window, borderwidth=1, relief=SUNKEN)
    reset_frame.grid(column=0, row=1, sticky=W, padx=2, pady=2)

def create_endat_analog_window():
    window = Toplevel()
    window.title("Endat analog")
    window.geometry("800x200")
    xpos = 140
    ypos = 190
    window.geometry("+%d+%d" % (xpos, ypos))

    menu = Menu(window)
    window.config(menu=menu)
    window_menu = Menu(menu)
    menu.add_cascade(label="Datei", menu=window_menu)
    window_menu.add_command(label="Beenden", command=window.destroy)

    info_frame = Frame(window, borderwidth=1, relief=SUNKEN)
    info_frame.grid(column=0, row=0, sticky=W, padx=2, pady=2)

    reset_frame = Frame(window, borderwidth=1, relief=SUNKEN)
    reset_frame.grid(column=0, row=1, sticky=W, padx=2, pady=2)
    
def create_endat_digital_window():
    window = Toplevel()
    window.title("Endat analog")
    window.geometry("800x200")
    xpos = 150
    ypos = 200
    window.geometry("+%d+%d" % (xpos, ypos))

    menu = Menu(window)
    window.config(menu=menu)
    window_menu = Menu(menu)
    menu.add_cascade(label="Datei", menu=window_menu)
    window_menu.add_command(label="Beenden", command=window.destroy)

    info_frame = Frame(window, borderwidth=1, relief=SUNKEN)
    info_frame.grid(column=0, row=0, sticky=W, padx=2, pady=2)

    reset_frame = Frame(window, borderwidth=1, relief=SUNKEN)
    reset_frame.grid(column=0, row=1, sticky=W, padx=2, pady=2)
def create_analog_window():
    window = Toplevel()
    window.title("Analog")
    window.geometry("800x200")
    xpos = 180
    ypos = 230
    window.geometry("+%d+%d" % (xpos, ypos))

    menu = Menu(window)
    window.config(menu=menu)
    window_menu = Menu(menu)
    menu.add_cascade(label="Datei", menu=window_menu)
    window_menu.add_command(label="Beenden", command=window.destroy)

    info_frame = Frame(window, borderwidth=1, relief=SUNKEN)
    info_frame.grid(column=0, row=0, sticky=W, padx=2, pady=2)

    reset_frame = Frame(window, borderwidth=1, relief=SUNKEN)
    reset_frame.grid(column=0, row=1, sticky=W, padx=2, pady=2)

def create_rs485_window():
    window = Toplevel()
    window.title("Endat digital")
    window.geometry("800x200")
    xpos = 210
    ypos = 230
    window.geometry("+%d+%d" % (xpos, ypos))

    menu = Menu(window)
    window.config(menu=menu)
    window_menu = Menu(menu)
    menu.add_cascade(label="Datei", menu=window_menu)
    window_menu.add_command(label="Beenden", command=window.destroy)

    info_frame = Frame(window, borderwidth=1, relief=SUNKEN)
    info_frame.grid(column=0, row=0, sticky=W, padx=2, pady=2)

    reset_frame = Frame(window, borderwidth=1, relief=SUNKEN)
    reset_frame.grid(column=0, row=1, sticky=W, padx=2, pady=2)


def main():
    

    for device in pyudev.Context().list_devices(
        subsystem="tty", ID_VENDOR_ID="0403"
    ):
        print(device.device_node)
        if device.device_node:
            try:
                with serial.Serial(
                    device.device_node, parity=serial.PARITY_EVEN, timeout=3
                ) as connection:
                    root = ThemedTk(theme="blue")
                    root.title(f"Servo-Box Version 1 {connection.port}")
                    root.config(bg=BACKGROUND_COLOR)
                    #root.attributes("-zoomed", True)
                    root.geometry("800x800")
                    XPOS = 50
                    YPOS = 15
                    root.geometry("+%d+%d" % (XPOS, YPOS))

                    menu = Menu(root)
                    root.config(menu=menu)
                    file_menu = Menu(menu)
                    geber_menu = Menu(menu)
                    messung_menu = Menu(menu)
                    info_menu = Menu(menu)

                    menu.add_cascade(label="Datei", menu=file_menu)
                    file_menu.add_command(
                        label="Beenden", command=root.destroy
                    )
                    menu.add_cascade(label="Geber", menu=geber_menu)
                    geber_menu.add_command(
                        label="Hyperface", command=create_hyperface_window
                    )

                    geber_menu.add_command(
                        label="Resolver", command=create_resolver_window
                        )
                    geber_menu.add_command(
                        label="Endat analog", command=create_endat_analog_window
                        )
                    geber_menu.add_command(
                        label="Endat digital", command=create_endat_digital_window
                        )

                    menu.add_cascade(
                        label="Data", menu=messung_menu
                        )
                    messung_menu.add_command(
                        label="Analog", command=create_analog_window
                        )
                    messung_menu.add_command(
                        label="RS485", command=create_rs485_window
                        )

                    menu.add_cascade(label="Info", menu=info_menu)
                    info_menu.add_command(
                        label=f"Schnittstelle : {connection.port}"
                    )
                    
                    projekt_label = Label(root, text="Projekt",bg=BACKGROUND_COLOR)
                    projekt_label.grid(column=0, row=0,padx=2,pady=2,  sticky='W')                   
                    projekt = Entry(root)
                    projekt.grid(column=1, row=0,padx=2,pady=2 )
                    
                    motorhersteller_label=Label(root, text="Motor - Hersteller",bg=BACKGROUND_COLOR)
                    motorhersteller_label.grid(column=0, row=1,padx=2,pady=2 , sticky='W')
                    motorhersteller = StringVar()
                    motorhersteller_combo = ttk.Combobox(root,
                                         state="readonly",
                                         textvariable= motorhersteller
                                         
                                         )
                    motorhersteller_combo.grid(column=1, row=1,padx=2,pady=2, sticky='E' )

                    
                    motormodel_label=Label(root, text="Motor - Model" ,bg=BACKGROUND_COLOR)
                    motormodel_label.grid(column=0, row=2,padx=1,pady=2 , sticky='W')
                    motormodel_combo = ttk.Combobox(root,
                                         state="readonly" 
                                         )
                    motormodel_combo.grid(column=1, row=2,padx=2,pady=2, sticky='E' )
                    
                    motor_polpaare=Label(root, text="Polpaare",bg=BACKGROUND_COLOR)
                    motor_polpaare.grid(column=0, row=3,padx=1,pady=2 , sticky='W' )
                    motor_polpaare_combo = ttk.Combobox(root,
                                         state="readonly"

                                         
                                         )
                    motor_polpaare_combo.grid(column=1, row=3,padx=2,pady=2 , sticky='E')

                    
                    datenbank = Datenbank(motorhersteller_combo, motormodel_combo, motor_polpaare_combo)
                    datenbank.motorhersteller_sqlite()
                    


                    root.mainloop()
                    break
            except serial.SerialException as error:
                messagebox.showerror(
                    "Fehler", f"{device.device_node}:\n{error}."
                )
    else:
        messagebox.showerror(
            "Beenden",
            (
                "Keine Schnittstelle/kein Gerät vorhanden.\n"
                "Bitte Anlage überprüfen und Programm neu starten."
            ),
        )





if __name__ == "__main__":
    main()

Benutzeravatar
Dennis89
User
Beiträge: 1741
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

die Werte für die Box werden mit `value`gesetzt. Minimalbeispiel:

Code: Alles auswählen

import tkinter as tk
from tkinter import ttk
from functools import partial


class Database:
    def __init__(self):
        self.motors = ["X", "Y", "Z"]


def get_motors(motors, database):
    motors["values"] = database.motors


def main():
    database = Database()
    window = tk.Tk()

    motors = ttk.Combobox(window)
    motors.pack()
    tk.Button(
        window, text="Add to Combobox", command=partial(get_motors, motors, database)
    ).pack()

    window.mainloop()


if __name__ == "__main__":
    main()
Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
DeaD_EyE
User
Beiträge: 1350
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Mein Versuch nur mit Funktionen:

Code: Alles auswählen

import tkinter as tk
from collections.abc import Callable
from tkinter import ttk
from tkinter.messagebox import showinfo
from typing import cast


def read_db_and_fill_box(combo: ttk.Combobox) -> tuple[str, ...]:
    data_from_db = ("A", "B", "C")
    combo["values"] = data_from_db
    return data_from_db


def read_button(combo: ttk.Combobox) -> Callable[[], None]:
    def inner() -> None:
        values = read_db_and_fill_box(combo)
        # z.B. den letzten Wert setzen
        combo.current(len(values) - 1)
        # das Event auslösen, falls gewünscht
        combo.event_generate("<<ComboboxSelected>>")

    return inner


def new_selection(text: tk.StringVar) -> Callable[[tk.Event], None]:
    def inner(event: tk.Event) -> None:
        value = text.get()
        print(value, "ausgewählt")
        showinfo("Neue Auswahl", f"Es wurde {value} ausgewählt.")
        # kann auch ohne StringVar vom widget ausgelesen werden
        # und dann braucht man die Funktion nicht zu verschachteln
        # mit Klassen ist das ganze dann einfacher, weil man Zugriff auf die Attribute der Instanz hat
        # print(event.widget.get(), "ausgewählt")

    return inner


def new_selection_evnt(event: tk.Event) -> None:
    combo = cast(ttk.Combobox, event.widget)
    value = combo.get()
    print(value)


def gui() -> None:
    root = tk.Tk()
    combo_selection = tk.StringVar(root)
    combo = ttk.Combobox(root, textvariable=combo_selection)
    combo.bind("<<ComboboxSelected>>", new_selection(combo_selection))
    combo.pack()
    ttk.Button(root, text="DB lesen", command=read_button(combo)).pack()
    ttk.Button(root, text="Verlassen", command=root.destroy).pack()
    root.mainloop()


if __name__ == "__main__":
    gui()

TypeHints und TKinter arbeiten nicht so gut zusammen. Ich mag tkinter nicht.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Benutzeravatar
Dennis89
User
Beiträge: 1741
Registriert: Freitag 11. Dezember 2020, 15:13

Wäre das mit `lambda` oder `partial()`nicht wesentlich einfacher zu schreiben und zu lesen, als mit `inner()`-Funktionen? Oder übersehe ich einen Grund dafür?


Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
Benutzeravatar
DeaD_EyE
User
Beiträge: 1350
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Dennis89 hat geschrieben: Mittwoch 10. Juni 2026, 11:56 Wäre das mit `lambda` oder `partial()`nicht wesentlich einfacher zu schreiben und zu lesen, als mit `inner()`-Funktionen? Oder übersehe ich einen Grund dafür?
Klar, beides wäre möglich. Ist Geschmackssache. Ich mag z.B. lambda nicht so gern. Die Funktion partial hat seit Python 3.14 auch einen Placeholder bekommen: https://docs.python.org/3/library/funct ... ls.partial

Wenn man mit Klassen arbeitet, kann man closures/partial/lambda auch vermeiden.
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Sirius3
User
Beiträge: 18417
Registriert: Sonntag 21. Oktober 2012, 17:20

@DeaD_EyE: Du suchst also nach Ausreden, warum Du die kompliziertere Variante geschrieben hast. Da hier eindeutig partial Vorteile hat, ist es keine Geschmackssache,

Code: Alles auswählen

import tkinter as tk
from tkinter import ttk
from tkinter.messagebox import showinfo
from functools import partial

def read_db_and_fill_box(combo):
    data_from_db = ("A", "B", "C")
    combo["values"] = data_from_db
    return data_from_db


def read_button(combo):
    values = read_db_and_fill_box(combo)
    # z.B. den letzten Wert setzen
    combo.current(len(values) - 1)
    # das Event auslösen, falls gewünscht
    combo.event_generate("<<ComboboxSelected>>")

def new_selection(text, event):
    value = text.get()
    print(value, "ausgewählt")
    showinfo("Neue Auswahl", f"Es wurde {value} ausgewählt.")
    # kann auch ohne StringVar vom widget ausgelesen werden
    # und dann braucht man die Funktion nicht zu verschachteln
    # mit Klassen ist das ganze dann einfacher, weil man Zugriff auf die Attribute der Instanz hat
    # print(event.widget.get(), "ausgewählt")


def new_selection_evnt(event):
    combo = cast(ttk.Combobox, event.widget)
    value = combo.get()
    print(value)


def gui():
    root = tk.Tk()
    combo_selection = tk.StringVar(root)
    combo = ttk.Combobox(root, textvariable=combo_selection)
    combo.bind("<<ComboboxSelected>>", partial(new_selection, combo_selection))
    combo.pack()
    ttk.Button(root, text="DB lesen", command=partial(read_button, combo)).pack()
    ttk.Button(root, text="Verlassen", command=root.destroy).pack()
    root.mainloop()


if __name__ == "__main__":
    gui()
Antworten