Hier werden die Unterschiede zwischen zwei Versionen gezeigt.
Nächste Überarbeitung | Vorhergehende Überarbeitung | ||
ss16:physiksimulation_doc_python [2016/08/13 15:46] markumnus angelegt |
ss16:physiksimulation_doc_python [2016/08/14 22:37] (aktuell) markumnus |
||
---|---|---|---|
Zeile 3: | Zeile 3: | ||
</a></center></html> | </a></center></html> | ||
- | <- [[ss16:Physiksimulation_doc_components|zurück zur Liste der Komponenten]] | + | **[[ss16:Physiksimulation|Physiksimulation]]/[[ss16:Physiksimulation_Doc_Main|Dokumentation]]/[[ss16:Physiksimulation_doc_components|Komponenten]]/** |
+ | =====Python-Teil===== | ||
- | =====Physiksimulation | Python-Teil===== | + | |
+ | ====Programmstruktur==== | ||
+ | {{ :ss16:physiksimulation_struktur2107.png?nolink |}} | ||
+ | |||
+ | |||
+ | ====Parser.py==== | ||
+ | In der Ergebnis-Textdatei sind einzelne Zeitschritte mit **//|//** getrennt, Objekte mit **//;//** und die Koordinaten der Objekte mit **//,//**. Die //parse()//-Funktion macht aus einem String nach diesem Muster eine Liste aus Tupeln mit den Positionsdaten. | ||
+ | <code Python> | ||
+ | def parse (string): | ||
+ | """ | ||
+ | Nimmt einen String mit Object-Positionsdaten entgegen und | ||
+ | gibt eine Liste aus Tupeln mit Positionsdaten zurueck. | ||
+ | Beispiel: | ||
+ | "0.0037, 0.0024, -0.0001;-0.315, -0.9603, -0.0001;-0.3129, -0.9592, -0.0002;|" -> [(0.0037, 0.0024, -0.0001), (-0.315, -0.9603, -0.0001), (-0.3129, -0.9592, -0.0002)] | ||
+ | """ | ||
+ | result = [] | ||
+ | |||
+ | # aufteilen | ||
+ | objects = string.split(";") | ||
+ | |||
+ | # bereinigen | ||
+ | for o in objects: | ||
+ | if o == "": | ||
+ | objects.remove(o) | ||
+ | |||
+ | # Koordinaten extrahieren | ||
+ | for o in objects: | ||
+ | coords = o.split(",") | ||
+ | |||
+ | # Whitespace entfernen | ||
+ | for i, c in enumerate (coords): | ||
+ | coords[i] = c.strip() | ||
+ | |||
+ | # in Floats umwandeln | ||
+ | try: | ||
+ | coords = map (float, coords) | ||
+ | except ValueError: | ||
+ | print "Konnte", coords, "nicht umwandeln." | ||
+ | |||
+ | result.append (tuple(coords)) | ||
+ | |||
+ | return result | ||
+ | </code> | ||
+ | |||
+ | |||
+ | ====Reader.py==== | ||
+ | In der Datei Reader.py wird die Klasse Reader definiert. Wir haben uns hier für eine Klasse entschieden (anstatt nur einer Funktion wie beim Parser), da wir die //%%__getitem__()%%//-Funktion nutzen wollten, um die Ergebnis-Datei wie eine Liste indizierbar zu machen. Mit reader[time_step] kann man bequem auf die Positionsdaten des jeweiligen Zeitschritts zugreifen. | ||
+ | <code Python> | ||
+ | ####################################### | ||
+ | # IMPORTE | ||
+ | ####################################### | ||
+ | import Parser | ||
+ | |||
+ | class Reader: | ||
+ | |||
+ | time_steps = [] | ||
+ | |||
+ | def __init__ (self, filename): | ||
+ | # Deklarationen | ||
+ | self.FILENAME = filename | ||
+ | |||
+ | # File lesen | ||
+ | with open (self.FILENAME) as f: | ||
+ | file_cont = f.read() | ||
+ | |||
+ | # Zeitschritte voneinander trennen | | ||
+ | self.time_steps = file_cont.split('|') | ||
+ | |||
+ | # letztes, leeres Element entfernen | ||
+ | self.time_steps.pop() | ||
+ | |||
+ | |||
+ | def __getitem__ (self, key): | ||
+ | """ | ||
+ | Indiziert einen Zeitschritt und gibt die Koordinaten der Objects zu diesem Zeitschritt zurueck. | ||
+ | """ | ||
+ | return Parser.parse(self.time_steps[key]) | ||
+ | |||
+ | |||
+ | def __len__ (self): | ||
+ | """ | ||
+ | Gibt Anzahl der verfuegbaren Zeitschritte zurueck. | ||
+ | """ | ||
+ | return len (self.time_steps) | ||
+ | |||
+ | def __str__ (self): | ||
+ | s = "Reader auf \"" + self.FILENAME + "\" mit " + str(len(self)) + " Zeitschritten." | ||
+ | return s | ||
+ | |||
+ | def __repr__(self): | ||
+ | return self.__str__() | ||
+ | |||
+ | </code> | ||
+ | |||
+ | |||
+ | ====Visualizer.py==== | ||
+ | Diese Datei deklariert die Klasse //Visualizer//. Im Konstruktor nimmt sie eine Liste mit Objekten entgegen, die visualisiert werden sollen. Außerdem wird die //scene// für Visual initialisiert.\\ | ||
+ | Mit der Methode //run()// wird die Animation gestartet. Die benötigten Positionsdaten werden mithilfe des Readers ausgelesen. | ||
+ | <code Python> | ||
+ | from visual import * | ||
+ | from Reader import * | ||
+ | from PyCInterface import PyCObject | ||
+ | |||
+ | class Visualizer: | ||
+ | def __init__ (self, list_of_objects, source = "results.txt"): | ||
+ | """ | ||
+ | Initialisiert den Visualizer mit verschiedenen Konstanten und Grundfunktionen, | ||
+ | sowie mit einer Liste an Objekten, die gezeichnet werden sollen | ||
+ | """ | ||
+ | self.PROGRAM_NAME = "Gravitationssimulation" | ||
+ | self.SPEED = 100 | ||
+ | self.reader = Reader (source) | ||
+ | self.time = 0 | ||
+ | self.PyCObjects = [] # Objekte mit vom User gegebenen Daten wie Name, Farbe, Groesse, etc. | ||
+ | self.Objects = [] # Objekte von Visual, die die Anzeige beeinflussen | ||
+ | self.renew_objects (list_of_objects) | ||
+ | |||
+ | # Initialisierung von Visual | ||
+ | scene = display (title = self.PROGRAM_NAME,\ | ||
+ | x=0, y=0, width=1920,height=1080,\ | ||
+ | range=10, background=color.black, center = (0,0,0)) | ||
+ | |||
+ | def renew_objects (self, list_of_objects): | ||
+ | """ | ||
+ | Setzt die Objects komplett neu. | ||
+ | """ | ||
+ | self.PyCObjects = list_of_objects # Objekte mit vom User gegebenen Daten wie Name, Farbe, Groesse, etc. | ||
+ | self.Objects = [] # Objekte von Visual, die die Anzeige beeinflussen | ||
+ | |||
+ | # lese Daten aus | ||
+ | for i, o in enumerate(self.PyCObjects): | ||
+ | try: | ||
+ | pos_o = self.reader[0][i] | ||
+ | size_o = o.size | ||
+ | color_o = o.color | ||
+ | self.Objects.append(sphere(pos=pos_o, radius = size_o, color = color_o, make_trail = true)) | ||
+ | except IndexError: | ||
+ | print "Mehr Objekte uebergeben als berechnet wurden:", i | ||
+ | |||
+ | def run (self): | ||
+ | """ | ||
+ | Laeuft einmal durch alle Daten und zeigt diese an. | ||
+ | """ | ||
+ | # Loop bis Fileende: | ||
+ | # update die Positionen | ||
+ | while self.time < len(self.reader): | ||
+ | rate (self.SPEED) | ||
+ | for i, o in enumerate (self.Objects): | ||
+ | o.pos = self.reader[self.time][i] # Object des aktuellen Zeitschrittes | ||
+ | self.time += 1 | ||
+ | </code> | ||
+ | |||
+ | |||
+ | ====Main.py==== | ||
+ | In dieser Datei werden mehrere Klassen definiert, darunter die MyApp-Klasse, die das Programm darstellt und einige PopupWindow-Klassen, die die Funktionen des Fensters steuern.\\ | ||
+ | Bei einem Klick auf einen Button wird mit der Codezeile | ||
+ | <code Python> | ||
+ | self.addSonne.clicked.connect(self.Add_Sonne) # 'addSonne' ist der Name des in der Window.ui-Datei deklarierten Buttons | ||
+ | </code> | ||
+ | die Methode Add_Sonne aufgerufen. | ||
+ | Diese erstellt ein PopupWindow... | ||
+ | <code Python> | ||
+ | def Add_Sonne(self): | ||
+ | print "Creating a new Sun..." | ||
+ | self.w = PopupWindow0() | ||
+ | self.w.setWindowTitle("Sonne erstellen...") | ||
+ | self.w.setGeometry(QtCore.QRect(500, 200, 360, 300)) | ||
+ | self.w.show() | ||
+ | </code> | ||
+ | ... welches wiederum Buttons hat, die z.B. beim Klicken auf "OK" ein Objekt erstellen und es zunächst per [[ss16:Physiksimulation_Doc_cpp#Interface.cpp|Interface]] im [[ss16:Physiksimulation_Doc_cpp#Universe.cpp|Universe]] speichern: | ||
+ | <code Python> | ||
+ | def output0(self): | ||
+ | obj = PyCObject() | ||
+ | obj.x = float(PositionEditx0.text()) # uebernehme | ||
+ | obj.y = float(PositionEdity0.text()) # Positionsdaten | ||
+ | obj.z = float(PositionEditz0.text()) # vom Benutzer | ||
+ | interface.add (obj) | ||
+ | </code> | ||
+ | Auf die gleiche Weise werden die Berechnungen und die Visualisierung gestartet. | ||
+ | |||
+ | Der Einstiegspunkt ist dann einfach folgender: | ||
+ | <code Python> | ||
+ | if __name__ == "__main__": | ||
+ | app = QtGui.QApplication(sys.argv) | ||
+ | window = MyApp() | ||
+ | window.move(QtCore.QPoint(850, 300)) | ||
+ | window.show() | ||
+ | sys.exit(app.exec_()) | ||
+ | </code> |