Simulation eines Objektes in der Nähe eines black hole

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
Steven
User
Beiträge: 3
Registriert: Freitag 16. Mai 2014, 18:51

Hallo,
ich bin noch ganz neu hier im Forum und schreibe einfach mal darauf los.

Ich habe hier ein Programm und ein Problem und soll meiner kleinen Schwester helfen ... bin selber etwas fit in PHP aber das Konzept usw. von Python ist mir völlig unbekannt.
Ich hoffe ihr könnt mir bzw- ihr weiter helfen.
Ich weiß das Namenschreibweisen und Leerzeichensetzung ungewöhnlich sind und keine sauber abgetrennten Codeteile (Programmlogik und GUI ) exisitieren die man für sich betrachten kann.

Zum Programm:
Zum Ausführen müssen zu erst die beiden leeren Checkbutton (min. 1x) betätigt werden und dem Programm muss die Position und Masse des Schwarzen Loches übergeben werden. (Die Position in Pixeln). Dann auf start drücken und der Körper beginnt sich auf das Schwarze Loch zu zu bewegen.
Probleme:
Drückt man nun erneut auf start, bleibt das erste Objekt einfach stehen (da es genauso heißt), ich bräuchte also variable Variblenamen, weiß jedoch nicht wie man das Programmiert
Wenn man während der Körper das Schwarze Loch noch nicht erreicht hat reset drückt verschwinden zwar die Objekte aber die While-schleife bricht nicht ab und so wird schließlich doch noch gesagt das der nicht vorhandene Körper den Schwarzschildradius erreicht.

Ich habe keine Ahung wie ich das machen kann, bin blutiger Anfänger.
Ist das ein kleines Problem oder kann man nicht ohne riesen Aufwand weiter helfen?

LG Aaron

Code: Alles auswählen

    # -*- coding: cp1252 -*-
    from Tkinter import *
    import tkMessageBox
    import math
     
    def info():                         #ruft eine Hilfefenster auf, das das Programm erklärt
        Information=Tk()
        Information.title("Information")
        Information.mainloop()
     
    def Lichtschalter():   #erhellt den Hintergrund
        if lamp.get()=="zurück zum dunklen Hintergrund":
            Ausgabe.config(bg="#B2DFEE")
        else:
            Ausgabe.config(bg="#004")
       
    def Verschiebung(ID,x0,y0,x1,y1):            #verschiebt objegt auf neue Koordinaten
        Ausgabe.coords(ID,(x0,y0,x1,y1))
       
    G=6.67384*10**(-11)   #Gravitationskonstante
    c=299792458.0         #Lichtgeschwindigkeit
     
    class Grundberechnung:    #berechnent alles was nur einmal berechnet werden muss, da es sich nicht verändert
        def __init__(self,Masse,x,y):
            self.Masse=Masse
            self.x=x
            self.y=y
     
        def Schwarzschildradius(self): #berechnet den Schwarzschildradius
            RSchw=2.0*G*float(self.Masse)/c**2.0
            print RSchw
            return RSchw
       
        def XAbstand(self):
            #gibt den Unterschied zwischen den X-Koordinaten von Schwarzem Loch und Startpunkt des Objekts zurueck
            Xr=abs(self.x-2.0)
            return Xr
     
        def YAbstand(self):
            #gibt den Unterschied zwischen den Y-Koordinaten von Schwarzem Loch und Startpunkt des Objekts zurueck
            Yr=abs(self.y-352.0)
            return Yr
     
        def Abstand(self):
            #berechnet den Abstand zwischen Schwarzem Loch und Startpunkt des Objektes
            A=((self.XAbstand())**2.0+(self.YAbstand())**2.0)**0.5
            return A
           
        def Winkel(self):
            #berechnet den Winkel zwischen den den gedachten Linien Y-Abstand und Abstand
            if float(self.x)!=2.0:
                alf=math.atan2(self.XAbstand(),self.YAbstand())
            else:
                alf=1/2*math.pi
            return alf
     
    class Koerper: #berechnent alles das sich in jedem Punkt verändert
        def __init__(self,Masse,Momentabstand,Alpha,x,y,xr,yr,RS):
            self.Masse=Masse
            self.Momentabstand=Momentabstand
            self.Alpha=Alpha
            self.x=x
            self.y=y
            self.xr=xr
            self.yr=yr
            self.RS=RS
     
        def Beschleunigung(self):
            #gibt die Beschleunigung fuer das Objekt in jedem Punkt zurueck
            a=G*float(self.Masse)/(float(self.Momentabstand))**2.0
            return a
           
        def Abstand2(self):
            #gibt den neuen Abstand zurueck der spaeter als neuer Momentabstand verwendet wird
            abstand=float(self.Momentabstand)-0.1
            return abstand
           
        def newX(self):
            #berechnet die neue X-Koordinate des Objekts
            if self.x<=2:
                x2=2.0-self.xr+float(self.Abstand2())*math.sin(float(self.Alpha))
            else:
                x2=self.xr-float((self.Abstand2())*math.sin(float(self.Alpha)))+2.0
            return x2
                   
        def newY(self):
            #berechnet die neue X-Koordinate des Objekts
            if self.y<=352:
                y2=352.0-self.yr+float((self.Abstand2())*math.cos(float(self.Alpha)))
            else:
                y2=self.yr-float(self.Abstand2())*math.cos(float(self.Alpha))+352.0
            return y2
           
        def zeit(self):
            #berechnet die zeit die ein beobachter auf dem koerper fuer eine bestimmte strecke wahrnimmt
            t=(0.0002/float(self.Beschleunigung()))**(0.5)
            return t
     
        def Geschwindigkeit(self):
            #gibt die Geschwindigkeit des Objektes zurueck
            v=float(self.Beschleunigung()*self.zeit())
            return v
     
        def Zeitdilitation(self):
            #durch Gravitation des Schwarzen Loches ausgeloeste Zeitdilitation
            T1=float(self.zeit()/((self.Abstand2()-(self.RS))/self.Abstand2())**(0.5))
            return T1
     
        def Zeitdilitation2(self):
            #durch Geschwindigkeit ausgeloeste Zeitdilitation
            T2=float((1+self.Geschwindigkeit()/c)/(1-self.Geschwindigkeit()/c))**(0.5)*self.zeit()
            return T2
     
    def Timedilitation(ma,ra,al,xa,ya,xb,yb,sch):
        #gibt die Wartezeit des Programmes je nach Eingabe des Benutzers zurueck
        V3=Koerper(ma,ra,al,xa,ya,xb,yb,sch)
        if state.get()=="Beobachter in Unendlicher Entfernung":
            wartezeit=round((V3.Zeitdilitation2()*V3.Zeitdilitation()),0)
        else:
            wartezeit=round(V3.zeit(),0)
        return wartezeit
     
    def start():
        if XAchse.get():
            xachse=float(XAchse.get())
        else:
            xachse=0
        if YAchse.get():
            yachse=float(YAchse.get())
        else:
            yachse=0
        #uebernimmt X- und Y-Koordinaten des Schwarzen Loches aus den Eingabefeldern
        O3=Ausgabe.create_oval(xachse-2,yachse-2,xachse+2,yachse+2,fill="#000")
        #O3 ist das Schwarze Loch
        O1=Ausgabe.create_oval(0,350,4,354,fill="#F02")
        #O1 ist ein roter Punkt, der anzeigt wo der Koerper startete
        O2=Ausgabe.create_oval(1,351,3,353,fill="#FE2")
        #O2 ist der Koerper
        if EingabeMasse.get():
            masse=float(EingabeMasse.get())*10**8
        else:
            tkMessageBox.showerror("Ein Fehler ist Aufgetretten",
                                   "Bitte reseten Sie und geben eine Masse ein bevor sie erneut auf start klicken")
        #uebernimmt die Masse des Schwarzen Loches aus dem Eingabefeld und vergroessert sie verhaeltnismaessig
        V1=Grundberechnung(masse,xachse,yachse)
        r=V1.Abstand()
        rSchwarz=V1.Schwarzschildradius()
        if r<=rSchwarz:
            tkMessageBox.showerror("Ein Fehler ist Aufgetretten",
                                   "Der Körper befindet sich bereits innerhalb des Schwarzschildradius. Bitte starten Sie das Programm neu!")
        else:
            while r>rSchwarz:
                # ruft alles in der richtigen Reihenfolge auf
                V2=Koerper(masse,r,V1.Winkel(),xachse,yachse,V1.XAbstand(),V1.YAbstand(),rSchwarz)
                j=round(float(V2.newX()),2)
                i=round(float(V2.newY()),2)
                Ausgabe.after(int(Timedilitation(masse,r,V1.Winkel(),xachse,yachse,V1.XAbstand(),V1.YAbstand(),rSchwarz)),
                              Verschiebung(O2,j-2,i-2,j+2,i+2))
                r=V2.Abstand2()
            if r<=rSchwarz:
                tkMessageBox.showinfo("Ende",
                                      "Der Körper hat den Schwarzschildradius erreicht.")
     
    def clear(): #loescht die Objekte
        Liste=Ausgabe.find_all()
        for i in range(0,len(Liste)):
            u=Liste[i]
            Ausgabe.delete(u)
           
    Fenster=Tk()
    sLeft   =  "%s" % 0    # X-Position auf dem Bildschirm (linke obere Ecke in Pixels)
    sTop    =  "%s" % 0    # Y-Position auf dem Bildschirm (linke obere Ecke in Pixels)
    sWidth  =  "%s" % 1360  # Breite (Pixels)
    sHeight =  "%s" % 760   # Höhe   (Pixels)
    Fenster.wm_geometry(sWidth+"x"+sHeight+"+"+sLeft+"+"+sTop)
    Fenster.title("Simulation eines Objektes in der Nähe eines Schwarzen Loches")
    state=StringVar(Fenster)
    lamp=StringVar(Fenster)
    Eingabe=Frame(Fenster,relief=RAISED)
    Eingabe.pack(side=RIGHT,fill=None,expand=0)
    Label(Eingabe, text="Position:",
          font=("Times",12,"bold")).pack(anchor=NW)
    Eframe1=Frame(Eingabe)
    Eframe1.pack(anchor=W)
    Label(Eframe1, text="x-Achse",
          font=("Times",12)).pack(side=LEFT)
    XAchse=Entry(Eframe1)
    XAchse.pack(side=RIGHT)
    Eframe2=Frame(Eingabe)
    Eframe2.pack(anchor=W)
    Label(Eframe2, text="y-Achse",
          font=("Times",12)).pack(side=LEFT)
    YAchse=Entry(Eframe2)
    YAchse.pack(side=RIGHT)
    Eframe3=Frame(Eingabe)
    Eframe3.pack(anchor=W)
    Label(Eframe3, text="Masse:",
          font=("Times",12,"bold")).pack(side=LEFT)
    EingabeMasse=Entry(Eframe3)
    EingabeMasse.pack(side=RIGHT)
    Luecke1=Label(Eingabe)
    Luecke1.pack()
    Eframe4=Frame(Eingabe)
    Eframe4.pack()
    cb=Checkbutton(Eframe4,
                   onvalue="Beobachter in Unendlicher Entfernung",
                   offvalue="Vorstellung eines Beobachtes auf dem Körper",
                   variable=state, width=36, textvariable=state, indicatoron=False)
    cb.pack()
    Luecke2=Label(Eingabe)
    Luecke2.pack()
    Eframe5=Frame(Eingabe)
    Eframe5.pack()
    cb2=Checkbutton(Eframe5,
                    onvalue="zurück zum dunklen Hintergrund",
                    offvalue="Wo ist das Schwarze Loch?",
                    variable=lamp, width=28, command=Lichtschalter,
                    textvariable=lamp, indicatoron=False)
    cb2.pack()
    Luecke3=Label(Eingabe)
    Luecke3.pack()
    Eframe6=Frame(Eingabe)
    Eframe6.pack()
    Startbutton=Button(Eframe6, text="Start",
                       command=start)
    Startbutton.pack(side=LEFT)
    Clearbutton=Button(Eframe6, text="Reset",
                       command=clear)
    Clearbutton.pack(side=LEFT)
    Infobutton=Button(Eframe6, text="Info",
                      command=info)
    Infobutton.pack(side=RIGHT)
    #kreiert die Eingabefelder
    Ausgabe=Canvas(Fenster)
    Ausgabe.pack(side=LEFT,fill=BOTH,expand=1)
    #kreiert den Hintergrund der Ausgabe
    Fenster.mainloop()
     
Steven
User
Beiträge: 3
Registriert: Freitag 16. Mai 2014, 18:51

Guten Morgen, ich möchte mal vorsichtig nachfragen ob die Frage zu umständlich formuliert ist? Grüße
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo und willkommen im Forum!
Steven hat geschrieben:Drückt man nun erneut auf start, bleibt das erste Objekt einfach stehen (da es genauso heißt), ich bräuchte also variable Variblenamen, weiß jedoch nicht wie man das Programmiert
Du möchtest keine Variablen dynamisch erstellen ;-) Wenn du in so eine Situation gerätst, dann musst du auf eine saubere Datenstruktur umstellen. Verwende dazu Listen, Tupel oder Dictionaries, was auch immer gerade zu deinem Problem passt.

Ganz allgemein solltest du deinen Code noch deutlich verbessern, das sieht alles ein wenig nach Chaos aus. Wenn du eine Programmiersprache verwendest, dann solltest du dich auch an deren Konventionen halten, in diesem Fall besonders an die Namensgebung. Wenn du den Code für dich behältst, dann kannst du machen was du willst, wenn du aber in einem Forum nachfragen musst, dann machst du es den Lesern unnötig schwer.

Dann solltest du dir auch ganz drigend *-Importe abgewöhnen, gerade bei Tkinter. Damit müllst du dir den kompletten Namensraum zu, überschreibst möglicherweise unabsichtlich Namen und machst es extrem schwer die Herkunft von Namen nachzuvollziehen.

Die Existenz von globalen Variablen vergisst du am besten gleich, die schaffen mehr Probleme als sie lösen können. Globaler Zustand ist selten eine gute Idee, im Normalfall braucht man den nicht. Werte betreten Funktionen durch Parameter und verlassen sie mittels Rückgabewert. Dein Programm wird dadurch komplett undurchsichtig. Bei Konstanten ist das natürlich etwas anderes. Diese schreibt man zur besseren Übersicht übrigens unter die Importe und nicht irgendwo zwischen Definitionen.

"Grundberechnung" ist eine überflüssige Klasse, das sollten alles Funktionen sein.

Wenn du Code der Struktur

Code: Alles auswählen

x = berechne(...)
return x[code=python]
hast, dann kannst du gleich
[code=python]return berechne(...)
schreiben.

floats mittels "==" oder "!=" zu vergleichen ist, in jeder Sprache, keine gute Idee. Berechnungen unterliegen immer Rundungsfehlern, daher wird ein Test auf Gleichheit fast immer fehlschlagen, bzw. ein Test auf Ungleichheit fast immer zutreffen. Du musst prüfen, ob sich Werte innerhalb eines gewissen Fehlerintervalls befinden oder nicht.

Kommentare solltest du nicht hinter eine Zeile schreiben, sondern darüber. Dann sieht das ganze viel übersichtlicher aus. Wenn du die Aufgabe einer Klasse oder Funktion beschreibst, dann mache das mit Docstrings.

Du scheinst teilweise Code zu verwenden ohne zu wissen warum du es so schreibst. Das wird an den ganzen float-Aufrufen deutlich. Dein ganzer Code ist voll davon, sie sind aber fast alle überflüssig.

if-else-Konstrukte sollten nur Teile behandeln, die wirklich unterschiedlich sind. Zeilen 80 bis 83 und 88 bis 91 demonstrieren dass sehr schön. Der größte Teil des Codes ist identisch, es finden sich nur an zwei Stellen Unterschiede. Die gleichen Teile solltest du aus dem if-else herausziehen.

"Abstand2" ist ein schlecht gewählter Name, der sagt nicht aus. Gleiches gilt für "newX" oder "newY" und noch weitere Funktionsnamen. Bennene diese nach der Aufgabe, denn Funktionen beschreiben Tätigkeiten.

Es heißt Dilatation.

Es ist mir unklar, warum du "Zeitdilatation", "Zeitdilatation2" und "Timedilatation" hast. Klar, die berechnen alle etwas Unterschiedliches, aber das solltest du auch deutlich machen. Es sieht eher danach aus, als ob dir die Funktionsnamen ausgehen und du nur noch wild benennst.

ma, ra, al, xa, ya, xb, yb, sch sind komplett nichtssagend. Warum schreibst du die Namen nicht aus, so viel mehr Schreibarbeit ist es doch nicht. Dafür kannst du das Programm aber nach zwei Wochen auch noch verstehen. Und: Andere können das auch. Koordinaten solltest du dann auch als solche behandeln und nicht als zwei verschiedene Werte. Stichwort Tupel.

Zeile 142 ist "mutig". Das versetzt dein Programm in einen ungültigen Zustand (masse ist nirgends definiert), du machst aber munter weiter. Das führt dann spätestens in Zeile 145 zu einem NameError.

Der ganze Code unten gehört nicht auf modulebene, sondern in eine Funktion. Sonst kannst du dein Programm nicht sinnvoll importieren.
Das Leben ist wie ein Tennisball.
Benutzeravatar
/me
User
Beiträge: 3555
Registriert: Donnerstag 25. Juni 2009, 14:40
Wohnort: Bonn

Steven hat geschrieben:

Code: Alles auswählen

    def Lichtschalter():   #erhellt den Hintergrund
        if lamp.get()=="zurück zum dunklen Hintergrund":
            Ausgabe.config(bg="#B2DFEE")
        else:
            Ausgabe.config(bg="#004")
Alleine dieser Code hat mich schon dazu gebracht, mir das nicht näher anzuschauen. Eine Funktion, in der auf magische Art und Weise auf einmal irgendwelche Objekte aus wer weiß welchem Namensraum erscheinen, ist ein deutliches Anzeichen für extrem falsch strukturierten Code.

Ich habe da eine Funktion, die keine Tätigkeit, sondern ein Objekt beschreibt. Ich habe dort Bezeichner (lamp und Ausgabe) bei denen völlig unklar ist woher sie kommen. Der Rückgabewert von lamp.get() wird mit einem String verglichen der besser eine Konstante wäre und der Methodenname get() ist zudem nicht gerade deskriptiv. Die Parameterwerte bei Ausgabe.config würde ich auch als Konstante definieren und einfach config scheint mir ein unglücklicher Methodenname zu sein, wenn es darum geht einen Wert zu setzen.

Nimm dazu noch, dass der Code in der Namensgebung und Teile der Formatierung so gar nicht dem Style Guide for Python Code entspricht, dann wird es echt anstrengend. Jetzt noch mal 200 Zeilen davon ... puh, das ist mir echt zu aufwändig.

Strukturiere das besser und greife vor allem nicht mehr in Funktionen auf irgendwelche Objekte aus dem globalen Namensraum zu. Parameter und Rückgabewerte können die Übersicht da deutlich vereinfachen.
BlackJack

@Steven: Das die leeren Checkboxen etwas sind wo man drauf klicken kann, muss man aber auch erst mal heraus finden. Checkboxen verwendet man üblicherweise so, dass *neben* der Checkbox ein Text steht der beschreibt wofür das Häkchen oder Kreuzchen gut ist, was man da setzen kann.

Das Umschalten zwischen der Beobachterposition hätte ich als Dropdown-Liste erwartet.

Beim anklicken der Checkbox für das ”Licht” passiert nur einmal etwas. Danach kommt auf der Konsole eine Fehlermeldung das versucht wird eine Zeichenkette mit einem Unicode-Objekt zu vergleichen:

Code: Alles auswählen

forum5.py:12: UnicodeWarning: Unicode equal comparison failed to convert both arguments to Unicode - interpreting them as being unequal
  if lamp.get()=="zurück zum dunklen Hintergrund":
Man müsste die literale Zeichenkette hier als Unicode-Objekt angeben. Allerdings ist das etwas eigenartig, normalerweise fragt man von einer Checkbox ab ob sie ausgewählt ist oder nicht, oder man hinterlegt Werte die man auch direkt benutzen kann, also in diesem Fall zum Beispiel die Farbwerte. Das man Anhand vom Inhalt von Beschriftungen in einer GUI entscheidungen in der Programmlogik trifft, ist eher keine gute Idee. Man kann dann nicht mal eben den Text ändern ohne irgendwo anders im Programm ebenfalls nachbessern zu müssen, und man kann erst recht keine mehrsprachige Anwendung daraus machen, oder man müsste auch in der Programmlogik die Übersetzung einbauen, die eigentlich nur mit der Benutzerschnittstelle zu tun hat.

Die Klassen sind alle etwas eigenartig indem sie zwar beim Initialisieren einiges an das Objekt binden, dann aber nur Methoden haben die aufgrund dieser Daten Berechnungen durchführen und Ergebnisse zurück geben. Keine einzige der Methoden ändert einen internen Objektzustand. Bei `Koerper` würde man aber genau das erwarten, dass dieser Typ eine Methode hat, die den Zustand um einen Simulationsschritt ”weiter” setzt. Und nicht das man für jeden Schritt einen neuen Körper erstellt, die Werte für den nächsten Zustand ausrechnet, und ausserhalb des Körper-Objekts in verschiedenen lokalen Variablen speichert und das Körper-Objekt selbst dann im nächsten Schritt durch ein neues ersetzt. Das ist nicht die Idee von objektorientierter Programmierung.

In Python 2 sollte man von `object` erben wenn man sonst keine Basisklasse hat. Sonst bekommt man keine „new style”-Klasse bei der alles wie in der Dokumentation beschrieben funktioniert.
Steven
User
Beiträge: 3
Registriert: Freitag 16. Mai 2014, 18:51

Hallo, vielen Dank für eure vielen Antworten, ich muss das jetzt mal sacken lassen. Ich melde mich deswegen wieder.
Antworten