Seite 1 von 1

Bitte um Hilfe bei Klassen-Vererbung

Verfasst: Donnerstag 3. September 2026, 19:34
von kiaralle
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()



Re: Bitte um Hilfe bei Klassen-Vererbung

Verfasst: Donnerstag 3. September 2026, 23:03
von __blackjack__
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.