Code Verbesserung/Verschönerung

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
kakashi
User
Beiträge: 2
Registriert: Freitag 5. März 2021, 10:32

Hallo Zusammen
Ich bin neu hier und dies ist mein erster Beitrag. Ich bin ein Python neuling und allgemein ein neuling im Programieren.
Ich habe mich an einem kleinem Spannungsrechner, den ich in meine Studium gebrauchen kann, versucht.
Der Rechner funktioniert einwandfrei, nun möchte ich ihn aber graphisch noch erweitern mit 3D-Bildern etc.
Jetzt meine Frage: Hat jemand einen Vorschlag wie ich denn Code ab Zeile "Label erzeugen" vereinfache oder verschönern kann ?
[img][https://ibb.co/YPc6XgS/img]

Code: Alles auswählen

[#Spannungstrapezverfahren 
import numpy as np 
import tkinter as tk 
main=tk.Tk()
main.title("Spannungstrapezverfahren")
main.minsize(160*4,90*4)
#Definition und berechnung der Eckspannungen [list][/list]
def Eckspannungsberechnung ():
    N=float(txtNormalkraft.get())
    bx=float(txtLänge_a.get())
    by=float(txtLänge_b.get())
    ex=float(txtAusmitte_x.get())
    ey=float(txtAusmitte_y.get())
    Wy=1/6*by*bx**2
    Wx=1/6*bx*by**2
    try: 
        S1=N/(bx*by)+(N*ex)/Wy+(N*ey)/Wx
        lblErgebnis_S1["text"]="S1= "+str(S1)+"kN/m^2"
        S2=N/(bx*by)-N*ex/Wy+N*ey/Wx
        lblErgebnis_S2["text"]="S2= "+str(S2)+"kN/m^2"
        S3=N/(bx*by)-N*ex/Wy-N*ey/Wx
        lblErgebnis_S3["text"]="S3= "+str(S3)+"kN/m^2"
        S4=N/(bx*by)+N*ex/Wy-N*ey/Wx
        lblErgebnis_S4["text"]="S4= "+str(S4)+"kN/m^2"
    except:
        lblErgebnis_S1["text"]="Zahlen eingeben"
        lblErgebnis_S2["text"]="Zahlen eingeben"
        lblErgebnis_S3["text"]="Zahlen eingeben"
        lblErgebnis_S4["text"]="Zahlen eingeben" 
#Label erzeugen 
lblNormalkraft=tk.Label(main,text="Normalkraft in kN/m^2: ")
lblLänge_a=tk.Label(main,text="Länge a in m eingeben")
lblLänge_b=tk.Label(main,text="Länge b in m eingeben")
lblAusmitte_x=tk.Label(main,text="Ausmitte in x-Richtung in m eingeben")
lblAusmitte_y=tk.Label(main,text="Ausmitte in y-Richtung in m eingeben")
lblErgebnis_S1=tk.Label(main,text="")
lblErgebnis_S2=tk.Label(main,text="")
lblErgebnis_S3=tk.Label(main,text="")
lblErgebnis_S4=tk.Label(main,text="")
#Textfelder erzeugen 
txtNormalkraft=tk.Entry(main,width=5,justify="center")
txtNormalkraft.insert(5,"")
txtLänge_a=tk.Entry(main,width=5,justify="center")
txtLänge_a.insert(5,"")
txtLänge_b=tk.Entry(main,width=5,justify="center")
txtLänge_b.insert(5,"")
txtAusmitte_x=tk.Entry(main,width=5,justify="center")
txtAusmitte_x.insert(5,"")
txtAusmitte_y=tk.Entry(main,width=5,justify="center")
txtAusmitte_y.insert(5,"")
#Befehlschaltfläche 
Berechnen_S1=tk.Button(main,text="Berechne Eckspannung S1, S2, S3 ,S4",command=Eckspannungsberechnung)
Ende=tk.Button(main,text="Benden",command=main.destroy)
#Steuerelemente einfügen 
lblNormalkraft.pack()
txtNormalkraft.pack()
lblLänge_a.pack()
txtLänge_a.pack()
lblLänge_b.pack()
txtLänge_b.pack()
lblAusmitte_x.pack()
txtAusmitte_x.pack()
lblAusmitte_y.pack()
txtAusmitte_y.pack()
Berechnen_S1.pack()
lblErgebnis_S1.pack()
lblErgebnis_S2.pack()
lblErgebnis_S3.pack()
lblErgebnis_S4.pack()
Ende.pack()
main.mainloop()/code]
Benutzeravatar
__blackjack__
User
Beiträge: 14054
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@kakashi: Numpy wird importiert aber nicht verwendet.

Man sollte es mit Leerzeilen nicht übertreiben, aber ein komplettes Programm ohne eine einzige Leerzeile ist nicht gut lesbar. Nach Kommas, um binäre Operatoren, und um das Gleichheitszeichen bei Zuweisungen ausser bei Schlüsselwort-Argumenten erhöhen Leerzeichen die Lesbarkeit.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst. Womit `main` für das Hauptfenster kein guter Name ist. Traditionell heisst das `Tk`-Objekt `root`, man könnte es aber auch `main_window` nennen.

Das erzeugen und anordnen von Anzeigeelementen sollte man nicht so voneinander trennen und das Erzeugen auch nicht nach dem Typ der Anzeigeelemente sortieren. Das macht es unnötig schwer am Code den GUI-Aufbau nachzuvollziehen und es macht auch unnötig Arbeit wenn man Teile der GUI-Erstellung in Funktionen oder eigene Klassen auslagern möchte, wenn man sich dann aus dem langen Code erst alles zusammensuchen muss, weil da nicht zusammen steht was zusammen gehört.

Beim Erzeugen der Eingabefelder machen die `insert()`-Aufrufe überhaupt keinen Sinn. Die fügen jeweils in ein leeres Feld an der 5. Position *nichts* ein. Die Felder bleiben dadurch also wie sie sind.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (PascalCase).

Namen sollten keine kryptischen Abkürzungen enthalten oder gar nur daraus bestehen. Wenn man `label` meint, sollte man nicht nur `lbl` schreiben. Und die Reihenfolge `label_normalkraft` wäre auch eher nur für Yoda okay, normale Menschen sagen dazu `normalkraft_label`.

Man muss aber auch nicht jedes Zwischenergebnis an einen Namen binden wenn man das danach nie wieder verwendet.

Man nummeriert keine Namen. Entweder will man sich dann bessere Namen überlegen, oder gar keine Einzelnamen sondern eine Datenstruktur. Oft eine Liste. Das wäre bei den Ausgabe-Label eine Option.

Im `tkinter`-Modul gibt es für Zeichenketten wie "center" Konstanten die man verwenden sollte.

Funktionen (und Methoden) werden üblicherweise nach der Tätigkeit benannt die sie durchführen, damit man sie von eher passiven Werten unterscheiden kann. `Eckspannungsberechnung()` → `berechne_eckspannungen()`.

Funktionen und Methoden bekommen alles was sie ausser Konstanten benötigen als Argumet(e) übergeben. Die `berechne_eckspannungen()`-Funktion braucht also sowohl die Eingabefelder als auch die Ausgabe-Label als Argumente.

Der ``try``/``except``-Block steht an der falschen Stelle und man sollte keine nackten ``except``\s ohne konkrete Ausnahemen die man auch tatsächlich so behandeln kann verwenden. Der Benutzer wird beispielsweise verwirrt sein wenn er Zahlen eingibt und dann die Meldung "Zahlen eingeben" bekommt. Die wird beispielsweise ausgegeben wenn Eingabewerte 0 waren und dadurch ein `ZeroDivisionError` ausgelöst wurde.

Da wo die Meldung Sinn machen würde, wird die dazugehörige Ausnahme gar nicht behandelt. Nämlich ein `ValueError` der beim Umwandeln der Texte aus den Eingabefeldern in Gleitkommazahlen auftreten kann.

Wenn man sich die Rechnungen für S1 bis S4 anschaut, dann unterscheiden die sich nur durch jeweils zwei Rechenoperationen:

Code: Alles auswählen

            S1 = N / (bx * by) + N * ex / Wy + N * ey / Wx
            S2 = N / (bx * by) - N * ex / Wy + N * ey / Wx
            S3 = N / (bx * by) - N * ex / Wy - N * ey / Wx
            S4 = N / (bx * by) + N * ex / Wy - N * ey / Wx
            #                  ^             ^
            #                  1             2
Diesen Unterschied kann man als Daten in eine Liste herausziehen und die Ergebnisse mit einer Schleife erstellen. Was auch zu den Ergebnis-Labeln in einer Liste passen würde.

Das zusammenstückeln von Zeichenketten und Werten mittels ``+`` und `str()` ist eher BASIC als Python. Dafür gibt es die `format()`-Methode auf Zeichenketten und f-Zeichenkettenliterale.

Zwischenstand:

Code: Alles auswählen

#!/usr/bin/env python3
import tkinter as tk
from functools import partial
from operator import add, sub


def berechne_eckspannungen(
    normalkraft_entry,
    laenge_a_entry,
    laenge_b_entry,
    ausmitte_x_entry,
    ausmitte_y_entry,
    ergebnis_labels,
):
    try:
        normalkraft = float(normalkraft_entry.get())
        laenge_a = float(laenge_a_entry.get())
        laenge_b = float(laenge_b_entry.get())
        ausmitte_x = float(ausmitte_x_entry.get())
        ausmitte_y = float(ausmitte_y_entry.get())
    except ValueError:
        for ergebnis_label in ergebnis_labels:
            ergebnis_label["text"] = "Zahlen eingeben"
    else:
        try:
            for (
                nummer,
                (ergebnis_label, (operation_a, operation_b)),
            ) in enumerate(
                zip(
                    ergebnis_labels,
                    [(add, add), (sub, add), (sub, sub), (add, sub)],
                ),
                1,
            ):
                ergebnis = operation_a(
                    normalkraft / (laenge_a * laenge_b),
                    operation_b(
                        6
                        * normalkraft
                        * ausmitte_x
                        / (laenge_b * laenge_a ** 2),
                        6
                        * normalkraft
                        * ausmitte_y
                        / (laenge_a * laenge_b ** 2),
                    ),
                )
                ergebnis_label["text"] = f"S{nummer} = {ergebnis} kN/m^2"

        except ZeroDivisionError:
            for ergebnis_label in ergebnis_labels:
                ergebnis_label["text"] = "Teilen durch 0 nicht möglich"


def main():
    root = tk.Tk()
    root.title("Spannungstrapezverfahren")

    entry_options = {"width": 5, "justify": tk.CENTER}
    tk.Label(root, text="Normalkraft in kN/m^2:").pack()
    normalkraft_entry = tk.Entry(root, **entry_options)
    normalkraft_entry.pack()

    tk.Label(root, text="Länge a in m eingeben:").pack()
    laenge_a_entry = tk.Entry(root, **entry_options)
    laenge_a_entry.pack()

    tk.Label(root, text="Länge b in m eingeben:").pack()
    laenge_b_entry = tk.Entry(root, **entry_options)
    laenge_b_entry.pack()

    tk.Label(root, text="Ausmitte in x-Richtung in m eingeben:").pack()
    ausmitte_x_entry = tk.Entry(root, **entry_options)
    ausmitte_x_entry.pack()

    tk.Label(root, text="Ausmitte in y-Richtung in m eingeben:").pack()
    ausmitte_y_entry = tk.Entry(root, **entry_options)
    ausmitte_y_entry.pack()

    berechnen_button = tk.Button(
        root, text="Berechne Eckspannung S1, S2, S3, S4"
    )
    berechnen_button.pack()

    ergebnis_labels = list()
    for _ in range(4):
        label = tk.Label(root)
        label.pack()
        ergebnis_labels.append(label)

    berechnen_button["command"] = partial(
        berechne_eckspannungen,
        normalkraft_entry,
        laenge_a_entry,
        laenge_b_entry,
        ausmitte_x_entry,
        ausmitte_y_entry,
        ergebnis_labels,
    )

    tk.Button(root, text="Beenden", command=root.quit).pack()

    root.mainloop()


if __name__ == "__main__":
    main()
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
kakashi
User
Beiträge: 2
Registriert: Freitag 5. März 2021, 10:32

@__blackjack__: Danke für deine Verbesserung und Tipps. Ich konnte fast all deine Anmerkungen und Verbesserung nachvollziehen und verstehen. Das einzige was ich nicht so ganz verstehe, ist am Schluss nach dem for ein _ steht und das zweite sind die letzten 2 if Zeilen. Danke schon mal Voraus.
Stimmt meine Interpretation das sobald der berechnen button geklickt wird: Python die Funktion der Berechnung abruft, ihr die eigegebenen werte übergibt und und im Anschluss die Ergebnisse darstellt.
Benutzeravatar
__blackjack__
User
Beiträge: 14054
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@kakashi: Nach dem ``for`` muss ein Namen stehen. Da hätte auch `hier_steht_ein_name_der_nirgends_benutzt_wird_aber_hier_stehen_muss` stehen können, aber `_` ist einfach sehr viel kürzer. Und konventionell der Name den man verwendet wenn man den gar nicht verwendet, aber schreiben *muss*.

Die letzten beiden Zeilen sorgen dafür das `main()` aufgerufen wird wenn man das Modul als Programm ausführt. Und nur dann. Wenn man das Modul importiert wird `main()` nicht aufgerufen.
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Antworten