Bitte um Hilfe bei Klassen-Vererbung

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
kiaralle
User
Beiträge: 225
Registriert: Donnerstag 19. August 2021, 19:11

Hi,
mein Programm wird jetzt etwas komplexer. Für mich jedenfalls.
Das Programm läuft eigentlich auf einem Raspi.
Damit ich es im Laptop programmieren kann, hab ich alles was die Schnittstellen betrifft raus genommen.

Was läuft:
  • Tkinter läuft
  • die Klasse Datenbank funktioniert und füllt meine Combobox richtig aus
Ich benötige einen Tritt bei:
  • Klasse Geber soll self.geber_db_id und self.motor_db_id aus Klasse Datenbank vererbt bekommen

Gern auch Kritik, denn ich bin in einer krassen Lernphase und mir qualmt das Hirn :D

Code: Alles auswählen

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Aug 26 20:11:54 2026

@author: ralf
"""



from tkinter import ttk, Label, Button, Entry, Menu, LabelFrame, Text, messagebox,StringVar, END
from ttkthemes import ThemedTk
import sqlite3


import time
from contextlib import contextmanager
from threading import Lock


import serial
import math

from crccheck.checksum import ChecksumXor8

import sick_error


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

TXDEN_1 = 27
TXDEN_2 = 22

geber_id=0

"""
import RPi.GPIO as GPIO  #GPIO einrichten
def setup_gpio():
    GPIO.setmode(GPIO.BCM)
    GPIO.setwarnings(False)
    GPIO.setmode(GPIO.BCM)
    #GPIO.setup((TXDEN_1, TXDEN_2), GPIO.OUT, value=True)
    GPIO.setup((TXDEN_1, TXDEN_2), GPIO.OUT)

@contextmanager
def enable_send(channel):
    GPIO.output(channel, GPIO.HIGH)
    yield  # ab dann wird der Code im with-Block ausgeführt
    # ab hier ist der Code des Kontext-Managers abgearbeitet
    # output auf 0 setzen, um wieder empfangen zu können
    GPIO.output(channel, GPIO.LOW)

#Serial config ist aktuell für Hiperface
class RS485config(object):

    def __init__(self, dev, enable_gpio, baudrate=9600,
                 bytesize = serial.EIGHTBITS, parity = serial.PARITY_EVEN,
                 stopbits = serial.STOPBITS_ONE):
        self.serial = serial.Serial(dev, baudrate, bytesize, parity, stopbits, timeout=1)
        self.enable_gpio = enable_gpio
        # soll gleichzeitiges Senden und Empfangen unterbinden
        self.send_receive_lock = Lock()


    def send(self, data):
        # zuerst wird der Lock abgefragt, danach der GPIO durch den Kontextmanager gesetzt
        # wenn er z.B. gerade Daten empfängt, wartet er, bis der Lock wieder freigegeben ist
        # danach wird erst der GPIO auf High gesetzt
        app_instance.conect_progress.step(10)
        with (
            self.send_receive_lock,
            enable_send(self.enable_gpio),
        ):
            self.serial.write(data)

    def receive(self, size):
        app_instance.conect_progress.step(10)
        time.sleep(0.01)
        with self.send_receive_lock:
            data = self.serial.read(size)
        return data

    def set_baudrate(self, baudrate):
        self.serial.close()
        self.serial = serial.Serial(self.serial.port, baudrate)

setup_gpio()

# Signatur geändert: Port, Enable-GPIO, Baudrate
endat_clock = RS485config("/dev/ttySC0", TXDEN_1)
endat_data = RS485config("/dev/ttySC1", TXDEN_2)
"""

class MainWindow():
    def __init__(self):

        self.hauptfenster = ThemedTk(theme="blue")
        self.hauptfenster.title("Servo-Box Version 1.1")
        # rootgröße ermitteln
        #w, h = root.winfo_screenwidth(), root.winfo_screenheight()
        #root.geometry("%dx%d+0+0" % (w, h))
        self.hauptfenster.geometry("1000x750")
        #root. minsize(width=900, height=950)

        my_menu=Menu(self.hauptfenster )
        self.hauptfenster.config(menu=my_menu)
        file_menu= Menu(my_menu)
        geber_menu = Menu(my_menu)
        messung_menu = Menu(my_menu)
        info_menu = Menu(my_menu)

        my_menu.add_cascade(label="Menu", menu=file_menu)
        file_menu.add_command(label="Motor Datenbank")
        file_menu.add_command(label="Geber Datenbank")
        file_menu.add_command(label="Exit",command=self.hauptfenster.destroy)

        if 1 !=1:
            messagebox.showerror('Beenden', 'Keine Schnittstelle, kein Gerät vorhanden. Bitte Anlage überprüfen und Programm neu starten.') # kommt eventuell spät was rein
        else:
            my_menu.add_cascade(label="Geber", menu=geber_menu)
            geber_menu.add_command(label="Hyperface + DSL")
            geber_menu.add_command(label="Resolver")
            geber_menu.add_command(label="Endat analog")
            geber_menu.add_command(label="Endat digital")

            my_menu.add_cascade(label="Data", menu=messung_menu)
            messung_menu.add_command(label="Analog")
            messung_menu.add_command(label="RS485")
            messung_menu.add_command(label="RS422")
#motorsetup
            my_menu.add_cascade(label="Info", menu=info_menu)

            label_titel = Label(self.hauptfenster,text="Servo-Prüfbox", font=('times', 25, 'bold', 'italic'), foreground="#0000ff")
            label_titel.grid(column=0, row=0, padx=20, pady=20)

            frame_motorsetup = LabelFrame(self.hauptfenster, bg="#eeeeee")
            frame_motorsetup.grid(column=0, row=1, sticky="nwse", padx=5, pady=5)

            label_motor = Label(frame_motorsetup,text="Motor-Hersteller", bg="#eeeeee")
            label_motor.grid(column=0, row=0, sticky="nw", padx=5, pady=5)

            motorhersteller_var = StringVar(self.hauptfenster)
            motorhersteller_combo = ttk.Combobox(frame_motorsetup,
                    state="readonly",
                    textvariable= motorhersteller_var,
                    postcommand= lambda:  datenbank.motorhersteller_sqlite())
            motorhersteller_combo.grid(column=1, row=0, sticky="we", padx=5, pady=5)

            label_motortyp = Label(frame_motorsetup,text="Model", bg="#eeeeee")
            label_motortyp.grid(column=0, row=1, sticky="nw", padx=5, pady=5)

            motortyp_var = StringVar(self.hauptfenster)
            motortyp_combo = ttk.Combobox(frame_motorsetup,
                    state="readonly",
                    textvariable= motortyp_var,
                    postcommand= lambda:  datenbank.motortyp_sqlite())
            motortyp_combo.grid(column=1, row=1, sticky="we", padx=5, pady=5)

            label_motorpolpaar = Label(frame_motorsetup,text="Motorpole", bg="#eeeeee")
            label_motorpolpaar.grid(column=0, row=2, sticky="nw", padx=5, pady=5)

            motorpolpaar_var = StringVar(self.hauptfenster)
            motorpolpaar_combo = ttk.Combobox(frame_motorsetup,
                    state="readonly",
                    textvariable= motorpolpaar_var,
                    postcommand= lambda:  datenbank.motorpolpaar_sqlite())
            motorpolpaar_combo.grid(column=1, row=2, sticky="we", padx=5, pady=5)

    #Motorinfos anzeigen
            motordaten = Text(frame_motorsetup, bg="#FFFFee", width=40, height=8)
            motordaten.grid(column=0, row=3, sticky="nw", padx=5, pady=5, columnspan=2)

    #Gebersetup
            frame_gebersetup = LabelFrame(self.hauptfenster, bg="#eeeeee")
            frame_gebersetup.grid(column=0, row=2, sticky="nwse", padx=5, pady=5)

            label_geber = Label(frame_gebersetup,text="Geber-Hersteller", bg="#eeeeee")
            label_geber.grid(column=0, row=1, sticky="nw", padx=5, pady=5)

            geberhersteller_var = StringVar(self.hauptfenster)
            geberhersteller_combo = ttk.Combobox(frame_gebersetup,
                                                 state="readonly",
                                                 textvariable= geberhersteller_var,
                                                 postcommand= lambda:  datenbank.geberhersteller_sqlite())
            geberhersteller_combo.grid(column=1, row=1, sticky="we", padx=5, pady=5)

            label_gebertyp = Label(frame_gebersetup,text="Model", bg="#eeeeee")
            label_gebertyp.grid(column=0, row=2, sticky="nw", padx=5, pady=5)

            gebertyp_var = StringVar(self.hauptfenster)
            gebertyp_combo = ttk.Combobox(frame_gebersetup,
                                          state="readonly",
                                          textvariable= gebertyp_var,
                                          postcommand= lambda:  datenbank.gebertyp_sqlite())
            gebertyp_combo.grid(column=1, row=2, sticky="we", padx=5, pady=5)

            label_gebersuche = Label(frame_gebersetup,text="Datenübertragung", bg="#eeeeee")
            label_gebersuche.grid(column=0, row=3, sticky="nw", padx=5, pady=5)
            conect_progress = ttk.Progressbar(frame_gebersetup, value=10, orient='horizontal', length=200 )
            conect_progress.grid(column=1, row=3, sticky="nw", padx=5, pady=5)
            self.conect_progress =conect_progress

    #Geberinfos anzeigen
            geberdaten = Text(frame_gebersetup, bg="#FFFFee", width=40, height=12)
            geberdaten.grid(column=0, row=5, sticky="nw", padx=5, pady=5, columnspan=2)

    #Projektdaten
            frame_projekt = LabelFrame(self.hauptfenster, bg="#eeeeee")
            frame_projekt.grid(column=1, row=0, sticky="nwse", padx=5, pady=5)
            label_name = Label(frame_projekt,text="Projekt", bg="#eeeeee")
            label_name.grid(column=0, row=0, sticky="nw", padx=5, pady=5)

            projekt_eingabe = Entry(frame_projekt,text="Projekt", bg="#FFFFFF")
            projekt_eingabe.grid(column=0, row=1, sticky="nw", padx=5, pady=5)
            projekt_speichern = Button(frame_projekt,text="Speichern")
            projekt_speichern.grid(column=1, row=1, sticky="nw", padx=5, pady=5)

            starten_buttom = Button(frame_projekt,text="Start", bg="#aaff00", activebackground="#7bb900", activeforeground="#ffffff" ,command= lambda: Geber.starten())
            starten_buttom.grid(column=0, row=2, sticky="nw", padx=5, pady=5)
            stoppen_buttom = Button(frame_projekt,text="Start", bg="#ff6619", activebackground="#d45515", activeforeground="#ffffff" ,command= lambda: Geber.stoppen())
            stoppen_buttom.grid(column=1, row=2, sticky="nw", padx=5, pady=5)

            frame_messungen = LabelFrame(self.hauptfenster, bg="#eeeeee")
            frame_messungen.grid(column=1, row=1, sticky="nws", padx=5, pady=5)

            positions_titel = Label(frame_messungen,text="absolute Position", bg="#eeeeee")
            positions_titel.grid(column=0, row=0, sticky="w", padx=5, pady=5)
            self.positions_value = Label(frame_messungen,text="0", bg="#eeeeee", width=20)
            self.positions_value.grid(column=1, row=0, sticky="e", padx=5, pady=5)
            mech_winkel_titel = Label(frame_messungen,text="mech. Winkel", bg="#eeeeee")
            mech_winkel_titel.grid(column=0, row=1, sticky="w", padx=5, pady=5)
            self.mech_winkel_value = Label(frame_messungen,text="---", bg="#eeeeee")
            self.mech_winkel_value.grid(column=1, row=1, sticky="w", padx=5, pady=5)
            offset_winkel_titel = Label(frame_messungen,text="Offset Winkel", bg="#eeeeee")
            offset_winkel_titel.grid(column=0, row=2, sticky="w", padx=5, pady=5)
            self.offset_winkel_value = Label(frame_messungen,text="---", bg="#eeeeee")
            self.offset_winkel_value.grid(column=1, row=2, sticky="w", padx=5, pady=5)

            datenbank = Datenbank(motorhersteller_combo, motorhersteller_var,
                              motortyp_combo, motortyp_var, motorpolpaar_combo,
                              motorpolpaar_var, geberhersteller_combo, geberhersteller_var,
                              gebertyp_combo, gebertyp_var,
                              geberdaten, conect_progress, motordaten)

    def mainloop(self):
        self.hauptfenster.mainloop()


class Datenbank():
    def __init__(self, motorhersteller_combo, motorhersteller_var, motortyp_combo, motortyp_var,
                 motorpolpaar_combo, motorpolpaar_var, geberhersteller_combo, geberhersteller_var,
                 gebertyp_combo, gebertyp_var,
                 geberdaten, conect_progress, motordaten):
        self.motorhersteller_combo = motorhersteller_combo
        self.motorhersteller_var = motorhersteller_var
        self.motortyp_combo = motortyp_combo
        self.motortyp_var = motortyp_var
        self.motorpolpaar_combo = motorpolpaar_combo
        self.motorpolpaar_var = motorpolpaar_var
        self.geberhersteller_combo = geberhersteller_combo
        self.geberhersteller_var = geberhersteller_var
        self.gebertyp_combo = gebertyp_combo
        self.gebertyp_var = gebertyp_var
        self.geberdaten = geberdaten
        self.conect_progress = conect_progress
        self.motordaten=motordaten

    def motorhersteller_sqlite(self):
        try:
            db_abfrage.execute("SELECT  DISTINCT  hersteller FROM motor")
            self.motorhersteller_combo['values'] = db_abfrage.fetchall()
        except verbindung.DatabaseError as error:
            print(error)

    def motortyp_sqlite(self):
        try:
            db_abfrage.execute('SELECT typ FROM motor WHERE hersteller = ?' , (self.motorhersteller_var.get(),))
            self.motortyp_combo['values'] = db_abfrage.fetchall()
        except verbindung.DatabaseError as error:
            print(error)

    def motorpolpaar_sqlite(self):
        try:
            db_abfrage.execute('SELECT id,pole,uvw,bemerkung FROM motor WHERE typ = ?' , (self.motortyp_var.get(),))
            abfrage=db_abfrage.fetchall()
            self.motorpolpaar_combo['values'] = abfrage[0][1]
            #print(db_abfrage)

            self.motor_db_id = int(abfrage[0][0])
            self.motordaten.delete("1.0", END)
            datenkopf=["ID: ", "Pole: ", "Prüfung bei UVW: ","Bemerkung: "]
            motordatensatz = "{} {} \n{} {} \n{} {} \n{} {} \n".format(datenkopf[0], abfrage[0][0],
                            datenkopf[1] , abfrage[0][1], datenkopf[2], abfrage[0][2],datenkopf[3], abfrage[0][3] )
            self.motordaten.insert('1.0',motordatensatz)
        except verbindung.DatabaseError as error:
            print(error)


 #Geberauswahl
    def geberhersteller_sqlite(self):
        self.geberdaten.delete("1.0", END)
        try:
            db_abfrage.execute("SELECT  DISTINCT  hersteller FROM geber")
            self.geberhersteller_combo['values'] = db_abfrage.fetchall()
        except verbindung.DatabaseError as error:
            print(error)

    def gebertyp_sqlite(self):
        if self.geberhersteller_var.get() == "Sick":
            self.gebertyp_combo['values'] = "Hiperface-Auto"
            self.geberdaten.insert('1.0', "Es wird nach einem Geber an \nSchnittstelle X gesucht und automatisch eingestellt")
            data = bytearray.fromhex("FF52")
            data.append(ChecksumXor8.calc(data))
            try:
                data = endat_data.send(data)
                response = endat_data.receive(7)
                if response.hex()[2:4] == "50" or  ChecksumXor8.calc(response) != 0:
                    error = sick_error.error(response.hex()[4:6])
                    print(error)
                else:
                    model_id = response[3:4].hex()
                    try:
                        db_abfrage.execute('SELECT * FROM geber WHERE model_id =?', (model_id,))
                        abfrage = db_abfrage.fetchall()
                        self.geberdaten.delete("1.0", END)
                    except verbindung.DatabaseError as error:
                        print(error)
            except Exception as e1:
                print ("error communicating...: " + str(e1))
        else:
            try:
                db_abfrage.execute('SELECT  model FROM geber WHERE hersteller = ?' , (self.geberhersteller_var.get(),))
                self.gebertyp_combo['values'] = db_abfrage.fetchall()
                db_abfrage.execute('SELECT * FROM geber WHERE hersteller = ?' , (self.geberhersteller_var.get(),))
                abfrage = db_abfrage.fetchall()
            except verbindung.DatabaseError as error:
                print("Fehler: ",error)

        datenkopf=["Typ:", "Datenverbindung:", "Model-ID:", "Sin-Cos / U:", "Singleturn:", "Multiturn:", "Speicher", "Hersteller:","Z-Spur:","DB-id:"]
        datensatz = "{} {} \n{} {} \n{} {} \n{} {} \n{} {} \n{} {} \n{} {} \n{} {} \n{} {} \n{} {}".format(datenkopf[0], abfrage[0][1],
                            datenkopf[1] , abfrage[0][2], datenkopf[2] ,abfrage[0][3],
                            datenkopf[3] ,abfrage[0][4], datenkopf[4] ,abfrage[0][5],
                            datenkopf[5] ,abfrage[0][6], datenkopf[6] ,abfrage[0][7],
                            datenkopf[7] ,abfrage[0][8], datenkopf[8] ,abfrage[0][9],
                            datenkopf[9] ,abfrage[0][0])
        self.geberdaten.insert('1.0',datensatz)

        self.geber_db_id = abfrage[0][0]



class Geber(Datenbank):
    def __init__(self):
        print(self.geber_db_id)
    
    def starten(self):
        print("starten")
        print(self.geber_db_id)

    def stoppen(self):
        print("stoppen ")

    def hiperface_position(self):
        db_abfrage.execute('SELECT * FROM geber WHERE id = ?' , (self.geber_db_id,))
        geber_abfrage = db_abfrage.fetchall()
        #ingleturn = geber_abfrage[0][5]
        multiturn=geber_abfrage[0][6]

        db_abfrage.execute('SELECT * FROM motor WHERE id = ?' , (self.motor_db_id,))
        motor_abfrage = db_abfrage.fetchall()
        motorpolpaar = motor_abfrage[0][3]/2

        data = bytearray.fromhex("FF42")
        data.append(ChecksumXor8.calc(data))
        try:
            data = endat_data.send(data)
            response = endat_data.receive(7)
            if response.hex()[2:4] == "50" or  ChecksumXor8.calc(response) != 0:
                error = sick_error.error(response.hex()[4:6])
                print(error)
            else:
                realposition = int.from_bytes(response[2:6],'big',signed=True)
                self.positions_value["text"] =  realposition

                schrittzahl_umdrehung_faktor = math.modf(realposition / multiturn)

#Achtung aktuell nur Multiturn
                mech_winkel = round((360 / 1) * (schrittzahl_umdrehung_faktor[0]))
                self.mech_winkel_value["text"] = mech_winkel
                offset_winkel = divmod(mech_winkel * motorpolpaar,  360)
                self.offset_winkel_value["text"] = offset_winkel[1]
        except Exception as e1:
            print ("error communicating...: " + str(e1))
        self.positions_value.after(10, self.hiperface_position)




if __name__ == "__main__":
    app_instance = MainWindow()
    app_instance.mainloop()


Benutzeravatar
__blackjack__
User
Beiträge: 14442
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

Die Datenbankverbindung und der Cursor haben da auf Modulebene nichts verloren.

Die `Datenbank`-Klasse repräsentiert ja irgendwie mehr als eine Datenbank — die kennt und befüllt GUI-Elemente. Und ein `Geber` ist ziemlich sicher keine `Datenbank`. Vererbung ist eine „ist-ein(e)“-Beziehung. Also beispielsweise `Auto` erbt von `Fahrzeug`, weil eine Auto ein Fahrzeug ist. Es wird auch die `Datenbank.__init__()` von der `Geber.__init__()` gar nicht aufgerufen.

Methodennamen beschreiben in der Regel tätigkeiten. `_sqlite` als Anhängsel ist da sehr komisch.
“Life is too long to know C++ well.” — Erik Naggum
kiaralle
User
Beiträge: 225
Registriert: Donnerstag 19. August 2021, 19:11

Hallo __blackjack__,

mal sehen ob ich dich richtig verstehe.

Mit dem Cursor meinst du die zB. execute("SELECT DISTINCT hersteller FROM motor") ?
Das sollte ich auslagern und mir über ein return zurück geben lassen?
Die Abfrage kann man sich ja dynamisch erstellen und übergeben.

Du hattest es schon mal irgendwo angemerkt, das ich GUI-Elemente und üblichen Code nicht vermischen soll.
Also auch das extra in einer def oder class. Ich könnte ja auch app_instance = MainWindow() benutze?

Gruß Ralf.
Benutzeravatar
noisefloor
User
Beiträge: 4340
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Klasse Geber soll self.geber_db_id und self.motor_db_id aus Klasse Datenbank vererbt bekommen
Vererbt ist IMHO falsch, du willst doch "nur" der Klasse Werte mitgeben. Wie das geht, weißt du ja.

Vererbung nutzt man dann, wenn man einer Klasse alle Attribute und Methoden einer anderen Klasse mitgeben will, um diese dann zu erweitern (oder überschreiben).

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

@kiaralle: Mit dem Cursor meine ich `db_anfrage`. Das ist ein Cursor-Objekt und auf Modulebene und Methoden greifen da einfach so magisch drauf zu, dabei sollte das als Argument übergeben werden. Entweder über das erste Argument der Methode wenn das zum Zustand des Objekts gehört, oder eben als normales Argument. Beziehungsweise eigentlich eher die Verbindung, denn Cursor sind als etwas eher kurzlebiges gedacht. Die erstellt man, führt damit eine ”Arbeitseinheit” durch, und dann schliesst man die wieder. Das ist also ein lokaler Wert innerhalb von Funktionen oder Methoden, maximal ein Argument, eher kein Teil vom Zustand eines anderen Objekts.

Auch `verbindung` sollte nicht auf Modulebene existieren. Genau so wenig wie `endat_data`.

`MainWindow` ist als Name falsch, oder es sollte halt wirklich das Hauptfenster sein, also von `Tk` erben. Denn wenn man einen Typ `MainWindow` hat, der ein Attribut `hauptfenster` hat, dann stimmt da was nicht. Die Klasse hat effektiv auch nur dieses Attribut. Da werden zwar noch ein ganzer Haufen anderer Attribute definiert, die aber niemals irgendwo benutzt werden. Dafür werden die als Argumente beim erstellen vom `Datenbank`-Objekt übergeben und dort auch noch mal alle an dieses Objekt gebunden. Ein Haufen GUI-Widgets macht zusammen ja aber keine Datenbank aus. `MainWindow` hat neben der `__init__()` nur eine einzige weitere Methode die gleich nach dem erstellen auch aufgerufen wird. Damit ist diese Klasse so wie sie da steht eigentlich nur eine verkleidete Funktion. Tatsächlich würden aber die ganzen Methoden von `Datenbank` eigentlich auf `MainWindow` gehören.

Viele (alle?) der ``lambda``-Ausdrücke sind unnötig, weil man da die jeweilige Methode direkt angeben könnte, statt einer anonymen Funktion die dann nur die Methode mit den gleichen Argumenten aufruft.
“Life is too long to know C++ well.” — Erik Naggum
kiaralle
User
Beiträge: 225
Registriert: Donnerstag 19. August 2021, 19:11

Hab mal was umgeschrieben und getestet.
Ich frage jetzt die Datenbank so ab:
Ob ich die Klasse Datenbank dann noch benötige sehe ich erst einmal.
Ich muss ja die ID vom Motor und Geber aus Datenbank mir irgendwo merken.
Weniger Code und übersichtlicher.

Besser so?

Code: Alles auswählen

motorhersteller_var = datenbank('SELECT DISTINCT hersteller FROM motor')
motorhersteller_combo = ttk.Combobox(frame_motorsetup,
state="readonly")
motorhersteller_combo['value']= motorhersteller_var

Code: Alles auswählen

def datenbank(option):
    try:
        db_abfrage.execute(option)
        db_antwort=db_abfrage.fetchall()
        return db_antwort
    except verbindung.DatabaseError as error:
        print(error)
Benutzeravatar
snafu
User
Beiträge: 7007
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Eine Klasse, die im Wesentlichen nur aus einer __init__() besteht, ist übrigens aus funktionaler Sicht keine Klasse. Die sollte man also objektorientiert gestalten, d. h. ihr ein paar Methoden zur Definition des Verhaltens mitgeben. Falls man keine sinnvollen Methoden findet, kann man entweder einen Namespace verwenden oder (was man häufiger als einen Namespace sieht) einfach ein Wörterbuch nutzen.

Außerdem erstellst du viele gleichartige GUI-Widgets. Das kann man eleganter, wartbarer und mit weniger Codezeilen per Schleifen lösen, ggf. mit 1-2 Hilfsfunktionen.
Zuletzt geändert von snafu am Freitag 4. September 2026, 19:43, insgesamt 1-mal geändert.
kiaralle
User
Beiträge: 225
Registriert: Donnerstag 19. August 2021, 19:11

snafu hat geschrieben: Freitag 4. September 2026, 19:32 Eine Klasse, die im Wesentlichen nur aus einer __init__() besteht, ist übrigens aus funktionaler Sicht keine Klasse. Die sollte man also objektorientiert gestalten, d. h. ihr ein paar Methoden zur Definition des Verhaltens mitgeben. Falls man keine sinnvollen Methoden findet, kann man entweder einen Namespace verwenden oder (was man häufiger als einen Namespace sieht) einfach ein Wörterbuch nutzen.

Außerdem erstellst du viele gleichartige GUI-Widgets. Das kann man eleganter, wartbarer und mit weniger Codezeilen per Schleifen lösen, ggf. mit 1-2 Hilfsfunktionen.
Das bezieht sich auf das "Hauptfenster"?
In meinem ersten Test hatte ich das auch nicht so. Hatte ich im Internet gesehen und dacht mir nichts dabei....
Ich muss das alles schrittweise umschreiben.
Benutzeravatar
snafu
User
Beiträge: 7007
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

kiaralle hat geschrieben: Freitag 4. September 2026, 19:39 Das bezieht sich auf das "Hauptfenster"?
Ja genau. Das ist halt eine riesige __init__(). Und die mainloop()-Methode setzt man nicht auf Klassen, sondern definiert am Ende eine main()-Funktion und ruft sie dort auf, nachdem man alle anderen Widgets erzeugt und korrekt miteinander "verbunden" hat. Dann findet das GUI-Framework (hier: Tkinter) die Widgets alle selbst. Und das ist auch der vorgesehene Weg. Man darf also nicht mehrere Mainloops gleichzeitig laufen lassen, falls du das gedacht hast.
kiaralle
User
Beiträge: 225
Registriert: Donnerstag 19. August 2021, 19:11

Ok, sch.. Internet :-)
Bringen ein immer nur durcheinander :-)
Benutzeravatar
snafu
User
Beiträge: 7007
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Allein schon, dass du die Klasse MainWindow hast, aber darauf das Attribut "hauptfenster" erstellst, ist etwas kurios. Dann erbe doch einfach an der Stelle. Ich weiß, das ist für einen Anfänger alles nicht so einfach. Deshalb wird ja auch empfohlen, größere GUI-Projekte erst zu starten, wenn man schon erweiterte Kenntnisse in der Programmiersprache hat. Sonst kommt da nämlich ab einem gewissen Level nur noch schwer durchschaubarer Spaghetti-Code raus.

Kann es sein, dass eine KI den Code für dich gebaut hat und er irgendwann nicht mehr funktioniert hat?
kiaralle
User
Beiträge: 225
Registriert: Donnerstag 19. August 2021, 19:11

KI kommt bei mir nicht in die Tüte.
Da dauert es lieber etwas länger und ich schraube meinen Code solange um bis ich die Sachen auch verstehe.
Ich lerne gern an einem Projekt.

Aber danke für die Hilfe hier.
Benutzeravatar
snafu
User
Beiträge: 7007
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

kiaralle hat geschrieben: Freitag 4. September 2026, 20:12 KI kommt bei mir nicht in die Tüte.
Hier dennoch ein KI-generiertes und funktionierendes Beispiel, welches ich im Dialog mit ein paar Prompts erhalten habe:

https://pastebin.com/wbu96LvS

Da sieht man mal, wie komplexere Strukturen mit Tkinter bzw. ttk aufgebaut werden können. Vielleicht möchtest du dir davon ja ein bisschen was abschauen.

Die Google-KI hat übrigens pack() und grid() beim Layout gemischt. Das habe ich jetzt nicht mehr korrigiert oder korrigieren lassen. Man sollte sich jedenfalls für einen Stil entscheiden. Ich bevorzuge grid().
Benutzeravatar
noisefloor
User
Beiträge: 4340
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Ich muss ja die ID vom Motor und Geber aus Datenbank mir irgendwo merken.
Wenn's nur um's merken geht, kann man auch im einfachsten Fall ein Dict nehmen oder eine dataclass, wenn man doch eine Klasse braucht. Dataclasses sind primär dafür nur Attribute zu speichern und enthalten entsprechend ein paar Vereinfachungen im Vergleich zu "normalen" Klassen in Python.

Gruß, noisefloor
Sirius3
User
Beiträge: 18428
Registriert: Sonntag 21. Oktober 2012, 17:20

Statt eines Wörterbuchs mit festen Schlüsseln ist eine Dataclass besser, weil damit definert ist, welche Attribute es gibt.
db_abfrage ist bei Dir immer noch ein globales cursor-Objekt. Wenn man weiß, dass eine Abfrage nur einen Eintrag liefern kann, benutzt man fetchone. Statt Indexzugriff nutzt man Tupleunpacking, statt format f-strings.
Eine Klasse Database ist sinnvoll, um die select-Aufrufe in sprechende Objekte umzuwandeln.
Generell braucht man Vererbung eher selten.
Die logisch Einheit Hauptfenster so zu nennen, ist völlig in )rdnung, und kann dann das tk-Objekt als Attribut enthalten. Das hat nämlich dutzende Attribute, die man nur selten braucht. Wenn dann noch deine Attribute hinzukommen würden, würde es schnell unübersichtlich werden.
Benutzeravatar
__blackjack__
User
Beiträge: 14442
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

An der Stelle würde ich Sirius3 widersprechen. Wenn das Hauptfenster nicht das Hauptfenster ist, sondern neben anderen Attributen das tatsächliche Hauptfenster enthält, dann würde ich das `App` oder so nennen, denn das Hauptfenster (und die Datenbankverbindung, …) sind ja Bestandteile der Anwendung. Interessanterweise lautet die Zeile die das ”Hauptfenster” erstellt, bei ja auch schon ``app_instance = MainWindow()`` was so ja auch schräg ist die ganze Anwendung mit dem Hauptfenster gleich zu setzen. Da würde ich also entweder das Hauptfenster tatsächlich zum Hauptfenster machen und ``main_window = MainWindow()`` schreiben oder die Klasse so lassen und nach `App` umbenennen damit da ein logischeres ``app_instance = App()`` steht.

Wobei das `_instance` da IMHO zu viel ist. In Python ist alles was man an einen Namen binden kann, ein Objekt. Also auch alles ein „instance“ von einem Datentyp. Das gibt dem Leser also keinerlei Mehrwert da `_instance` dran zu schreiben. Und warum da dran schreiben, aber an alle anderen Namen nicht‽
“Life is too long to know C++ well.” — Erik Naggum
Antworten