tkinter zeichnet erst nach Abschluss aller Berechnungen

Fragen zu Tkinter.
Antworten
Rolando1964
User
Beiträge: 2
Registriert: Dienstag 14. Mai 2019, 21:21

Hallo, ich versuche gerade Python zu lernen, hab früher nur C und Pearl genutzt, ohne den objektorientierten Ansatz.
Ich versuche, ein physikalisches Simulationsprogramm zu schreiben. Im Prinzip funktioniert es schon gut, nur das GUI macht nicht richtig mit.

Selbst wenn die Subroutine Simulation 1000 Zyklen durchläuft, werden die 1000 Zeichnungen erst am Schluss auf einmal geplottet, die after Funktion sorgt zwar für eine Zeitverzögerung, aber gezeichnet wird erst ganz am Schluss.
Ich verstehe das nicht ganz, jede pack anweisung schiebt die zu zeichnende Linie doch in eine Queue, wo sie unabhängig vom Programmablauf gezeichnet wird? Warum wartet das Gui mit dem zeichnen, bis das Programm durch alle Zyklen durch ist, anstatt sofort zu loszulegen ?

Code: Alles auswählen

import time
#import datetime
import os
import re
import math
from tkinter import *

# Spezifikation der Variablen
G=6.672*10 ** (-11)
LBL=[]
M=[]
R=dict()
pstart=dict()
pz=lz=oz=Anzahl_Objekte=zeit=lfz=0
t_alt=-1
P=[]
Kalibrierung=0
R[0]=0
V=[]
O=[]
F=[]
ROTATION=[]
BM=[]
BP=[]
BV=[]
S=[]
NAME=[]
LINIE=[]
SKALIERUNG=[]
ZOOM=[]
UPS=[]
ZK=0
Z=0
BV_Schritt=1
counter=1
Zyklen=0
lfz=0
PROJEKTION="Zentralprojektion"

class Data:
    def __init__(self,objnr,txt="",x=0,y=0,z=0):
        self.objnr=objnr
        self.txt=txt
        self.x=x
        self.y=y
        self.z=z
    def Drehen(self,zeit,ups,n1,n2,n3):
        u=ups*zeit
        w=2*math.pi*(u - int(u))
        cos=math.cos(w)
        sin=math.sin(w)
        a1=n1**2*(1-cos) + cos
        a2=n1*n2*(1-cos) + n3*sin
        a3=n1*n3*(1-cos) - n2*sin
        b1=n1*n2*(1-cos) - n3*sin
        b2=n2**2*(1-cos) + cos
        b3=n2*n3*(1-cos) + n1*sin
        c1=n1*n3*(1-cos)+ n2*sin
        c2=n2*n3*(1-cos)-n1*sin
        c3=n3**2*(1-cos) + cos   
        xx=self.x * a1 + self.y * b1 + self.z * c1
        yy=self.x * a2 + self.y * b2 + self.z * c2
        zz=self.x * a3 + self.y * b3 + self.z * c3
        self.x=xx
        self.y=yy
        self.z=zz
    def Laden(fn):
        global LBL, pstart, M, R, pz, lz, oz, zeit, lfz, t_alt, P
        global R, V, O, F, ROTATION, BM, BP, BV, S, NAME, LINIE, SKALIERUNG, ZOOM
        global UPS, ZK, Z, PROJEKTION, Anzahl_Objekte, BV_Schritt
        Anzahl_Objekte=counter=Zyklen=0
        BM=[]
        BV=[]
        BP=[]
        SKALIERUNG=[]
        P=[]
        LINIE=[]
        ROTATION=[]
        O=[]
        F=[]
        LBL=[]
        pz=0
        lz=0
        
        f=open(fn,"r")
        for l in f:
            zeile=l.split()
            if zeile[1] == "P":
                t0=int(zeile[0])
                t2=float(zeile[2])
                t3=float(zeile[3])
                t4=float(zeile[4])

                P.append(Data(t0, "", t2, t3, t4))
                if t_alt != t0:
                    R[t0]=0
                    pstart[t0] = pz
                    t_alt = t0
                pz = pz + 1
                # Ermittle max Radius für Kollisionsdetektor
                betrag=(t2 ** 2 + t3 ** 2 + t4 ** 2)**0.5
                if R[t0]< betrag : R[t0]=betrag
            elif zeile[1] == "NAME": NAME.append(Data(0, zeile[2]))       
            elif zeile[1] == "L":
                t0=int(zeile[0])
                LINIE.append(Data(int(zeile[0]), "", int(zeile[2]) + pstart[t0], int(zeile[3]) + pstart[t0]))
                lz=lz+1
            elif zeile[1] == "F": F.append(Data(0, zeile[2]))
            elif zeile[1] == "ZOOM": ZOOM = float(zeile[2])
            elif zeile[1] == "ZK": ZK = float(zeile[2])
            elif zeile[1] == "Z": Z = float(zeile[2])                
            elif zeile[1] == "V": V.append(Data(int(zeile[0]), "", float(zeile[2]), float(zeile[3]), float(zeile[4])))
            elif zeile[1] == "O":
                Anzahl_Objekte += 1
                t0=int(zeile[0])
                O.append(Data(t0, "", float(zeile[2]), float(zeile[3]), float(zeile[4])))
                LBL.extend(["Info_" + zeile[0]])
                LblPos=28+ 100 * t0
                LBL[t0] = Label(master=master, bg='#FFCFC9', text=zeile[0])
                LBL[t0].place(x=1000, y=LblPos, width=200, height=99)
            elif zeile[1] == "ROTATION": ROTATION.append(Data(0, "", float(zeile[2]), float(zeile[3]), float(zeile[4])))
            elif zeile[1] == "UPS": UPS.append(Data(float(zeile[2])))
            elif zeile[1] == "M": M.append(Data(float(zeile[2])))
            elif zeile[1] == "S": S.append(Data(int(zeile[0]), "", float(zeile[2]), float(zeile[3]), float(zeile[4])))
            elif zeile[1] == "BP": BP.append(Data(0, "", float(zeile[2]),float(zeile[3]),float(zeile[4])))
            elif zeile[1] == "BM": BM.append(Data(0, "", float(zeile[2]), float(zeile[3]), float(zeile[4])))
            elif zeile[1] == "BV": BV.append(Data(0, "", float(zeile[2]), float(zeile[3]), float(zeile[4])))
            elif zeile[1] == "SKALIERUNG": SKALIERUNG.append(Data(0, "", float(zeile[2]), float(zeile[3]), float(zeile[4])))
            elif zeile[1] == "BVS": BV_Schritt = float(zeile[2])
            elif zeile[1] == "PROJEKTION": PROJEKTION = zeile[2]
        f.close()
        update_Info()  
    def Zyklus_berechnen():
        global LBL, pstart, M, R, pz, lz, oz, zeit, lfz, t_alt, Zyklen, Kollisionswarnung
        global R, V, O, F, BP, BV, BM, ROTATION, S, NAME, LINIE, SKALIERUNG, ZOOM, P
        global UPS, ZK, Z, PROJEKTION, Anzahl_Objekte, BV_Schritt, counter

        # Update Anzahl Zyklen Label
        counter+=1
        v=(BV[0].x**2 + BV[0].y**2 + BV[0].z**2)**0.5
        txt="Anzahl Zyklen: " + str(counter) + " BV: " + str(v)
        LabelZyklen.config(text=txt)

        # Ermittlung der Abstände und Kraftvektoren
        z1=0
        while z1 < Anzahl_Objekte:
            z2=0
            if counter == 1: break
            while z2 < Anzahl_Objekte:
                # Sonderfall für nur ein Objekt
                if Anzahl_Objekte==1:
                    O[0].x += Z * V[0].x
                    O[0].y += Z * V[0].y
                    O[0].z += Z * V[0].z
                    betrag=(V[0].x**2 + V[0].y**2 + V[0].z**2)**0.5
                    txt=NAME[0].txt + " \nV=" + str(betrag) + " m/s"
                    LBL[0].config(text=txt)

                    # Änderung der Beobachterposition
                    BP[0].x += BV[0].x * Z
                    BP[0].y += BV[0].y * Z
                    BP[0].z += BV[0].z * Z
                    update_Info()

                    z2=z2+1                    
                    continue
                elif z2<=z1:
                    z2=z2+1
                    continue

                # Ermittlung des Kraftvektors zwischen den Objekten
                x=O[z2].x - O[z1].x
                y=O[z2].y - O[z1].y
                z=O[z2].z - O[z1].z

                # Normiere den Kraftvektor
                betrag=(x ** 2 + y ** 2 + z ** 2)**0.5
                if betrag == 0:
                    x=y=z=0
                else:
                    x=x/betrag
                    y=y/betrag
                    z=z/betrag

                # Kollisionsprüfung
                if Kollisionswarnung >0 and betrag <= R[z1] + R[z2]:
                    line="Achtung, Kollision Objekt " + str(z1) + " und " + str(z2) + "\nTrotzdem fortfaheren?"
                    if Bestätigung(line) == "n": return(0)
                    if Bestätigung("Weitere Kollisionswarnungen unterdrücken ?") == "j":
                        Kollisionswarnung=0
            
                # Berechnung der Anziehungskraft
                if betrag > 0:
                    Kraft = (G * M[z1].objnr * M[z2].objnr)/betrag ** 2
                else:
                    Kraft=0
                line="Kraft zwischen Objekt " + str(z1) + " und Objekt " + str(z2) + " : " + str(Kraft) + "\n"
                log.write(line)
                line="Kraftvektor: " + str(x) + " , " + str(y) + " , " + str(z) + "\n"
                log.write(line)

                # Berechnung der resultierenden Beschleunigungen
                if M[z1].objnr > 0: a1 = Kraft / M[z1].objnr
                else: a1=0
                v1 = Z * a1
                if M[z2].objnr > 0: a2 = -1 * Kraft / M[z2].objnr
                else: a2=0
                v2 = Z * a2
                line="a1,v1,a2,v2,zeit: " + str(a1) + "," + str(v1) + "," + str(a2) + "," + str(v2) + "," +  "\n"
                log.write(line)

                #andrucken der aktuellen Daten
                txt=NAME[z1].txt + " \nV=" + str(v1) + " m/s\na=" + str(a1) + "\nMitte-Mitte Abstand zu " + NAME[z2].txt + " =" + str(betrag)
                LBL[z1].config(text=txt)
                txt=NAME[z2].txt + " \nV=" + str(v2) + " m/s\na=" + str(a2) + "\nMitte-Mitte Abstand zu " + NAME[z1].txt + " =" + str(betrag)
                LBL[z2].config(text=txt)
                
                # Änderung der Ortsvektoren unter Annahme konstanter Kraft
                O[z1].x += Z * V[z1].x + x * 0.5 * a1 * Z ** 2
                O[z1].y += Z * V[z1].y + y * 0.5 * a1 * Z ** 2
                O[z1].z += Z * V[z1].z + z * 0.5 * a1 * Z ** 2
                O[z2].x += Z * V[z2].x + x * 0.5 * a2 * Z ** 2
                O[z2].y += Z * V[z2].y + y * 0.5 * a2 * Z ** 2
                O[z2].z += Z * V[z2].z + z * 0.5 * a2 * Z ** 2

                # Änderung der Geschwindigekeitsvektoren
                V[z1].x += v1 * x
                V[z1].y += v1 * y
                V[z1].z += v1 * z
                V[z2].x += v2 * x
                V[z2].y += v2 * y
                V[z2].z += v2 * z

                #Schreibe Objektdaten in Logfile
                line = str(z1) + " O " +str(O[z1].x) + " " + str(O[z1].y) + " " + str(O[z1].z) + "\n"
                log.write(line)
                line  =str(z1) + " V " +str(V[z1].x) + " " + str(V[z1].y) + " " + str(V[z1].z) + "\n"
                log.write(line)
                line = str(z2) + " O " +str(O[z2].x) + " " + str(O[z2].y) + " " + str(O[z2].z) + "\n"
                log.write(line)
                line = str(z2) + " V " +str(V[z2].x) + " " + str(V[z2].y) + " " + str(V[z2].z) + "\n"
                log.write(line)   
                z2=z2+1
            z1=z1+1
        lfz+=Z
        laufzeit=str(lfz)
        line="Laufzeit " + laufzeit + "\n\n"
        log.write(line)
          
        #Anpassung der Punkte
        PP=[]
        p=0
        while p < pz:
            objnr=P[p].objnr
            
            # Drehung der Objekte
            if UPS[objnr].objnr != 0: P[p].Drehen(lfz,UPS[objnr].objnr,ROTATION[objnr].x,ROTATION[objnr].y,ROTATION[objnr].z)

            # Normierung auf das Beobachterkooordinatensystem
            x=(P[p].x+O[objnr].x-BP[0].x)*BM[0].x+(P[p].y+O[objnr].y-BP[0].y)*BM[0].y+(P[p].z+O[objnr].z-BP[0].z)*BM[0].z
            y=(P[p].x+O[objnr].x-BP[0].x)*BM[1].x+(P[p].y+O[objnr].y-BP[0].y)*BM[1].y+(P[p].z+O[objnr].z-BP[0].z)*BM[1].z
            z=(P[p].x+O[objnr].x-BP[0].x)*BM[2].x+(P[p].y+O[objnr].y-BP[0].y)*BM[2].y+(P[p].z+O[objnr].z-BP[0].z)*BM[2].z
 
            if PROJEKTION=="Parallelprojektion":
                x=int(x * Kalibrierung * ZOOM) + 400
                y=400 - int(y * Kalibrierung * ZOOM)
                PP.append(Data(objnr, "", x, y, z))
            elif PROJEKTION=="Projektion_auf_Kugel":
                betrag=(x**2 + y**2 + z**2)**0.5
                x=int((x * Kalibrierung * ZOOM)/betrag) + 400
                y=400 - int((y * Kalibrierung * ZOOM)/betrag)
                PP.append(Data(objnr, "", x, y, z))
            else:
                if z != 0:
                    x=int((x/z) * Kalibrierung * ZOOM + 400)
                    y=400 - int((y/z) * Kalibrierung * ZOOM)
                else: x = y = -1000
                PP.append(Data(objnr, "", int(x), int(y), z))
            line="Punkt " + str(p) + "--> z=" + str(z) +" Bildtafel:" + str(x) + " " +str(y) + "\n"
            log.write(line)
            p = p + 1
                
        #Zeichnen der Linien
        l=0
        while l < lz:
            objnr=int(LINIE[l].objnr)
            p1=LINIE[l].x
            p2=LINIE[l].y
            l=l+1

            # Überspringe Zeilen mit Start- und Endpunkt hinter dem Betrachter
            if PP[p1].z <= 0 and PP[p2].z <= 0: continue

            # Setzen des Mindestabstandes von einem Punkt
            if PP[p1].x == PP[p2].x: PP[p1].x += 1
            if PP[p1].y == PP[p2].y: PP[p2].y += 1
            w.create_line(PP[p1].x, PP[p1].y, PP[p2].x, PP[p2].y, fill=F[objnr].txt , width=1)
            w.pack()

    def Kalibrierung():
        global Kalibrierung, pz, P, BM, O, BP, PROJEKTION, log
        max=p=0
        while p < pz:
            objnr=P[p].objnr
            x=(P[p].x+O[objnr].x-BP[0].x)*BM[0].x+(P[p].y+O[objnr].y-BP[0].y)*BM[0].y+(P[p].z+O[objnr].z-BP[0].z)*BM[0].z
            y=(P[p].x+O[objnr].x-BP[0].x)*BM[1].x+(P[p].y+O[objnr].y-BP[0].y)*BM[1].y+(P[p].z+O[objnr].z-BP[0].z)*BM[1].z
            z=(P[p].x+O[objnr].x-BP[0].x)*BM[2].x+(P[p].y+O[objnr].y-BP[0].y)*BM[2].y+(P[p].z+O[objnr].z-BP[0].z)*BM[2].z
            if PROJEKTION == "Parallelprojektion":
                xx=abs(x)
                yy=abs(y)
            elif PROJEKTION == "Projektion_auf_Kugel":
                betrag=(x**2 + y**2 + z**2)**0.5
                xx=abs(x/betrag)
                yy=abs(y/betrag)
            elif PROJEKTION == "Zentralprojektion":
                if z != 0:
                    xx=abs(x/z)
                    yy=abs(y/z)
                else: xx=yy=0
            if xx > max : max=xx
            if yy > max : max=yy
            line="x="+str(x)+" y="+str(y)+" z=" + str(z)+ " xx="+str(xx)+" YY="+str(yy)+ " max=" + str(max) + "\n"
            log.write(line)
            p += 1
        if max != 0: Kalibrierung=200/max
        else: Kalibrierung = 1
        line="Projektionsart: " + PROJEKTION + "Kalibrierungsfakror: " + str(Kalibrierung) + "\n"
        log.write(line)

def Zeit_anpassen(Zeit):
    if Zeit> 31536000:
        Faktor=1/31536000
        Einheit="Jahre"
    elif Zeit> 86400:
        Faktor=1/86400
        Einheit = "Tage"
    elif Zeit > 3600:
        Faktor=1-3600
        Einheit = "Stunden"
    else:
        Faktor=1
        Einheit = "Sekunden"
    return(Zeit * Faktor,Einheit)

def update_Info():
    global BV, BM, BP
    Faktor = 1
    Einheit = " m"
    if abs(BP[0].x) > 1000 and abs(BP[0].y) > 1000 and abs(BP[0].z) > 1000:
        Faktor = 0.001
        Einheit = " Km"
    elif abs(BP[0].x) < 0.001 and abs(BP[0].y) < 0.001 and abs(BP[0].z) < 0.001:
        Faktor = 1000000
        Einheit = " μm"
    elif abs(BP[0].x) < 1 and abs(BP[0].y) < 1 and abs(BP[0].z) < 1:
        Faktor = 1000
        Einheit = " mm"
    x='{:.2f}'.format(BP[0].x * Faktor)
    y='{:.2f}'.format(BP[0].y * Faktor)
    z='{:.2f}'.format(BP[0].z * Faktor)
    txt="Position: " + x + "/" + y + "/" + z + Einheit
    Label_BP.config(text=txt)

    betrag=(BV[0].x**2 + BV[0].y**2 + BV[0].z**2)**0.5
    Faktor = 1
    Einheit = " m/Sek"
    if betrag > 1000:
        Faktor = 0.001
        Einheit = " Km/Sek"
    elif betrag < 0.001:
        Faktor = 1000000
        Einheit = " μm/Sek"
    elif betrag < 1:
        Faktor = 1000
        Einheit = " mm/Sek"    
    txt="Geschwindigkeit " + '{:.2f}'.format(betrag * Faktor) + Einheit
    Label_BV.config(text=txt)
    txt="Blickrichtung: " + '{:.2f}'.format(BM[2].x) + "/" + '{:.2f}'.format(BM[2].y) + "/" + '{:.2f}'.format(BM[2].z)
    txt=txt + "\n" + "Horizont : " + '{:.2f}'.format(BM[1].x) + "/" + '{:.2f}'.format(BM[1].y) + "/" + '{:.2f}'.format(BM[1].z) 
    Label_BM.config(text=txt)

def Beobachter_drehen(Achse,Richtung):
    n1=n2=n3=0
    ups=1/360
    if   Achse == "x": n1 = 1
    elif Achse == "y": n2 = 1
    elif Achse == "z": n3 = 1
    if Richtung == "links": ups = -ups
    BM[0].Drehen(1, ups, n1, n2, n3)
    BM[1].Drehen(1, ups, n1, n2, n3)
    BM[2].Drehen(1, ups, n1, n2, n3)
    x='{:3.2f}'.format(BM[2].x)
    y='{:3.2f}'.format(BM[2].y)
    z='{:3.2f}'.format(BM[2].z)
    a='{:3.2f}'.format(BM[0].x)
    b='{:3.2f}'.format(BM[0].y)
    c='{:3.2f}'.format(BM[0].z)
    txt="Blickrichtung: " + x + "/" + y + "/" + z
    txt=txt + "\n" + "Horizont : " + a + "/" + b + "/" + c +"/" 
    Label_BM.config(text=txt)

def Beobachter_bewegen(Achse,Richtung):
    global BV, BV_Schritt
    if   Richtung == "+": Schritt = BV_Schritt
    elif Richtung == "-": Schritt = - BV_Schritt
    if   Achse == "x": BV[0].x += Schritt
    elif Achse == "y": BV[0].y += Schritt
    elif Achse == "z": BV[0].z += Schritt
    v=str((BV[0].x**2 + BV[0].y**2 + BV[0].z**2)**0.5)
    txt="Beobachtertempo: " + v
    Label_BV.config(text=txt)

def neu_laden():
    global fn
    Data.Laden(fn)

def löschen():
    w.delete("all")

def Info(txt):
    info = Tk()
    T = Text(info, height=2, width=80)
    T.pack()
    T.insert(END, txt)
    T.mainloop()

def Bestätigung(txt):
    # Öffnet Ein Auswahlfeld für ja oder nein
    Eingabe = Toplevel()
    Eingabe.geometry('300x100+0+0')
    v = StringVar()
    Label(Eingabe,text=txt,justify = LEFT,padx = 50).pack()
    Radiobutton(Eingabe,text="Ja",indicatoron = 0,width = 40,padx = 50,variable=v,command=Eingabe.quit,value="j").pack(anchor=W)
    Radiobutton(Eingabe,text="Nein",indicatoron = 0,width =40,padx = 50,variable=v,command=Eingabe.quit,value="n").pack(anchor=W)
    Eingabe.mainloop()
    antwort=v.get()
    Eingabe.destroy()
    return(antwort)

def Auswahl(Labels):
    # Öffnet Ein Auswahlfeld für eine Liste von Argumenten
    Eingabe = Toplevel()
    v = StringVar()
    Label(Eingabe,text="Bitte wählen",justify = LEFT,padx = 50).pack()
    for label in Labels:
        Radiobutton(Eingabe,text=label,indicatoron = 0,width = 50,padx = 50,variable=v,command=Eingabe.quit,value=label).pack(anchor=W)
    Eingabe.mainloop()
    antwort=v.get()
    Eingabe.destroy()
    return(antwort)
    
def Datei_wählen(match):
    # Zeigt Dateien an, auf die match passt, lässt eine auswählen, und gibt den Dateinahmen als Returnwert zurück
    datei = Toplevel()
    v = IntVar()
    V=[]
    x=0
    Label(datei,text="""Bitte Datei auswählen:""",justify = LEFT,padx = 50).pack()
    for txt in os.listdir("Simulator"):
        if re.search(match,txt):
            V.append(txt)
            Radiobutton(datei, 
                text=txt,
                indicatoron = 0,
                width = 50,
                padx = 50, 
                variable=v, 
                command=datei.quit,
                value=x).pack(anchor=W)
            x=x+1
    mainloop()
    fn=V[v.get()]
    datei.destroy()
    return(fn)

def Eingabe(Labels):
    # Öffnet Ein textfeld zur Eingabe beliebig vieler Parameter, Ergebnis wird als Liste zurückgegeben
    entries=[]
    resultat=[]
    Eingabe = Toplevel()
    for field in Labels:
        L=field.split("=")
        row = Frame(Eingabe)
        lab = Label(row, width=60, text=L[0], anchor='w')
        ent = Entry(row)
        ent.insert(10,L[1])
        row.pack(side=TOP, fill=X, padx=5, pady=5)
        lab.pack(side=LEFT)
        ent.pack(side=RIGHT, expand=YES, fill=X)
        entries.append((L[0], ent))

    #Eingabe.bind('<Return>', (lambda event))   
    b1 = Button(Eingabe, text='Fertig',command=(Eingabe.quit))
    b1.pack(side=LEFT, padx=5, pady=5)
    Eingabe.mainloop()

    for entry in entries:
       field = entry[0]
       text  = entry[1].get()
       resultat.append(text)
    Eingabe.destroy()
    return(resultat)

def Neue_Objekte() :
    L=[]
    L=Eingabe(["Name des neuen Objekts="])
    obj='Simulator/' + L[0] + '_obj.txt'
    
    #Prüfe Existenz von Verzeichnis
    if (os.path.isdir('Simulator') == False):
         os.mkdir('Simulator')

    #Prüfe Existenz von Datei
    if (os.path.isfile(obj)):
        if Bestätigung("Datei existiert, soll sie überschrieben werden ?") == "n":
            return(1)

    f=open(obj,"w+")
    form=Auswahl(["Kreis","Kugel","Quader","Pyramide","Freie Modelierung"])
    if ('Kugel' in form):
        L=Eingabe(["Tiefe in Meter=1","Breite in Meter=1","Höhe in Meter=1","Teilung=10"])
        t=float(L[0])
        b=float(L[1])
        h=float(L[2])
        teilung=int(L[3])
        
        #Verschachtelte Schleife, um die Kugel zu konstruieren
        if teilung == "": teilung=30
        s=2 * math.pi / teilung
        wxz=0
        p=0
        while wxz < teilung:
            wxy=0
            while wxy < teilung:
                p=teilung * wxz + wxy
                y=0.5 * math.sin(s * wxy) * h
                x=0.5 * math.cos(s * wxy) * math.cos(wxz) * b
                z=0.5 * math.sin(s * wxz) * math.sin(wxy) * t
                line= "P " + str(x) + " " + str(y) + " " + str(z) + "\n"
                if wxy>0: line=line + " L " + str(p-1) + " " + str(p) + "\n"
                else: line=line + " L " + str(p) + " " + str(p+teilung -1) + "\n"
                if wxz > 0:
                    line=line + " L " + str(p) + " " + str(p - teilung) + "\n"
                f.write(line)
                wxy += 1
            wxz += 1
    elif ('Kreis' in form):
        L=Eingabe(["Breite in Meter=1","Höhe in Meter=1","Teilung=10"])
        b=float(L[0])
        h=float(L[1])
        teilung=int(L[2])
        
        # Schleife, um den Kreis zu konstruieren
        if teilung == "": teilung=30
        s=2 * math.pi / teilung
        p=0
        while p < teilung:
            y=0.5 * math.sin(s * p) * h
            x=0.5 * math.cos(s * p) * b
            line= "P " + str(x) + " " + str(y) + " 0\n"
            if p>0: line=line + " L " + str(p-1) + " " + str(p) + "\n"
            f.write(line)
            p += 1
        line=line + " L 0 " + str(p-1) + "\n"
        f.write(line)    
    elif('Quader' in form):
        L=Eingabe(["Tiefe in Meter=1","Breite in Meter=1","Höhe in Meter=1"])
        t=str(float(L[0])/2)
        b=str(float(L[1])/2)
        h=str(float(L[2])/2)
        line= "P -" + b + " -" + h + " -" + t + "\n"
        line= line + "P -" + b + " -" + h + " " + t + "\n"
        line= line + "P " + b + " -" + h + " " + t + "\n"
        line= line + "P " + b + " -" + h + " -" + t + "\n"
        line= line + "P -" + b + " " + h + " -" + t + "\n"
        line= line + "P -" + b + " " + h + " " + t + "\n"
        line= line + "P " + b + " " + h + " -" + t + "\n"
        line= line + "P " + b + " " + h + " " + t + "\n"
        line= line + "L 0 1\nL 1 2\nL 2 3\nL 3 0\n"
        line= line + "L 4 5\nL 5 6\nL 6 7\nL 7 4\n"
        line= line + "L 0 4\nL 1 5\nL 2 6\nL 3 7\n"
        f.write(line)
    elif('Pyramide' in form):
        L=Eingabe(["Tiefe in Meter=1","Breite in Meter=1","Höhe in Meter=1"])
        t=float(L[0])
        b=float(L[1])
        h=float(L[2])
        line= "P " + str(-0.5*b) + " " + str(-0.25*h) + " " + str(-0.5*t) + "\n"
        line= line + "P " + str(0.5*b) + " " + str(-0.25*h) + " " + str(-0.5*t) + "\n"
        line= line + "P " + str(0.5*b) + " " + str(-0.25*h) + " " + str(0.5*t) + "\n"
        line= line + "P " + str(-0.5*b) + " " + str(-0.25*h) + " " + str(0.5*t) + "\n"
        line= line + "P 0 "  + str(0.75*h) + " 0\n"
        line= line + "L 0 1\nL 1 2\nL 2 3\nL 3 0\n"
        line= line + "L 0 4\nL 1 4\nL 2 4\nL 3 4\n"
        f.write(line)
    elif('Freie Modelierung' in form):
        # Wird nur zur Anpassung der Punkte genutzt, um den Schwerpunkt zu 0/0/0 zu verschieben
        L=Eingabe(["Schwerpunkt des Objekts X-Achse=0", "Schwerpunkt des Objekts Y-Achse=0", "Schwerpunkt des Objekts Z-Achse=0"])
        x=float(L[0])
        y=float(L[1])
        z=float(L[2])
        f.write("SP 0 0 0\n")

        #Eingabe der Punkte
        L=Eingabe(["Anzahl der Punkte=", "Anzahl der Linien="])
        zeile=[]
        p=int(L[0]) # Anzahl der Punkte
        l=int(L[1]) # Anzahl der Linien
        for i in range(0, p):
            txt="Punkt " + str(i) + " X-Koordinate="
            zeile.extend([txt])
            txt="Punkt " + str(i) + " Y-Koordinate="
            zeile.extend([txt])
            txt="Punkt " + str(i) + " Z-Koordinate="
            zeile.extend([txt])
        L=Eingabe(zeile)
        zeile=[]
        for i in range(0, p):
            line="0" + " P " + str(float(L[i*3])-x) + " " + str(float(L[i*3 + 1])-y) + " " + str(float(L[i*3 + 2])-z) + "\n"
            f.write(line)

        # Eingabe der Linien
        for i in range(0, l):
            txt="Linie " + str(i) + " Startpunkt="
            zeile.extend([txt])
            txt="Linie " + str(i) + " Endpunkt="
            zeile.extend([txt])
        L=Eingabe(zeile)
        for i in range(0, l):
            line="0" + " L " + L[i*2] + " " + L[i*2 + 1] + "\n"
            f.write(line)     
        f.close()
            
def Neue_Simulation(modus):
    global LBL, pstart, M, R, pz, lz, oz, zeit, lfz, t_alt, P, Kalibrierung, G
    global R, V, O, F, ROTATION, BM, BP, BV, S, NAME, LINIE, SKALIERUNG, ZOOM
    global UPS, D, ZK, Z, ROJEKTION, Anzahl_Objekte, BV_Schritt

    if modus == "ändern": sim="Simulator/" + Datei_wählen("_sim")
    elif modus == "neu":  
        L=[]
        L=Eingabe(["Name der Simulation="])
        sim='Simulator/' + L[0] + '_sim.txt'
        if (os.path.isfile(sim)) and Bestätigung("Datei existiert bereits, soll sie überschrieben werden ?") == "n": return(1)
            
    #Prüfe Existenz von Verzeichnis
    if (os.path.isdir('Simulator') == False): os.mkdir('Simulator')

    # Setzen der Defaultwerte bzw Laden der aktuellen Werte
    if modus == "ändern": Data.Laden(sim)
    else:
        ZK=1
        Z=1
        BP.append(Data(0,"", 0, 0, 0))
        BM.append(Data(0, "", 1, 0, 0))
        BM.append(Data(0, "", 0, 1, 0))
        BM.append(Data(0, "", 0, 0, 1))
        BV.append(Data(0, "", 0, 0, 0))
        BV_Schritt=1
        ZOOM=1
        UPS.append(Data(0))
        ROTATION.append(Data(0, "", 0, 0, 0))
        
    # Spezifiziere Beobachtungssystem
    fs=open(sim,"w+")
    objnr=0
    L=["Beobachterposition X/Y/Z="+str(BP[0].x) + "/" + str(BP[0].y) + "/" +str(BP[0].z)]
    L.extend(["Beobachterkoordinatensystem X-Achse: X/Y/Z="+str(BM[0].x) + "/" +str(BM[0].y) + "/" + str(BM[0].z)])
    L.extend(["Beobachtekoordinatensystemx Y-Achse: X/Y/Z="+str(BM[1].x) + "/" + str(BM[1].y) + "/" + str(BM[1].z)])
    L.extend(["Beobachterkoordinatensystem Z-Achse: X(Y(Z="+str(BM[2].x) + "/" + str(BM[2].y) + "/" + str(BM[2].z)])
    L.extend(["Beobachtergeschwindigkeit in X/Y/Z="+str(BV[0].x) + "/" + str(BV[0].y) + "/" + str(BV[0].z)])
    L.extend(["Simulierte Zeit zwischen den Simulationsschritten in Sekunde="+str(Z),"Anzahl der zu berechnenden Zyklen="+str(ZK)])
    L.extend(["Zoomfaktor="+str(ZOOM), "Schrittweite der Änderung der Beobachtergeschwindigkeit="+str(BV_Schritt)])
    L=Eingabe(L)
    zeile=L[0].split("/")
    line=str(objnr) + " BP " + zeile[0] + " " +  zeile[1] + " " + zeile[2] + "\n"
    fs.write(line)
    zeile=L[1].split("/")    
    line=str(objnr) + " BM " + zeile[0] + " " +  zeile[1] + " " + zeile[2] + "\n"
    fs.write(line)
    zeile=L[2].split("/")  
    line=str(objnr) + " BM " + zeile[0] + " " +  zeile[1] + " " + zeile[2] + "\n"
    fs.write(line)
    zeile=L[3].split("/")  
    line=str(objnr) + " BM " + zeile[0] + " " +  zeile[1] + " " + zeile[2] + "\n"
    fs.write(line)
    zeile=L[4].split("/")  
    line=str(objnr) + " BV " + zeile[0] + " " +  zeile[1] + " " + zeile[2] + "\n"
    fs.write(line)
    line="0 Z " + L[5] + "\n"
    fs.write(line)
    line="0 ZK " + L[6] + "\n"
    fs.write(line)
    line="0 ZOOM " + L[7] + "\n"
    fs.write(line)
    line="0 BVS " + L[8] + "\n"
    fs.write(line)
    L=["Zentralprojektion", "Parallelprojektion", "Projektion_auf_Kugel"]
    antwort=Auswahl(L)
    line="0 PROJEKTION " + antwort + "\n"
    fs.write(line)
    
    #Lese Objekte aus Katalog ein, und individualisiere sie
    while True :
        obj=Datei_wählen("_obj")
        obj='Simulator/' + obj
        F.append(Data(0, "black"))
        O.append(Data(0, "", 0, 0, 0))
        V.append(Data(0, "", 0, 0, 0))
        ROTATION.append(Data(0,"", 0, 0, 0))
        M.append(Data(1))
        SKALIERUNG.append(Data(0, "", 1, 1, 1))
        NAME.append(Data(0, "?"))
        UPS.append(Data(0))
        
        L=["Farbe (Englisch klar oder #...... als RGB Wert="+F[objnr].txt]
        L.extend(["Skalierung Achsen X/Y/Z=" +str(SKALIERUNG[objnr].x) + "/" + str(SKALIERUNG[objnr].y) + "/" + str(SKALIERUNG[objnr].z)])
        L.extend(["Startposition des Objekts X/Y/Z="+str(O[objnr].x) + "/" + str(O[objnr].y) + "/" + str(O[objnr].z)])
        L.extend(["Geschwindigkeit in X/Y/Z="+str(V[objnr].x) +"/" + str(V[objnr].y) + "/" + str(V[objnr].z)])
        L.extend(["Masse in Kg="+str(M[objnr].objnr),"Objektbezeichnung="+NAME[objnr].txt,"Umdrehungen pro Sekunde (- für Linksdehung)="+str(UPS[objnr].objnr)])
        L.extend(["Rotationsvektor X/Y/Z="+str(ROTATION[objnr].x) + "/" +str(ROTATION[objnr].y) + "/" + str(ROTATION[objnr].z)])
        L=Eingabe(L)
        sk=L[1].split("/")
        line=str(objnr) + " SKALIERUNG " + sk[0] + " " + sk[1] + " " + sk[2] + "\n"
        fs.write(line)
        line=str(objnr) + " F " + L[0] + "\n"
        fs.write(line)
        zeile=L[3].split("/")
        line=str(objnr) + " V " + zeile[0] + " " + zeile[1] + " " + zeile[2] + "\n"
        fs.write(line)
        zeile=L[2].split("/")
        line=str(objnr) + " O " + zeile[0] + " " + zeile[1] + " " + zeile[2] + "\n"
        fs.write(line)
        line=str(objnr) + " M " + L[4] + "\n"
        fs.write(line)
        line=str(objnr) + " NAME " + L[5] + "\n"
        fs.write(line)
        line=str(objnr) + " UPS " + L[6] + "\n"
        fs.write(line)

        # Normierung des Rotationsvektors
        zeile=L[7].split("/")
        n1=float(zeile[0])
        n2=float(zeile[1])
        n3=float(zeile[2])
        betrag=(n1**2 + n2**2 + n3**2)**0.5
        if betrag > 0:
            n1 = n1 / betrag
            n2 = n2 / betrag
            n3 = n3 / betrag
            line=str(objnr) + " ROTATION " + str(n1) + " " + str(n2) + " " + str(n3) + "\n"
            fs.write(line)
                   
        # schreibe adaptierte Objektdaten in Simulationsdatei    
        fo=open(obj,"r")
        for l in fo:
            zeile=l.split()
            if (zeile[0] == "P"):
                x=str(float(sk[0]) * float(zeile[1]))
                y=str(float(sk[1]) * float(zeile[2]))
                z=str(float(sk[2]) * float(zeile[3]))
                line=str(objnr) + " P " + x + " " + y + " " + z + "\n"
                fs.write(line)
            else:
                line=str(objnr) + " " + l
                fs.write(line)
        fo.close()
        
        objnr +=1
        if modus == "ändern" and Anzahl_Objekte > objnr:
            txt="Alte Simulation enthält mehr Objekte, trotzdem beenden ?"
        else:
            txt="Ist die Simulation nun fertig ?"
        if Bestätigung(txt) == "j":
            fs.write(line)
            fs.close()
            break
            
def Simulation():
    global Kollisionswarnung, counter, Zyklen, ZK, log
    
    # Wähle Datei aus und öffne sie
    fn="Simulator/" + Datei_wählen("_sim")
    Data.Laden(fn)

    # Lege Logfile an
    logfile=fn.replace("_sim","_log",1)
    log=open(logfile,"w+")
    
    # Schleife durch alle Zyklen
    Kollisionswarnung=1
    counter=Zyklen=0
    Data.Kalibrierung()
    Data.Zyklus_berechnen()
    Zyklen += 1        
    while Zyklen < ZK:
        #Data.Zyklus_berechnen()
        w.after(100, Data.Zyklus_berechnen())
        Zyklen += 1        
        if Zyklen >= ZK:
            Zeit,Einheit=Zeit_anpassen(lfz)
            line="Laufzeit: " + '{:.2f}'.format(Zeit) + Einheit + "\nWeitere(n) " + str(ZK) + " Zyklen starten ?"
            if Bestätigung(line) == "j": Zyklen=0

    log.close()


os.chdir("/Users/Lenovo/Google Drive/Dokumente/Software/Python")
master = Tk()
master.geometry('1200x800')
master.title("Mein Simulator")
w = Canvas(master,width=1200,height=800)
w.pack
LabelZyklen = Label(master=master, bg='#FFCFC9', text='0')
LabelZyklen.place(x=1000, y=0, width=200, height=27)
button = Button(master, text='Plot löschen', width=20, command=löschen)
button.place(x=800, y=0, width=100, height=25)
button = Button(master, text='Neu laden', width=20, command=neu_laden)
button.place(x=900, y=0, width=100, height=25)
button = Button(master, text='Hoch drehen\nX-Achse Links', width=100, command=lambda: Beobachter_drehen("x","links"))
button.place(x=800, y=25, width=100, height=50)
button = Button(master, text='Runter drehen\nX-Achse Rechts', width=100, command=lambda: Beobachter_drehen("x","rechts"))
button.place(x=900, y=25, width=100, height=50)
button = Button(master, text='Links drehen\nY-Achse Links', width=100, command=lambda: Beobachter_drehen("y","links"))
button.place(x=800, y=75, width=100, height=50)
button = Button(master, text='Rechts drehen\nY-Achse Rechts', width=100, command=lambda: Beobachter_drehen("y","rechts"))
button.place(x=900, y=75, width=100, height=50)
button = Button(master, text='Links drehen\nZ-Achse Links', width=100, command=lambda: Beobachter_drehen("z","links"))
button.place(x=800, y=125, width=100, height=50)
button = Button(master, text='Ansicht drehen\nZ-Achse Rechts', width=100, command=lambda: Beobachter_drehen("z","rechts"))
button.place(x=900, y=125, width=100, height=50)
button = Button(master, text='Rechts bewegen\nX-Achse', width=100, command=lambda: Beobachter_bewegen("x","+"))
button.place(x=900, y=175, width=100, height=50)
button = Button(master, text='Links bewegen\nX-Achse', width=100, command=lambda: Beobachter_bewegen("x","-"))
button.place(x=800, y=175, width=100, height=50)
button = Button(master, text='Hoch bewegen\nY-Achse', width=100, command=lambda: Beobachter_bewegen("y","+"))
button.place(x=900, y=225, width=100, height=50)
button = Button(master, text='Runter bewegen\nY-Achse', width=100, command=lambda: Beobachter_bewegen("y","-"))
button.place(x=800, y=225, width=100, height=50)
button = Button(master, text='Vorwärts bewegen\nZ-Achse', width=100, command=lambda: Beobachter_bewegen("z","+"))
button.place(x=900, y=275, width=100, height=50)
button = Button(master, text='Rückwärts bewegen\nZ-Achse', width=100, command=lambda: Beobachter_bewegen("z","-"))
button.place(x=800, y=275, width=100, height=50)
Label_BP = Label(master=master, text='Beobachterposition')
Label_BP.place(x=800, y=325, width=200, height=25)
Label_BV = Label(master=master, text='Beobachtergeschwindigkeit')
Label_BV.place(x=800, y=350, width=200, height=25)
Label_BM = Label(master=master, text='Blickrichtung')
Label_BM.place(x=800, y=375, width=200, height=50)


menu = Menu(master)
master.config(menu=menu)
filemenu = Menu(menu)
menu.add_cascade(label="Simulation", menu=filemenu)
filemenu.add_command(label="Starte existierende Simulation", command=Simulation)
filemenu.add_command(label="Existierende Simulation verändern", command= lambda: Neue_Simulation("ändern"))
filemenu.add_command(label="Definiere neue Simulation", command= lambda: Neue_Simulation("neu"))
filemenu.add_command(label="Definiere neue Objektmaster", command=Neue_Objekte)
filemenu.add_separator()
filemenu.add_command(label="Exit", command=master.quit)
master.mainloop()
Benutzeravatar
__blackjack__
User
Beiträge: 13080
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Rolando1964: Du erwartest das sich jetzt jemand ernsthaft mit diesem Quelltext beschäftigt? Wenn Du BASIC Programme im Stil der 80er Jahre schreiben möchtest, dann schreibe die in einem BASIC-Dialekt aus den 80ern und nicht in Python. Das sieht so aus als würdest Du absichtlich gegen alles anprogrammieren was in Python üblich und auch allgemein als saubere Programmierung angesehen wird.

Ein Anfang wäre: Keine Sternchen-Importe, kein ``global`` → keine Variablen auf Modulebene, vernünftige Namen, die sich zudem an die Namenskonventionen halten. Der Style Guide for Python Code kann hilfreich sein.

Dann wäre es nett wenn Du den Code auf das tatsächliche Problem eindampfst, und es besser beschreibst. Etwas von einer Queue zu erzählen, bei einem Quelltext in dem soweit ich das sehe keiner der beiden Datentypen vorkommt, an die man da sofort denkt, aber anscheinend auch keine Liste als Queue missbraucht wird, in dem nicht einmal das Wort „queue“ irgendwo vorkommt, ist erst einmal nicht so hilfreich.

Du erwähnst `after()` – da ist zumindest schon mal falsch das Du als zweites Argument den *Rückgabewert* von `Data.Zyklus_berechnen()` übergibst. Der scheint entweder `None` zu sein, oder 0 falls das eine explizite ``return`` ausgeführt wird. Komische Wahl. Auf jeden Fall kann man weder `None` noch 0 aufrufen, also ist das sinnlos das `after()` als zweites Argument zu übergeben. Da muss etwas aufrufbares übergeben werden. Wahrscheinlich willst Du da `Data.Zyklus_berechnen` selbst übergeben – ohne es aufzurufen. Das ist genau der gleiche Unterschied wie bei C zwischen der Funktion selbst und einem Aufruf der Funktion.

Was auch falsch aussieht ist die Schleife in der der `after()`-Aufruf passiert der anscheinend mehrere dieser Aufrufe nach 100ms ausgeführt haben möchte. Die werden dann alle nach 100ms direkt hintereinander, ohne Wartezeit ausgeführt. Und ich weiss ja nicht wie viele das sind, aber dafür ist das eigentlich nicht gedacht, da viele Rückrufe zu planen die alle zur ”gleichen” Zeit ausgeführt werden sollen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi Rolando1964

Willkommen in unserem Forum. Könntest du uns die Datei einer existierende Versuchssimulation zur Verfügung stellen. Da ich nicht vom Fach bin ist die Eingabe fiktiver Daten für mich schwierig. Am besten eine Simulation die schon eine Grafik ins Fenster bringt:

Gruss wuf :-)
Take it easy Mates!
Rolando1964
User
Beiträge: 2
Registriert: Dienstag 14. Mai 2019, 21:21

Danke erstmal, bin gerade erst am Erlernen von Python, und experimentiere noch rum, auch der 80er Jahre Style (stimmt exakt mit meiner Computersozialisierung überein) wird schrittweise modenisiert.

Was ich nicht verstehe, ist die Tatsache, das die pack Anweisung der Linien nicht unverzüglich vom GUI umgesetzt wird, sondern erst nach ein paar tausend pack Anweisungen, wenn die Zyklen beendet und das Bestätigungsfenster aufgerufen wird. Ich hab das so verstanden, das pack die Anweisungen in eine Warteschlange des Grafiksystems schiebt, wo sie unabhängig vom fütternden Programm abgearbeitet werden. Tut es aber leider nicht, sondern zeichnet erst alles zum Schluss, wenn alle pack Anweisungen abgesetzt worden sind.

Ein Beispieldatensatz für eine Simulation des Zusammenspiels von Erde und Mond wäre folgende Datei:

0 BP 0.0 0.0 -1000000000.0
0 BM 1.0 0.0 0.0
0 BM 0.0 1.0 0.0
0 BM 0.0 0.0 1.0
0 BV 0.0 0.0 0.0
0 Z 3600.0
0 ZK 10000.0
0 ZOOM 0.5
0 BVS 1.0
0 PROJEKTION Zentralprojektion
0 SKALIERUNG 12000000.0 12000000.0 12000000.0
0 F blue
0 V 0.0 0.0 0.0
0 O 0.0 0.0 0.0
0 M 6e+24
0 NAME Erde
0 UPS 0.0
0 P 6000000.0 0.0 0.0
0 P 4854101.966249685 3526711.5137548386 0.0
0 L 0 1
0 P 1854101.9662496848 5706339.097770921 0.0
0 L 1 2
0 P -1854101.966249684 5706339.097770922 0.0
0 L 2 3
0 P -4854101.966249684 3526711.5137548395 0.0
0 L 3 4
0 P -6000000.0 7.347880794884119e-10 0.0
0 L 4 5
0 P -4854101.966249685 -3526711.513754838 0.0
0 L 5 6
0 P -1854101.9662496853 -5706339.097770921 0.0
0 L 6 7
0 P 1854101.9662496834 -5706339.097770922 0.0
0 L 7 8
0 P 4854101.966249684 -3526711.51375484 0.0
0 L 8 9
0 P 4854101.966249684 -3526711.51375484 0.0
0 L 8 9
0 L 0 9
0 SP 0 0 0
1 SKALIERUNG 3400000.0 3400000.0 3400000.0
1 F black
1 V 1000.0 0.0 100.0
1 O -10000000.0 300000000.0 0.0
1 M 7.3e+22
1 NAME Mond
1 UPS 0.0
1 P 1700000.0 0.0 0.0
1 P 1375328.8904374107 999234.9288972043 0.0
1 L 0 1
1 P 525328.8904374107 1616796.077701761 0.0
1 L 1 2
1 P -525328.8904374104 1616796.0777017612 0.0
1 L 2 3
1 P -1375328.8904374104 999234.9288972046 0.0
1 L 3 4
1 P -1700000.0 2.0818995585505005e-10 0.0
1 L 4 5
1 P -1375328.8904374107 -999234.9288972041 0.0
1 L 5 6
1 P -525328.8904374109 -1616796.077701761 0.0
1 L 6 7
1 P 525328.8904374103 -1616796.0777017612 0.0
1 L 7 8
1 P 1375328.8904374104 -999234.9288972047 0.0
1 L 8 9
1 P 1375328.8904374104 -999234.9288972047 0.0
1 L 8 9
1 L 0 9
1 SP 0 0 0
1 SP 0 0 0
Benutzeravatar
__blackjack__
User
Beiträge: 13080
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Rolando1964: `pack()` macht etwas ganz anderes. Das sorgt dafür dass das Widget auf dem es aufgerufen wird, überhaupt angezeigt wird, und bestimmte wie es angeordnet wird (mit dem pack-Layout). Und man ruft das nur einmal pro Widget auf. Mehr Aufrufe machen keinen Sinn. Ich wüsste jetzt nicht einmal aus dem Kopf was dann passiert – ob das Widget von der alten Position entfernt, und neu gepackt wird beispielsweise.

Gezeichnet wird wenn die GUI-Hauptschleife läuft. Und die läuft halt nicht solange *Dein* Code läuft. Dein Code darf nur kurz etwas tun und muss dann zur GUI-Hauptschleife zurückkehren. Wenn Du lang laufende Schleifen hast, die etwas mit der GUI tun sollen, musst Du die so umprogrammieren, dass Du einen Methodenaufruf pro (ehemaligen) Schleifendurchlauf hast, der dann mit `after()` so oft als Rückruffunktion gesetzt wird, bis alles abgearbeitet ist. Und wie gesagt: `after()` braucht als zweites Argument etwas aufrufbares was es *später* aufrufen kann, und nich das Ergebnis dieses Aufrufs bevor überhaupt `after()` aufgerufen wird.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi Rolando1964

Hier mein Vorschlag für die GUI Erstellung deiner Anwendung in OOP:

Code: Alles auswählen

from functools import partial
import tkinter as tk

'''
a) Sternchen Importe vergessen!!!
   z.B:
   Falsch: from tkinter import *
   Richtig: import tkinter as tk (oder ein selbst gewählter Name oder Kürzel)
b) Die Breite und Höhe des Fensters passt sich dem Inhalt an.
c) Namenskonvension für Klassen beachten!
d) Namenkonvension für Methoden beachten!
e) Für die Codierung nur Namen mit Sinn verwenden nicht einzelne Buchstaben!
f) Für GUI's nur OOP!!!
g) Wenn möglich für die Codierung englische Namen verwenden!
h) Global nur Konstanten!!!
i) Aufwendige Berechnungen in Thread auslagern!
j) Tk() nur einmal verwenden. Für weiter Fenster nur das 'Toplevel'-Widget!!!
k) WICHTIG: unbedingt den Leitfaden PEP 8 für Pythonprogrammierung studieren!


Definition für die Ausgabe in ein Label dieser Anwendung
    label_index = self.LABELS.index(Hier der Name des Labels)
    label_var = self.label_vars[label_index]
    label_var.set(Hier den Wert)
'''


APP_TITLE = "Mein Simulator"
APP_XPOS = 100
APP_YPOS = 100
APP_WIDTH = 350
APP_HEIGHT = 200
CANVAS_WIDTH = 1200
CANVAS_HEIGHT = 800

        
class Application(object):
    BUTTONS = [
        ['Plot löschen', 'Neu laden'],
        ['Hoch drehen\nX-Achse Links', 'Runter drehen\nX-Achse Rechts'],
        ['Links drehen\nY-Achse Links', 'Rechts drehen\nY-Achse Rechts'],
        ['Links drehen\nZ-Achse Links', 'Ansicht drehen\nZ-Achse Rechts'],
        ['Links bewegen\nX-Achse', 'Rechts bewegen\nX-Achse'],
        ['Runter bewegen\nY-Achse', 'Hoch bewegen\nY-Achse'],
        ['Rückwärts bewegen\nZ-Achse', 'Vorwärts bewegen\nZ-Achse']
        ]
            
    BUTTON_ARGS = [
        ['clear', 'load'],
        [["x","links"], ["x","rechts"]],
        [["y","links"], ["y","rechts"]],
        [["z","links"], ["z","rechts"]],
        [["x","-"], ["x","+"]],
        [["y","-"],["y","+"]],
        [["z","-"],["z","+"]]
        ]
    BOTTON_FONT = ('Helvetica', 10, 'normal')
        
    LABELS = [
        'Beobachterposition',
        'Beobachtergeschwindigkeit',
        'Blickrichtung',
        'Zyklen'
        ]
    LABEL_FONT = ('Helvetica', 10, 'bold')
      
    def __init__(self, main_win):
        self.main_win = main_win
        self.main_win.protocol("WM_DELETE_WINDOW", self.close_app)
        
        self.fn = None
        self.G=6.672*10 ** (-11)
        self.LBL=[]
        self.M=[]
        self.R=dict()
        self.pstart=dict()
        self.pz=lz=oz=Anzahl_Objekte=zeit=lfz=0
        self.t_alt=-1
        self.P=[]
        self.Kalibrierung=0
        self.R[0]=0
        self.V=[]
        self.O=[]
        self.F=[]
        self.ROTATION=[]
        self.BM=[]
        self.BP=[]
        self.BV=[]
        self.S=[]
        self.NAME=[]
        self.LINIE=[]
        self.SKALIERUNG=[]
        self.ZOOM=[]
        self.UPS=[]
        self.ZK=0
        self.Z=0
        self.BV_Schritt=1
        self.counter=1
        self.Zyklen=0
        self.lfz=0
        self.PROJEKTION="Zentralprojektion"
        
        self.label_vars = list()
        
        self.build_gui()
        
        # Demo für Label-Ausgabe
        self.trial_loop()
        
    def build_gui(self):
        # Menu erstellen
        menu = tk.Menu(self.main_win)
        self.main_win.config(menu=menu)
        filemenu = tk.Menu(menu)
        menu.add_cascade(label="Simulation", menu=filemenu)
        filemenu.add_command(label="Starte existierende Simulation",
            command=self.Simulation)
        filemenu.add_command(label="Existierende Simulation verändern",
            command= lambda: self.Neue_Simulation("ändern"))
        filemenu.add_command(label="Definiere neue Simulation",
            command= lambda: self.Neue_Simulation("neu"))
        filemenu.add_command(label="Definiere neue Objektmaster",
            command=self.Neue_Objekte)
        filemenu.add_separator()
        filemenu.add_command(label="Exit",
            command=self.main_win.quit)

        self.main_frame = tk.Frame(self.main_win)
        self.main_frame.pack(fill='both', expand=True)

        self.canvas = tk.Canvas(self.main_frame, width=CANVAS_WIDTH,
            height=CANVAS_HEIGHT, bg='steelblue', highlightthickness=0)
        self.canvas.pack(side='left')

        command_frame = tk.Frame(self.main_frame)
        command_frame.pack()
        
        button_frame = tk.Frame(command_frame)
        button_frame.pack()
        
        for row, button_name_pair in enumerate(self.BUTTONS):
            # Linke Schaltflächenreihe
            left_button_name = button_name_pair[0]
            tk.Button(button_frame, text=left_button_name,
                font=self.BOTTON_FONT, command=partial(
                    self.button_callback, left_button_name,
                    self.BUTTON_ARGS[row][0])
                ).grid(row=row, column=0, sticky='we')
            # Rechte Schaltflächenreihe
            right_button_name = button_name_pair[1]
            tk.Button(button_frame, text=right_button_name,
                font=self.BOTTON_FONT, command=partial(
                    self.button_callback, right_button_name,
                    self.BUTTON_ARGS[row][1])
                ).grid(row=row, column=1, sticky='we')
        
        label_frame = tk.Label(command_frame)
        label_frame.pack()
        
        for row, label_name in enumerate(self.LABELS):
            label_frame = tk.LabelFrame(label_frame, text=label_name, bd=0,
                font=self.LABEL_FONT, fg='steelblue')
            label_frame.pack(fill='x')
            label_var = tk.StringVar(label_frame, 0)
            label = tk.Label(label_frame, textvariable=label_var)
            label.pack(fill='x')
            self.label_vars.append(label_var)
            if label_name == 'Zyklen':
                label.configure(bg='#FFCFC9')
                
    def neu_laden(self):
        print('Neu laden')
        #Data.Laden(self.fn)

    def delete(self):
        print('Löschen')

    def beobachter_bewegen(self, axis, action):
        print('Bewegen:', action, axis)
    
    def beobachter_drehen(self, axis, action):
        print('Drehen:', action, axis)
        
    def button_callback(self, button_name, args):
        if args == 'load':
            self.neu_laden()
        elif args == 'clear':
            self.delete()
        else:
            axis, action = args
            #print(action, axis)
            if action == 'links' or action == 'rechts':
                #print(axis)
                self.beobachter_drehen(axis, action)
            elif action == '-' or action == '+':
                #print(axis)
                self.beobachter_bewegen(axis, action)
        
    def Simulation(self):
        pass
        
    def Neue_Simulation(self):
        pass
    
    def Neue_Objekte(self):
        pass

    # Nur für Versuch
    def trial_loop(self, count=0):
        # Definition für die Ausgabe in ein Anwendunglabels
        label_index = self.LABELS.index('Zyklen')
        label_var = self.label_vars[label_index]
        label_var.set(count)
        
        count += 1
        if count > 50: return
        
        self.main_win.after(200, self.trial_loop, count)
        
    def close_app(self):
        self.main_win.withdraw()
        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()      
Gruss wuf :-)
Take it easy Mates!
Benutzeravatar
__blackjack__
User
Beiträge: 13080
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@wuf: Es ist aber noch nicht so wirklich viel gewonnen wenn man die ca. 40 globalen Variablen jetzt alle in eine Gottklasse verschiebt. Da muss deutlich mehr Struktur in Code und Daten. :-)

@all: Die 40 habe ich jetzt nicht durch manuelles zählen heraus bekommen, nein ich hab's mir viel leichter gemacht und ein kleines Programm geschrieben das nach dem Importieren da mal ein bisschen analysiert. Okay, zählen wäre wahrscheinlich schneller gegangen, aber es hätte deutlich weniger Spass gemacht. Hier das Analyseprogramm:

Code: Alles auswählen

#!/usr/bin/env python3
from importlib import import_module
from inspect import (
    getclosurevars, getsourcelines, isclass, isfunction, ismodule, Signature
)
from textwrap import wrap

from attr import attrib, attrs


def case_insensitive_key_func(string):
    return (string.casefold(), string.swapcase())


def is_dunder(name):
    return name.startswith('__') and name.endswith('__')


def is_public(name):
    return is_dunder(name) or not name.startswith('_')


@attrs
class Vars:
    _name2value = attrib(factory=dict)

    def __len__(self):
        return len(self._name2value)

    def __iter__(self):
        return iter(self._name2value.items())

    def __sub__(self, other):
        other_names = other.names
        return Vars(
            {
                name: value for name, value in self
                if name not in other_names or value != other._name2value[name]
            }
        )

    @property
    def names(self):
        return self._name2value.keys()

    @property
    def values(self):
        return self._name2value.values()

    def add(self, name, value):
        self._name2value[name] = value

    def just_public(self):
        return Vars({name: value for name, value in self if is_public(name)})

    def without_dunder(self):
        return Vars(
            {name: value for name, value in self if not is_dunder(name)}
        )

    def _without_by_value(self, predicates):
        return Vars(
            {
                name: value for name, value in self
                if not any(predicate(value) for predicate in predicates)
            }
        )
    
    def just_variables(self):
        return self._without_by_value([ismodule, isclass, isfunction])

    def without_modules(self):
        return self._without_by_value([ismodule])

    @classmethod
    def from_object(cls, obj, without_dunder=True, just_public=True):
        result = cls(vars(obj))
        if without_dunder:
            result = result.without_dunder()
        return result.just_public() if just_public else result


@attrs(frozen=True)
class CategorizedVars:
    modules = attrib(factory=Vars)
    classes = attrib(factory=Vars)
    functions = attrib(factory=Vars)
    variables = attrib(factory=Vars)

    @classmethod
    def from_vars(cls, variables):
        result = cls()
        for name, value in variables:
            if ismodule(value):
                result.modules.add(name, value)
            elif isclass(value):
                result.classes.add(name, value)
            elif isfunction(value):
                result.functions.add(name, value)
            else:
                result.variables.add(name, value)
        
        for class_ in result.classes.values:
            for value in Vars.from_object(class_, False).values:
                assert not isclass(value), 'oops can not deal with nested class'
                if isfunction(value):
                    result.functions.add(value.__qualname__, value)
    
        return result


def print_names(category_name, names, indent=0):
    if names:
        names_string = ', '.join(sorted(names, key=case_insensitive_key_func))
        lines = wrap(
            f'{category_name} ({len(names)}): {names_string}.',
            initial_indent=' ' * indent,
            subsequent_indent=' ' * (indent + 4),
        )
        print('\n'.join(lines))
        

@attrs(frozen=True)
class LineInfo:
    start = attrib()
    length = attrib()
    
    def __str__(self):
        return f'Zeilen {self.start}-{self.end} (={self.length})'
    
    @property
    def end(self):
        return self.start + self.length

    @classmethod
    def from_function(cls, function):
        lines, line_no = getsourcelines(function)
        return cls(line_no, len(lines))


@attrs(frozen=True)
class NameInfo:
    global_ = attrib()
    local = attrib()
    
    @property
    def count(self):
        return len(self.global_) + len(self.local)
    
    @classmethod
    def from_function(cls, function):
        return cls(
            Vars(getclosurevars(function).globals).just_variables().names,
            function.__code__.co_varnames
        )
        

@attrs(frozen=True)
class FunctionInfo:
    name = attrib()
    signature = attrib()
    line_info = attrib()
    names = attrib()

    def __str__(self):
        return f'{self.name}{self.signature}, {self.line_info}'
    
    @classmethod
    def from_function(cls, function):
        return cls(
            function.__qualname__,
            Signature.from_callable(function),
            LineInfo.from_function(function),
            NameInfo.from_function(function),
        )


def process_functions(functions):
    print('Funktionen und (ungebundene) Methoden:')
    for function_info in sorted(
        map(FunctionInfo.from_function, functions),
        key=lambda fi: case_insensitive_key_func(fi.name)
    ):
        print(' ', function_info)
        print_names('global', function_info.names.global_, 4)
        print_names('lokal', function_info.names.local, 4)
        if function_info.names.count:
            print(f'    = {function_info.names.count}')


def main():
    tkinter = import_module('tkinter')
    # 
    # Prevent `Tk.mainloop()` from running so next import has no side effect.
    # 
    tkinter.Tk.mainloop = lambda self: None
    simulator = import_module('forum13')
    
    total_vars = Vars.from_object(simulator)
    print(f'{len(total_vars)} normale, öffentliche Namen auf Modulebene.')

    original_vars = total_vars.without_modules() - Vars.from_object(tkinter)
    print(f'{len(original_vars)} ohne `{tkinter.__name__}`-Namen und Module.')
    
    categorized_vars = CategorizedVars.from_vars(original_vars)
    for category_name, variables in [
        ('Klasse', categorized_vars.classes),
        ('Funktion/Methode', categorized_vars.functions),
        ('Variable', categorized_vars.variables),
    ]:
        print_names(category_name, variables.names)

    process_functions(categorized_vars.functions.values)


if __name__ == '__main__':
    main()
Und die Ausgabe:

Code: Alles auswählen

195 normale, öffentliche Namen auf Modulebene.
57 ohne `tkinter`-Namen und Module.
Klasse (1): Data.
Funktion/Methode (19): Auswahl, Beobachter_bewegen, Beobachter_drehen,
    Bestätigung, Data.__init__, Data.Drehen, Data.Kalibrierung,
    Data.Laden, Data.Zyklus_berechnen, Datei_wählen, Eingabe, Info,
    löschen, neu_laden, Neue_Objekte, Neue_Simulation, Simulation,
    update_Info, Zeit_anpassen.
Variable (42): Anzahl_Objekte, BM, BP, button, BV, BV_Schritt,
    counter, F, filemenu, G, Kalibrierung, Label_BM, Label_BP,
    Label_BV, LabelZyklen, LBL, lfz, LINIE, lz, M, master, menu, NAME,
    O, oz, P, PROJEKTION, pstart, pz, R, ROTATION, S, SKALIERUNG,
    t_alt, UPS, V, w, Z, zeit, ZK, ZOOM, Zyklen.
Funktionen und (ungebundene) Methoden:
  Auswahl(Labels), Zeilen 440-451 (11)
    global (2): LEFT, W.
    lokal (5): antwort, Eingabe, label, Labels, v.
    = 7
  Beobachter_bewegen(Achse, Richtung), Zeilen 402-412 (10)
    global (3): BV, BV_Schritt, Label_BV.
    lokal (5): Achse, Richtung, Schritt, txt, v.
    = 8
  Beobachter_drehen(Achse, Richtung), Zeilen 382-401 (19)
    global (2): BM, Label_BM.
    lokal (13): a, Achse, b, c, n1, n2, n3, Richtung, txt, ups, x, y,
        z.
    = 15
  Bestätigung(txt), Zeilen 427-439 (12)
    global (2): LEFT, W.
    lokal (4): antwort, Eingabe, txt, v.
    = 6
  Data.__init__(self, objnr, txt='', x=0, y=0, z=0), Zeilen 41-47 (6)
    lokal (6): objnr, self, txt, x, y, z.
    = 6
  Data.Drehen(self, zeit, ups, n1, n2, n3), Zeilen 47-67 (20)
    lokal (22): a1, a2, a3, b1, b2, b3, c1, c2, c3, cos, n1, n2, n3,
        self, sin, u, ups, w, xx, yy, zeit, zz.
    = 22
  Data.Kalibrierung(), Zeilen 300-329 (29)
    global (7): BM, BP, Kalibrierung, O, P, PROJEKTION, pz.
    lokal (10): betrag, line, max, objnr, p, x, xx, y, yy, z.
    = 17
  Data.Laden(fn), Zeilen 67-133 (66)
    global (27): Anzahl_Objekte, BM, BP, BV, BV_Schritt, F, LBL,
        LINIE, lz, M, master, NAME, O, P, PROJEKTION, pstart, pz, R,
        ROTATION, S, SKALIERUNG, t_alt, UPS, V, Z, ZK, ZOOM.
    lokal (12): betrag, counter, f, fn, l, LblPos, t0, t2, t3, t4,
        zeile, Zyklen.
    = 39
  Data.Zyklus_berechnen(), Zeilen 133-299 (166)
    global (26): Anzahl_Objekte, BM, BP, BV, counter, F, G,
        Kalibrierung, LabelZyklen, LBL, lfz, LINIE, lz, M, NAME, O, P,
        PROJEKTION, pz, R, ROTATION, UPS, V, w, Z, ZOOM.
    lokal (21): a1, a2, betrag, Kraft, l, laufzeit, line, objnr, p,
        p1, p2, PP, txt, v, v1, v2, x, y, z, z1, z2.
    = 47
  Datei_wählen(match), Zeilen 452-475 (23)
    global (2): LEFT, W.
    lokal (7): datei, fn, match, txt, v, V, x.
    = 9
  Eingabe(Labels), Zeilen 476-503 (27)
    global (5): LEFT, RIGHT, TOP, X, YES.
    lokal (12): b1, Eingabe, ent, entries, entry, field, L, lab,
        Labels, resultat, row, text.
    = 17
  Info(txt), Zeilen 420-426 (6)
    global (1): END.
    lokal (3): info, T, txt.
    = 4
  löschen(), Zeilen 417-419 (2)
    global (1): w.
    = 1
  neu_laden(), Zeilen 413-416 (3)
  Neue_Objekte(), Zeilen 504-633 (129)
    lokal (20): b, f, form, h, i, l, L, line, obj, p, s, t, teilung,
        txt, wxy, wxz, x, y, z, zeile.
    = 20
  Neue_Simulation(modus), Zeilen 634-778 (144)
    global (16): Anzahl_Objekte, BM, BP, BV, BV_Schritt, F, M, NAME,
        O, ROTATION, SKALIERUNG, UPS, V, Z, ZK, ZOOM.
    lokal (20): antwort, betrag, fo, fs, l, L, line, modus, n1, n2,
        n3, obj, objnr, sim, sk, txt, x, y, z, zeile.
    = 36
  Simulation(), Zeilen 779-806 (27)
    global (6): counter, Kalibrierung, lfz, w, ZK, Zyklen.
    lokal (5): Einheit, fn, line, logfile, Zeit.
    = 11
  update_Info(), Zeilen 345-381 (36)
    global (6): BM, BP, BV, Label_BM, Label_BP, Label_BV.
    lokal (7): betrag, Einheit, Faktor, txt, x, y, z.
    = 13
  Zeit_anpassen(Zeit), Zeilen 330-344 (14)
    lokal (3): Einheit, Faktor, Zeit.
    = 3
Problematisch sind die 42 Variablen und jede Funktion die bei 'global' etwas stehen hat was keine Konstante ist. Hätte ich gerne noch nach gefiltert, aber unter den Variablen sind so viele die wie Konstanten benannt sind, so dass man damit das Ergebnis verfälscht.

Dann sollte die Gesamtzahl der Variablen in einer Funktion auch nicht zu gross sein. So 10—15 in der Regel. Mehr können normale Menschen eh nicht gleichzeitig im Kopf behalten. Und zu lang sollten Funktionen auch nicht werden. Üblicherweise so maximal 25—50 Zeilen.

Es gibt Ausnahmen, oft im GUI-Bereich wo das ab und zu eher stupider Boilerplate-Code ist. Da ist ja auch keine Programmlogik drin, und egal wie man das schreibt, es bleibt bei jeder nicht ganz trivialen GUI schwierig sich das Endergebnis vorzustellen. Bei `tkinter` würde ich da auf eigene Klassen setzen in denen GUI-Teile zusammengefasst werden. Bei anderen GUI-Rahmenwerken verschwindet der Kram ja $GOTT sei Dank in Datendateien die man auch nicht mit einem Texteditor bearbeitet, sondern in einem grafischen Werkzeug.

@Rolando1964: Ich weiss nicht wie so etwas in Perl ausgesehen hätte, aber in C hätte man das doch auch nicht so unstrukturiert geschrieben. Das erinnert mich an BASIC vor Dialekten die eigene Verbunddatentypen hatten. Also so ein bisschen wie ein von GW-BASIC nach QBasic portiertes Programm wo man alle Variablen und Arrays noch global belassen hat.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
msmn
User
Beiträge: 35
Registriert: Samstag 13. Juli 2019, 07:15

@_Blackjack_:
wollte mit Hilfe deines oben angeführtes Analyseprogrammes auch "mein" Programm analysieren.
erhalte jedoch beim Aufruf (Windows Python 3.7-3.2) von 'python dein_programm.py" ….den Fehler: ModuleNotFoundError: No module named 'forum13'

mache ich etwas falsch?....und wo wird bitte das zu analysierende Programm angegeben?

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

@msmn: Genau da wo ich das Modul `forum13` importiere – denn das wird analysiert.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
msmn
User
Beiträge: 35
Registriert: Samstag 13. Juli 2019, 07:15

@_Blackjack_:
okay....ich habe "meinen" Programmnamen jetzt eingefügt (Zeile 197).
wenn ich beim Win-Prompt dann aufrufe: "python deinprogramm.py"...
..läuft auch scheinbar "mein" Programm an....es kommt auch keine Fehlermeldung....allerdings kommt es auch zu keiner Analyseausgabe (und ich habe auch keinen Log-File gefunden)
sollte ich eventuell irgendwie anders vorgehen?
msmn
Benutzeravatar
__blackjack__
User
Beiträge: 13080
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@msmn: Wenn Dein Programm anläuft, dann ist es offensichtlich nicht so geschrieben das man es importieren kann *ohne* dass das Programm anläuft (``if __name__ == '__main__':``). Das war das Programm von Rolando1964 auch nicht – das konnte ich durch das ”weg patchen” von der Tk-Hauptschleife umgehen. Scheint bei Deinem Programm nicht zu funktionieren.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten