Dropdown menü deaktivieren

Fragen zu Tkinter.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Marvin75854: ich bin nicht auf die Idee gekommen, dass Du noch Python2 benutzt. Da funktionieren dann einige Dinge doch anders.
Mein Code also nochmal in der Python2-Version:

Code: Alles auswählen

import Tkinter as tk

class Berechnung():
    def __init__(self, hx):
        self.hx = hx
        
    def run1(self):
        print("Run1", self.hx)
        
    def run2(self):
        print("Run2", self.hx)

class GUI(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.title("GUI")
        self.label = tk.Label(self, text="Hallo")
        self.label.grid(row=1,column=1)
        self.hx = tk.Entry(self)
        self.hx.grid(row=1, column=2)
        self.run_button = tk.Button(self, text="Run", command=self.run)
        self.run_button.grid(row=2, column=1)
        
    def run(self):
        berechnung = Berechnung(self.hx.get())
        berechnung.run1()
        berechnung.run2()

def main():
    gui = GUI() 
    gui.mainloop()

if __name__ == "__main__": 
    main()

Modul_2 ist ein schlechter Name für ein Modul. Wenn Dir kein besserer einfällt, also einen der beschreibt, was der Nutzen dieses Moduls ist, dann ist das wahscheinlich keine gute Aufteilung in Module. Warum self.HX und self.HY die Werte None haben, habe ich schon weiter oben erklärt. Attribute sollten generell klein geschrieben werden, komplett GROSSBUCHSTABEN ist für Konstanten reserviert. Berechnung_button ist auch None, zudem eine lokale Variable, die sonst nicht benutzt wird, kann also weg. Lambda ist bei command= auch unnötig, weil man direkt die Funktion als Wert übergeben kann.

Vieles was importiert wird, wird nicht benutzt. Sternchenimporte vermeiden, da man nicht kontrollieren kann, was da alles in den Modulnamensraum importiert wird. Wie man's richtig macht, zeigt mein Beispiel.

get_Zahlen hat zwei Returns, von denen aber nur das erste ausgeführt wird. Auf die Entry-Objekte an sich, will man aber sowieso nicht direkt zugreifen, wenn dann nur auf die Werte, die drinstehen.
Frame_2 und Frame_3 sind bis auf den Schreibfehler in Frame_2.__init__ identisch, da braucht es also nur eine Klasse, die man zweimal verwenden kann.

Wie Du die Klasse Berechnung verwendest, habe ich Dir in meinem Beispiel doch auch schon gezeigt. Beim Drücken eines Knopfes werden alle nötigen Informationen aus den Entry-Feldern gelesen, ein Berechnenobjekt erstellt und die Berechnungen durchgeführt. Was da noch fehlt, ist die Ergebnisse wieder darzustellen.
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Marvin75854: Mit `Enum` definiert man ja eine Klasse beziehungsweise Singleton-Objekte die man nicht mehr verändert, also Konstanten. Also ja, die Klasse definiert man normalerweise auf Modulebene als Namensraum für die Konstanten.

Mein Beispiel war ja komplett ungetestet und auch unvollständig, denn die `PointEntry`-Klasse und die Funktion die am Ende aufgerufen werden sollte, fehlten ja.

Zu Deinem Code: Da wird `None` ausgegeben weil das der Rückgabewert von der `grid()`-Methode ist. Und den bindest Du ja an `self.HX` und `self.HY`. Und auch an `Berechnung_button`.

Bei ``command=lambda:self.Zahlenwerte()`` ist das ``lambda`` überflüssig, denn `self.Zahlenwerte` ist ja bereits ein passendes aufrufbares Objekt: ``command=self.Zahlenwerte``.

In `Zahlenwerte()` müsstest Du die Berechnung machen. Da kommst Du an die eingegebenen Daten heran, und da kannst Du ein Exemplar von `Berechnung` erstellen und dann dessen `calc()`-Methode aufrufen.

In `get_Zahlen()` wird das zweite ``return`` nie erreicht, denn das erste beendet ja schon die Ausführung der Methode. Und die Methode suggeriert auch das da Zahlen zurückgegeben werden und keine `Entry`-Objekte (wenn das nicht `None` wäre, siehe oben).

Die `Berechnung`-Klasse sieht jetzt wieder nach einer sehr umständlich geschriebenen Funktion aus, und es werden auch wieder Attribute ausserhalb der `__init__()` eingeführt.

Code: Alles auswählen

class Berechnung():
    def __init__(self, HX, HXX, HY, HYY):
        self.HX = HX
        self.HY = HY
        self.HXX = HXX
        self.HYY = HYY
    
    def calc():
        self.Ergebnis_X = self.HX + self.HXX
        self.Ergebnis_Y = self.HY + self.HYY
        print(self.Ergebnis_X)
        print(self.Ergebnis_Y)

# Warum nicht:

def berechne(hx, hy, hxx, hyy):
    return (hx + hxx, hy + hyy)

# Oder:

def berechne(point_a, point_b):
    return point_a + point_b

# Mit:

class Point(object):
    
    def __init__(self, x=0, y=0):
        self.x = x
        self.y = y
    
    def __add__(self, other):
        return Point(self.x + other.x, self.y + other.y)
Die *.pyc-Dateien enthalten das Modul zu Bytecode compiliert. Damit Python die nicht jedes mal neu compilieren muss. Das passiert dann nur noch wenn die dazugehörige *.py-Datei neuer ist, man also (wahrscheinlich) etwas am Quelltext geändert hat. Man kann die Dateien verhindern, aber dazu gibt es eigentlich keinen Grund.

Das `super()` ohne Argumente funktioniert nur in Python 3.x und mit Argumenten würde ich das auch in Python 2.7 nicht für `Tkinter`-Unterklassen verwenden, denn die sind in Python 2 noch „old style“-Klassen. Ich würde auch in Python 3 kein `super()` verwenden.

Bei `Tk` brauchst Du kein `parent` das ist ja schon an oberster Stelle. Bei den inneren Widgets brauchst Du das um anzugeben in welchem Eltern-Widget das Widget angeordnet werden soll. Die Aufrufseite davon kennst Du ja: Du übergibst ja allen `Label`, `Entry`, usw. als erstes Argument den Frame oder das Tk-Objekt in dem es dann dargestellt wird.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Marvin75854

Sirius3 hat geschrieben: Donnerstag 26. Juli 2018, 10:25 @Marvin75854: ich bin nicht auf die Idee gekommen, dass Du noch Python2 benutzt. Da funktionieren dann einige Dinge doch anders.
Mein Code also nochmal in der Python2-Version:
Wie groß ist da der Unterschied eigentlich? Also, wenn mein Programm jetzt von Python 2.6 auf Python 3.x umgestellt werden müsste.

Sirius3 hat geschrieben: Donnerstag 26. Juli 2018, 10:25 Modul_2 ist ein schlechter Name für ein Modul. Wenn Dir kein besserer einfällt, also einen der beschreibt, was der Nutzen dieses Moduls ist, dann ist das wahscheinlich keine gute Aufteilung in Module. Warum self.HX und self.HY die Werte None haben, habe ich schon weiter oben erklärt. Attribute sollten generell klein geschrieben werden, komplett GROSSBUCHSTABEN ist für Konstanten reserviert. Berechnung_button ist auch None, zudem eine lokale Variable, die sonst nicht benutzt wird, kann also weg. Lambda ist bei command= auch unnötig, weil man direkt die Funktion als Wert übergeben kann.

Ja die Namen werde ich für mein richtiges Programm anpassen. Hier habe ich nur ein wenig rumprobiert. Mein Button wurde immer von alleine ausgelöst und ich habe irgendwo bei google ein Beispiel mit Lambda gesehen. Ich weiß gar nicht genau was das Lambda bewirkt, jetzt funktioniert es aber auch ohne.

Sirius3 hat geschrieben: Donnerstag 26. Juli 2018, 10:25 Vieles was importiert wird, wird nicht benutzt. Sternchenimporte vermeiden, da man nicht kontrollieren kann, was da alles in den Modulnamensraum importiert wird. Wie man's richtig macht, zeigt mein Beispiel.
Ja da muss ich nochmal durchgucken was ich alles wirklich brauche. Habe immer einfach alles reinkopiert, wenn irgendeine neue Funktion dazugekommen ist.




__blackjack__ hat geschrieben: Donnerstag 26. Juli 2018, 11:30 @Marvin75854: Mit `Enum` definiert man ja eine Klasse beziehungsweise Singleton-Objekte die man nicht mehr verändert, also Konstanten. Also ja, die Klasse definiert man normalerweise auf Modulebene als Namensraum für die Konstanten.
Das kann sehr hilfreich sein denke ich.

__blackjack__ hat geschrieben: Donnerstag 26. Juli 2018, 11:30 Die *.pyc-Dateien enthalten das Modul zu Bytecode compiliert. Damit Python die nicht jedes mal neu compilieren muss. Das passiert dann nur noch wenn die dazugehörige *.py-Datei neuer ist, man also (wahrscheinlich) etwas am Quelltext geändert hat. Man kann die Dateien verhindern, aber dazu gibt es eigentlich keinen Grund.
Ja ich weiß nicht ob das schlimm ist, aber ich arbeite nicht am privaten Rechner und das Programm soll später von meinem Arbeitgeber und einem Kunden benutzt werden. Gefällt mir einfach nicht so, dass der einfach automatisch eine Datei erstellt, zumal das compilieren ja keine Sekunde dauert. Ich weiß allerdings nicht wie das mit anderen Programmen gehandhabt wird. Wahrscheinlich ist das völlig egal und es gibt, wie du schon sagst, keinen Grund das zu unterdrücken.

__blackjack__ hat geschrieben: Donnerstag 26. Juli 2018, 11:30 Bei `Tk` brauchst Du kein `parent` das ist ja schon an oberster Stelle. Bei den inneren Widgets brauchst Du das um anzugeben in welchem Eltern-Widget das Widget angeordnet werden soll. Die Aufrufseite davon kennst Du ja: Du übergibst ja allen `Label`, `Entry`, usw. als erstes Argument den Frame oder das Tk-Objekt in dem es dann dargestellt wird.
Ok das verstehe ich jetzt. Habe da noch ein bisschen rumprobiert.


Also, wenn ich die Struktur richtig verstehe, dann erstelle ich in der main Methode eine GUI und füge dann durch eine oder mehrere Klassen verschiedene Widgets ein. In diesen Klassen schreibe ich auch eine Funktion die von einem Button z.b. aufgerufen werden. Diese Funktionen erstellen dann ein oder mehrere Objekte anderer Klassen oder rufen externe Funktionen auf um bspw. eine Berechnung durchzuführen oder eine Textdatei rauszuschreiben oder ähnliches.

Also es wird nach if __name__ == "__main__": gesucht und dann die main Methode aufgerufen. Die main Methode erstellt erst die GUI mit Namen, Reitern usw. und danach objekte der Klassen mit den Widgets auf der GUI. Also hier ist die GUI schon fertig modeliert. In den Klassen mit den Widgets sind dann gleichzeitig auch die Buttons definiert, die andere Funktionen aufrufen für die Berechnung usw., die ich in meinem Fall in ein anderes Modul schreiben will. Also wenn ich das richtig verstehe, gebe ich aus den Klassen und Funktionen für die Berechnung so direkt gar keine Werte zurück an die GUI, sondern rufe die innerhalb der GUI Klassen auf. Es wird quasi innerhalb der GUI Klassen nach Werten gefragt. Die Durchführung der Berechnung findet innerhalb der GUI Klasse statt ist nur woanders definiert. Genauso wie die Erstellung der GUI komplett in der main Funktion statt findet.
Meine Beschreibung ist vielleicht nicht ganz korrekt, aber so stelle ich mir den Ablauf des Programms im Moment vor.

Vielen Dank. Bin jetzt auf jeden Fall schon einen ganzen Schritt weiter. Ich hoffe das wird am Ende auf wertgeschätzt, dass ich mir die Mühe mit der OOP mache und nicht doch einfach alles so runterschreibe :D Mit den Klassen habe ich bisher nur wild rumprobiert, werde jetzt aber mal anfangen meine 3000 Zeilen umzuschreiben und das nochmal von vorne aufzubauen, bevor ich weiterprogrammiere.
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Marvin75854: Sooo unterschiedlich sind Python 2 und 3 nicht. Es gibt mehrere Projekte die beim Umstieg helfen, beispielsweise `future` und `six`. Es gibt ein HOWTO in der Python-Dokumentation, wo auch das frei verfügbare „Porting to Python 3“ verlinkt ist. Und die Dokumentation zu den Hilfsbibliotheken für den Umstieg haben natürlich auch Informationen.

Ich werde, wenn die Zeit dafür gekommen ist, mit `future` arbeiten und die Codebasis erst einmal auf beiden Versionen zum laufen bekommen, und wenn es auf der 3 dann erfolgreich läuft, den Python 2 Support langsam einstellen.

Deine Schaltfläche wurde nicht von alleine ausgelöst, *Du* hast die Funktion/Methode ziemlich wahrscheinlich selbst aufgerufen und deren Rückgabewert als `command` übergeben statt die Funktion/Methode selbst. Ersteres: ``command=self.Zahlenwerte()``. Richtig: ``command=self.Zahlenwerte``.

``lambda`` definiert einfach eine Funktion ohne Namen, a.k.a. eine anonyme Funktion:

Code: Alles auswählen

result = map(lambda x: x * 2, [1, 2, 3])

# <=>

def double(x):
    return x * 2

result = map(double, [1, 2, 3])
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Marvin75854

__blackjack__ hat geschrieben: Donnerstag 26. Juli 2018, 15:33 @Marvin75854: Sooo unterschiedlich sind Python 2 und 3 nicht. Es gibt mehrere Projekte die beim Umstieg helfen, beispielsweise `future` und `six`. Es gibt ein HOWTO in der Python-Dokumentation, wo auch das frei verfügbare „Porting to Python 3“ verlinkt ist. Und die Dokumentation zu den Hilfsbibliotheken für den Umstieg haben natürlich auch Informationen.

Ich werde, wenn die Zeit dafür gekommen ist, mit `future` arbeiten und die Codebasis erst einmal auf beiden Versionen zum laufen bekommen, und wenn es auf der 3 dann erfolgreich läuft, den Python 2 Support langsam einstellen.
OK. Ich habe nämlich keine Ahnung was beim Kunden zur Verfügung steht. Die Chance besteht, dass ich das noch umstellen muss. Habe ich am Anfang gar nicht drüber nachgedacht.

__blackjack__ hat geschrieben: Donnerstag 26. Juli 2018, 15:33 ``lambda`` definiert einfach eine Funktion ohne Namen, a.k.a. eine anonyme Funktion:

Code: Alles auswählen

result = map(lambda x: x * 2, [1, 2, 3])

# <=>

def double(x):
    return x * 2

result = map(double, [1, 2, 3])
Ah ok, das könnte vielleicht auch noch praktisch werden.


Ich brauche schon wieder eure Hilfe. Und zwar habe ich jetzt den ersten kleinen Bereich mit der Funktion auf meiner GUI erstellt. Ich erstelle 3 Klassen mit GUI elementen auf 3 verschiedenen Reitern. Nach der Eingabe der Koordinaten auf dem ersten Reiter wird durch einen Export Button über eine andere Klasse "HDPA_Berechnung in einem anderen Modul eine Berechnung durchgeführt und etwas in eine textdatei geschrieben. Mein Problem ist jetzt, dass ich nicht weiß wie ich auch die Klasse "HDPA_Sitzreihe_2" und damit nochmal die Klasse "HDPA_Berechnung" und im späteren Verlauf noch einige andere Klassen einbinde. Also, dass ich am Ende nur einen Export Button habe. Meine Lösung wäre die gesamte GUI in eine Klasse zu schreiben oder evtl. eine Extra Klasse für den Button zu schreiben der dann eine Funktion aufruft in der die Klasse "HDPA_Sitzreihe_1", "HPDA_Sitzreihe_2" und damit eben öfters die Klasse "HDPA_Berechnung" und alle möglichen anderen Klassen die noch hinzukommen, aufruft. Ich weiß allerdings nicht wie genau das umsetzbar ist. Habt ihr einen Tipp, wie ich das Problem lösen kann?
Hier mein Code:

Code: Alles auswählen

#!/usr/bin/python
from __future__ import print_function
import subprocess
from Tkinter import *
import Tkinter, Tkconstants, tkFileDialog
import os
from tkMessageBox import *
import math
import sys
import time
import Tix as tx



import Programmlogik



class HDPA_Sitzreihe_1(Frame):
    def __init__(self, parent):
        Frame.__init__(self, parent)
        
        Label(self, text="X").grid(row=13)
	Label(self, text="Z").grid(row=13, column=1)

	self.hx = Entry(self)
	self.hz = Entry(self)
	self.hx.grid(row=14, column=0)
	self.hz.grid(row=14, column=1)

	Label(self, text="Y-Abstand: COG/H-Punkt zu Airbag").grid(row=15,pady=(20,0))
	self.dy = Entry(self)
	self.dy.grid(row=16, column=0) 
        
        
        self.HDPA = Label(self, text="H-Punkt WS50%")
	self.HDPA.grid(row=12, column=0, padx=20)
        
	self.box = IntVar()
	Checkbutton(self, text="HDPA Feld", variable=self.box).grid(row=11)        
        
        self.logo = PhotoImage(file="Sitzreferenzfeld.gif")
	w1 = Label(self, image=self.logo).grid(row=0, column=0, pady=20,padx=20, rowspan=10, columnspan=5)
        
        self.sitzreihe = 1
        self.i = 1
        
        
        self.Export_button = Button(self, text="Export", command=self.Export)
        self.Export_button.grid(row=18, column=0)
        
        
        
    def Export(self):
    
        if self.box.get() == 1:
        
            filename = tkFileDialog.asksaveasfilename(initialdir = os.getcwd(),title = "Speichern der Include-Datei",filetypes = (("all files","*.*"),("inc files","*.inc")))
            f = open(filename,"a")
            f.write("$Erstellt durch COG-Tool am: "+time.strftime("%d.%m.%Y %H:%M:%S")+"\n")
            time.sleep(1)
            
            HDPA = Programmlogik.HDPA_Berechnung(self.hx, self.hz, self.dy, filename, self.sitzreihe, self.i)
            HDPA.HDPA_Berechnen() 
            HDPA.HDPA_Ausgabe()
            
            f = open(filename,"a")
            f.write("ENDDATA\n") 
            f.close()
        else:
            print("Kein HDPA-Feld")
            print(self.hx)
                
        
class HDPA_Sitzreihe_2(Frame):
    def __init__(self, parent):
        Frame.__init__(self, parent)
    
	Label(self, text="X").grid(row=13)
	Label(self, text="Z").grid(row=13, column=1)
        
	self.HX = Entry(self)
	self.HZ = Entry(self)
	self.HX.grid(row=14, column=0)
	self.HZ.grid(row=14, column=1)
        
	Label(self, text="Y-Abstand: COG/H-Punkt zu Airbag").grid(row=15,pady=(20,0))
	self.HY = Entry(self)
	self.HY.grid(row=16, column=0)

	self.HDPA = Label(self, text="H-Punkt WS50%")
	self.HDPA.grid(row=12, column=0, padx=20)

	box = IntVar()
	Checkbutton(self, text="HDPA Feld", variable=box).grid(row=11)
        
        self.logo = PhotoImage(file="Sitzreferenzfeld_2.Reihe.gif")
	w2 = Label(self, image=self.logo).grid(row=0, column=0, pady=20,padx=20, rowspan=10, columnspan=5)
        
        
class HDPA_Sitzreihe_3(Frame):
    def __init__(self, parent):
        Frame.__init__(self, parent)        
        
        self.logo = PhotoImage(file="Sitzreferenzfeld_3.Reihe.gif")
        w = Label(self, image=self.logo).grid(row=0, column=0, pady=20,padx=20, rowspan=10, columnspan=5)
        
        
        

def main():
    GUI = tx.Tk()
    GUI.title ("COG Tool") 
    GUI.geometry("1800x800")
    
    swr=tx.ScrolledWindow(GUI)
    swr.pack(fill=tx.BOTH, expand=1)
    
    nb=tx.NoteBook(swr.window)
    nb.pack(fill=tx.BOTH, expand=1)

    nb.add("sitzreihe"+str(1),label="1. Sitzreihe")
    nb.add("sitzreihe"+str(2),label="2. Sitzreihe")
    nb.add("sitzreihe"+str(3),label="3. Sitzreihe")
     
    
    hdpa_sitzreihe_1 = HDPA_Sitzreihe_1(nb.sitzreihe1)
    hdpa_sitzreihe_1.grid(row=0, column=0)
    
    
    hdpa_sitzreihe_2 = HDPA_Sitzreihe_2(nb.sitzreihe2)
    hdpa_sitzreihe_2.grid(row=0, column=0)
 
    hdpa_sitzreihe_3 = HDPA_Sitzreihe_3(nb.sitzreihe3)
    hdpa_sitzreihe_3.grid(row=0, column=0) 
 
    
    GUI.mainloop()


if __name__ == "__main__": 
    main()

und das 2. Modul:

Code: Alles auswählen

#!/usr/bin/python
from __future__ import print_function
import subprocess
from Tkinter import *
import Tkinter, Tkconstants, tkFileDialog
import os
from tkMessageBox import *
import math
import sys
import time
import Tix as tx


class HDPA_Berechnung(object):

    def __init__(self, hx, hz, dy, filename, sitzreihe, i):
        self.hx = float(hx.get())
        self.hz = float(hz.get())
        self.dy = float(dy.get())
        self.sitzreihe = int(sitzreihe)
        self.filename = filename
        self.i = i
        
    def HDPA_Berechnen(self):
        self.links_unten_x = self.hx + 126 - 82   # ACHTUNG: Hier noch + SITZLAENGSVERSTELLWEG
        self.links_unten_z = self.hz + 594 -58    # ACHTUNG: Hier noch + SITZLAENGSVERSTELLWEG
        self.rechts_unten_x = self.hx + 147 +82   # ACHTUNG: Hier noch + SITZLAENGSVERSTELLWEG
        self.rechte_unten_z = self.hz + 594 -52   # ACHTUNG: Hier noch + SITZLAENGSVERSTELLWEG
        self.links_oben_x = self.hx + 126 - 82    # ACHTUNG: Hier noch + SITZLAENGSVERSTELLWEG
        self.links_oben_z = self.hz + 693 + 82    # ACHTUNG: Hier noch + SITZLAENGSVERSTELLWEG
        self.rechts_oben_x = self.hx + 147 + 82   # ACHTUNG: Hier noch + SITZLAENGSVERSTELLWEG
        self.rechts_oben_z = self.hz + 693 + 82   # ACHTUNG: Hier noch + SITZLAENGSVERSTELLWEG
            
        self.delta_y = self.dy - 365
        
    def HDPA_Ausgabe(self):
        
        f = open(self.filename,"a")
        f.write("$H-Punkt: ("+str(self.hx)+", -365, "+str(self.hz)+")\n")
        f.write("$HDPA-Feld fuer die "+str(self.sitzreihe)+". Sitzreihe:\n")
        f.write("NODE  / 1000000"+str(self.i)+"%16.6f%16.6f%16.6f\n"%(self.links_unten_x, self.delta_y, self.links_unten_z))
        f.write("NODE  / 1000000"+str(self.i+1)+"%16.6f%16.6f%16.6f\n"%(self.rechts_unten_x, self.delta_y, self.rechte_unten_z))
        f.write("NODE  / 1000000"+str(self.i+2)+"%16.6f%16.6f%16.6f\n"%(self.links_oben_x, self.delta_y, self.links_oben_z))
        f.write("NODE  / 1000000"+str(self.i+3)+"%16.6f%16.6f%16.6f\n"%(self.rechts_oben_x, self.delta_y, self.rechts_oben_z))
        f.write("SHELL / 1000000"+str(self.sitzreihe)+"       11000000"+str(self.i)+"1000000"+str(self.i+1)+"1000000"+str(self.i+3)+"1000000"+str(self.i+2)+"        \n")
        f.write("PART  / 1000000"+str(self.sitzreihe)+"SHELL          1                        \n")
        f.write("NAMEDefault HDPA_Feld_"+str(self.sitzreihe)+".Sitzreihe\n")
        f.write("                    \n")
        f.write("                              \n")
        f.write("        1.    3               \n\n")
        f.write("END_PART\n")
        f.close()        
        

             
Was mir auch nicht gefällt ist die Lösung wie ich in der Funktion "HDPA_Ausgabe" die Ausgabe mit der Variablen "i" und "sitzreihe" versuche zu automatisieren. Habe da aber noch keine wirkliche Lösung gefunden.
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Marvin75854: Die `HDPA_Sitzreihe_*`-Klassen sehen nicht so aus als sollten das tatsächlich drei Klassen sein/werden. Wenn Du so viel Code kopierst, machst Du etwas falsch.

Die Programmlogik bekommt GUI-Elemente übergeben → das sollte nicht sein. Andererseits schreibt die GUI etwas in die Datei, was wohl auch eher zur Ausgabefunktion in der Programmlogik gehört. Die gleiche Datei wird auch dreimal hintereinander geöffnet (und nicht jedes mal wieder geschlossen!), das erscheint mir auch komisch.

`HDPA_Berechnung` sieht für mich immer noch nicht nach einer sinnvollen Klasse aus. Das sollten Funktionen sein die Argumente übergeben bekommen und Ergebnisse zurückliefern. Die `__init__()` definiert auch wieder nicht alle Attribute.

Die vielen `write()`-Aufrufe mit der Mischung aus literalen Zeichenketten, ``+``, `str()`-Aufrufen, und Formatierung von Teilen mit ``%`` ist sehr unübersichtlich. Da würde ich *eine* mehrzeilige Zeichenkette mit Platzhaltern für die `format()`-Methode verwenden.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Marvin75854

__blackjack__ hat geschrieben: Freitag 27. Juli 2018, 17:55 @Marvin75854: Die `HDPA_Sitzreihe_*`-Klassen sehen nicht so aus als sollten das tatsächlich drei Klassen sein/werden. Wenn Du so viel Code kopierst, machst Du etwas falsch.
Da die trotzdem immer unterschiedlich sind brauche ich ja 3 Klassen oder ich schreibe alles untereinander in eine.
__blackjack__ hat geschrieben: Freitag 27. Juli 2018, 17:55 Die Programmlogik bekommt GUI-Elemente übergeben → das sollte nicht sein. Andererseits schreibt die GUI etwas in die Datei, was wohl auch eher zur Ausgabefunktion in der Programmlogik gehört. Die gleiche Datei wird auch dreimal hintereinander geöffnet (und nicht jedes mal wieder geschlossen!), das erscheint mir auch komisch.
Was für GUI-Elemente werden denn übergeben? Bzw. wie übergebe ich denn nur die Werte davon? Wusste nicht wie ich das sonst lösen soll, weil ich

Code: Alles auswählen

            filename = tkFileDialog.asksaveasfilename(initialdir = os.getcwd(),title = "Speichern der Include-Datei",filetypes = (("all files","*.*"),("inc files","*.inc")))
            f = open(filename,"a")
            f.write("$Erstellt durch COG-Tool am: "+time.strftime("%d.%m.%Y %H:%M:%S")+"\n")
            time.sleep(1)
am Anfang der Text datei haben will und

Code: Alles auswählen

            f = open(filename,"a")
            f.write("ENDDATA\n") 
            f.close()
und am Ende. Und wenn ich die Funktion HDPA_Ausgabe ofters aufrufe, habe ich das ja auch mehrfach in der Textdatei stehen.

__blackjack__ hat geschrieben: Freitag 27. Juli 2018, 17:55 `HDPA_Berechnung` sieht für mich immer noch nicht nach einer sinnvollen Klasse aus. Das sollten Funktionen sein die Argumente übergeben bekommen und Ergebnisse zurückliefern. Die `__init__()` definiert auch wieder nicht alle Attribute.
Bekommt die Funktion die Argumente nicht über die __init__? Und die Ergebnisse sind bspw. self.links_unten_x. Meinst du auch z.b. self.links_unten_x mit Attribute die ich nicht definiert habe? Ich muss die Ergebnisse aber doch irgendwie benennen. Ich sehe da irgendwie den Unterschied zu euren Code Beispielen nicht wirklich.
Weiß gerade auch nicht, ob das überhaupt Sinn macht. Mein Programm hat vorher ja auch funktioniert und diese OOP kostet mich extrem viel Zeit. Mache seit zwei Wochen absolut keinen Fortschritt mehr und habe immer noch nichts verstanden.

__blackjack__ hat geschrieben: Freitag 27. Juli 2018, 17:55 Die vielen `write()`-Aufrufe mit der Mischung aus literalen Zeichenketten, ``+``, `str()`-Aufrufen, und Formatierung von Teilen mit ``%`` ist sehr unübersichtlich. Da würde ich *eine* mehrzeilige Zeichenkette mit Platzhaltern für die `format()`-Methode verwenden.
Werde ich mir mal angucken. Es ist halt sehr wichtig, dass das Format in der Textdatei am Ende so eingehalten wird. Nicht aus optischen Gründen, sondern weil es sonst einfach nicht funktioniert.
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Marvin75854: Wo Unterscheiden sich die `HDPA_Sitzreihe_*`\en denn? Zumindst von dem was man sehen kann ist die `__init__()` mindestens bei den ersten beiden ja gleich. So viel Code zu kopieren würde ich im Grunde sogar als Fehler ansehen und nicht mehr einfach nur als unschön.

Du Übergibst beim erstellen des `HDPA_Berechnung`-Exemplars `hx`, `hz`, und `dy` als `Entry`-Objekte. Das sind Eingabeelemente aus der GUI, davon sollte die Berechnung nichts wissen müssen. Stell Dir mal vor Du möchtest das in einer interaktiven Python-Shell mal testen. Das würde man eigentlich gerne so machen können:

Code: Alles auswählen

>>> from Programmlogik import HDPA_Berechnung
>>> hb = HDPA_Berechnung(42, 23, 47.11, 'test.inc', 1, 1)
>>> hb.hdpa_berechnen()
>>> hb.links_unten_x
86
Geht aber nicht weil die ersten drei Argumente GUI-Eingabeelemente sind. Das heisst statt hier Wert übergeben zu können die man für die reine Berechnung benötigt, muss man erst `Entry`-Elemente erstellen (für die man vorher ein `Tk`-Objekt benötigt) und die Werte dort rein stecken. Anders kann man das nicht testen oder wiederverwenden. Man kann kein anderes GUI-Toolkit verwenden und auch keine Webanwendung daraus machen, obwohl die Art wie die Werte eingegeben wurden, mit der Berechnung eigentlich gar nichts zu tun hat.

Code: Alles auswählen

>>> import Tkinter as tk
>>> from Programmlogik import HDPA_Berechnung
>>> root = tk.Tk()
>>> hx = tk.Entry(root)
>>> hx.set('42')
>>> hz = tk.Entry(root)
>>> hz.set('23')
>>> dy = tk.Entry(root)
>>> dy.set('47.11')
>>> hb = HDPA_Berechnung(hx, hz, dy, 'test.inc', 1, 1)
>>> hb.hdpa_berechnen()
>>> hb.links_unten_x
86
>>> root.destroy()
Wenn Du die `HDPA_Ausgabe()` mehrfach aufrufst, machst Du in dem Code aber nicht, *und* es macht auch dann immer noch keinen Sinn die Datei tatsächlich dreimal zu öffnen und zu schliessen. Die könnte man einmal öffnen und dann nicht den Dateinamen übergeben, sondern das offene Dateiobjekt. Ich persönlich würde den Datenfluss dann vielleicht auch umkehren: Nicht den Dateinamen oder das Dateiobjekt an die `HDPA_Ausgabe()` übergeben, sondern die Zeilen/Daten von `HDPA_Ausgabe()` zurückgeben lassen, damit der Aufrufer dann damit machen kann was er möchte. Beispielsweise die Daten in eine Datei schreiben.

Bei `HDPA_Berechnung()` bekommt die Funktion die Argumente in der Tat über die `__init__()`. Was keinen Sinn macht, denn man könnte ihr die Argumente auch direkt übergeben. Also der *Funktion*, denn eine Methode ist das ja nicht wirklich.

Genau, `links_unten_x` & Co sind die Attribute die nicht in der `__init__()` definiert werden. Klar musst Du die Ergebnisse irgendwie benennen, aber die gehören halt nicht als Attribute auf die Klasse, genau so wenig wie die Argumente als Attribute auf diese Klasse gehören. Eventuell kann es Sinn machen Argumente für die Funktion und das Ergebnis der Funktion jeweils in einem eigenen Datentyp zusammen zu fassen, aber alle drei Sachen — Argumente, Funktion, und Ergebnisse — gehören einfach so nicht in einer Klasse.

Ich denke Du fängst bei der Programmlogik am falschen Ende an Daten in Klassen zusammenzufassen. Schreib das lieber erst einmal als Funktionen und schau dann was sich an ”Kleinteilen” zusammenfassen lässt, statt von ”oben” anzufangen und gleich erst mal die komplette Berechnung und Ausgabe in eine Klasse zu stecken.

Die Berechnungsklasse mal als Funktionen, mit einem `collections.namedtuple()` um das Ergebnis der `hdpa_berechnen()`-Funktion zu einem Objekt zusammenfassen zu können:

Code: Alles auswählen

from collections import namedtuple

HDPAResult = namedtuple(
    'HDPAResult',
    'links_unten_x links_unten_z'
    ' rechts_unten_x rechts_unten_z'
    ' links_oben_x links_oben_z'
    ' rechts_oben_x rechts_oben_z'
    ' delta_y'
)

        
def hdpa_berechnen(hx, dy, hz, ddy):
    return HDPAResult(
        hx + 126 - 82, hz + 594 - 58,
        hx + 147 + 82, hz + 594 - 52,
        hx + 126 - 82, hz + 693 + 82,
        hx + 147 + 82, hz + 693 + 82,
        dy + ddy,
    )


def hdpa_ausgabe(hx, ddy, hz, sitzreihe, i_start, hdpa_result, out_file):
    out_file.write(
        '$H-Punkt: ({0}, {1}, {2})\n'
        '$HDPA-Feld fuer die {3}. Sitzreihe:\n'.format(hx, ddy, hz, sitzreihe)
    )
    node_values = [
        (hdpa_result.links_unten_x, hdpa_result.links_unten_z),
        (hdpa_result.rechts_unten_x, hdpa_result.rechts_unten_z),
        (hdpa_result.links_oben_x, hdpa_result.links_oben_z),
        (hdpa_result.rechts_oben_x, hdpa_result.rechts_oben_z),
    ]
    for i, (x, z) in enumerate(node_values, i_start):
        out_file.write(
            'NODE  / 1000000{0}{1:16.6f}{2:16.6f}{3:16.6f}\n'.format(
                i, x, hdpa_result.delta_y, z
            )
        )
    out_file.write(
        'SHELL / 1000000{0}       11000000{1}        \n'
        'PART  / 1000000{0}SHELL          1                        \n'
        'NAMEDefault HDPA_Feld_{0}.Sitzreihe\n'
        '                    \n'
        '                              \n'
        '        1.    3               \n\n'
        'END_PART\n'.format(
            sitzreihe,
            '1000000'.join(str(i_start + j) for j in [0, 1, 3, 2]),
        )
    )
Hier sieht man IMHO ganz gut das es Sinn machen könnte eine Klasse für 3D-Punkte einzuführen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Sollen wirklich die ganzen Zahlen ohne Leerraum ausgegeben werden? Das erinnert so an kaputten Fortran-Output, den man nicht mehr parsen kann, sobald die Zahlen zu groß werden. Diese NODE-Zahlen, die da aneinandergeklebt werden, sehen auch komisch aus.
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Sirius3: Da es sich ja wohl um irgendwelche numerischen Modellrechnungen/Simulationen handelt, könntest Du mit Fortran vielleicht sogar richtig liegen. Marvin75854's letzter Satz spricht IMHO auch für ein ”fixed size” Ein-/Ausgabeformat.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Marvin75854

__blackjack__ hat geschrieben: Montag 30. Juli 2018, 10:38 @Marvin75854: Wo Unterscheiden sich die `HDPA_Sitzreihe_*`\en denn? Zumindst von dem was man sehen kann ist die `__init__()` mindestens bei den ersten beiden ja gleich. So viel Code zu kopieren würde ich im Grunde sogar als Fehler ansehen und nicht mehr einfach nur als unschön.
Ja stimmt, im Grunde genommen unterscheidet sich bei der 1. und 2. Nur das PhotoImage. Das kann ich aber ja auch einfach in der main Methode übergeben. Dann müsste auch noch übergeben werden um welche Sitzreihe es sich handelt, damit die Ausgabe in der Programmlogik angepasst wird. Also da kann ich eine Klasse löschen. Die 3. Klasse lasse ich erst mal so oder schreibe das PhotoImage in die main methode.

__blackjack__ hat geschrieben: Montag 30. Juli 2018, 10:38 Du Übergibst beim erstellen des `HDPA_Berechnung`-Exemplars `hx`, `hz`, und `dy` als `Entry`-Objekte. Das sind Eingabeelemente aus der GUI, davon sollte die Berechnung nichts wissen müssen. Stell Dir mal vor Du möchtest das in einer interaktiven Python-Shell mal testen. Das würde man eigentlich gerne so machen können:

Code: Alles auswählen

>>> from Programmlogik import HDPA_Berechnung
>>> hb = HDPA_Berechnung(42, 23, 47.11, 'test.inc', 1, 1)
>>> hb.hdpa_berechnen()
>>> hb.links_unten_x
86
Geht aber nicht weil die ersten drei Argumente GUI-Eingabeelemente sind. Das heisst statt hier Wert übergeben zu können die man für die reine Berechnung benötigt, muss man erst `Entry`-Elemente erstellen (für die man vorher ein `Tk`-Objekt benötigt) und die Werte dort rein stecken. Anders kann man das nicht testen oder wiederverwenden. Man kann kein anderes GUI-Toolkit verwenden und auch keine Webanwendung daraus machen, obwohl die Art wie die Werte eingegeben wurden, mit der Berechnung eigentlich gar nichts zu tun hat.

Code: Alles auswählen

>>> import Tkinter as tk
>>> from Programmlogik import HDPA_Berechnung
>>> root = tk.Tk()
>>> hx = tk.Entry(root)
>>> hx.set('42')
>>> hz = tk.Entry(root)
>>> hz.set('23')
>>> dy = tk.Entry(root)
>>> dy.set('47.11')
>>> hb = HDPA_Berechnung(hx, hz, dy, 'test.inc', 1, 1)
>>> hb.hdpa_berechnen()
>>> hb.links_unten_x
86
>>> root.destroy()
Ja. Also, wenn ich bei mir das .get() der übergebenen Werte lösche, funktioniert das Testen. Also liegt es wohl daran. Ich habe mir nochmal eure Beispiele angeguckt und der einzige Unterschied ist, dass die get() Methode schon in der GUI Klasse verwendet wird und in der Programmlogik dann nur noch das hx. Ich glaube jetzt verstehe ich das. Mit dem .get() in der Programmlogik wurde das Entry Feld aus der GUI geholt. So wie es jetzt ist, wird das Feld aus der GUI schon vorher geholt und in der Programmlogik nur noch der Wert verwendet?

__blackjack__ hat geschrieben: Montag 30. Juli 2018, 10:38 Wenn Du die `HDPA_Ausgabe()` mehrfach aufrufst, machst Du in dem Code aber nicht, *und* es macht auch dann immer noch keinen Sinn die Datei tatsächlich dreimal zu öffnen und zu schliessen. Die könnte man einmal öffnen und dann nicht den Dateinamen übergeben, sondern das offene Dateiobjekt. Ich persönlich würde den Datenfluss dann vielleicht auch umkehren: Nicht den Dateinamen oder das Dateiobjekt an die `HDPA_Ausgabe()` übergeben, sondern die Zeilen/Daten von `HDPA_Ausgabe()` zurückgeben lassen, damit der Aufrufer dann damit machen kann was er möchte. Beispielsweise die Daten in eine Datei schreiben.
Ja in meinem Beispiel ist das noch nicht mehrfahr aufgerufen, weil ich die Funktion bei Sitzreihe 2 noch nicht eingefügt habe. Da soll eben die selbe Berechnung mit anderen Punkten durchgeführt und die Ergebnisse im selben Format ausgegeben werden. Das heißt ich rufe die Funktion z.b. zwei mal auf und geben jeweil das Ergebnis zurück. Mit dem Code den du gezeigt hast. Und dann kann ich die Daten in eine Datei schreiben. Dafür schreibe ich am Besten noch eine extra Funktion?
Ich habe dann aber immer noch das Problem, dass in diese Datei zum späteren Zeitpunkt noch mehr geschrieben werden soll. Ich will also nur einen Button auf der GUI haben. Wenn ich jetzt alle Elemente auf der GUI in eine Klasse schreibe, dann könnte ich das Problem lösen, aber das will ich ja eigentlich vermeiden. Sonst könnte ich es ja genauso gut global lassen.
__blackjack__ hat geschrieben: Montag 30. Juli 2018, 10:38 Bei `HDPA_Berechnung()` bekommt die Funktion die Argumente in der Tat über die `__init__()`. Was keinen Sinn macht, denn man könnte ihr die Argumente auch direkt übergeben. Also der *Funktion*, denn eine Methode ist das ja nicht wirklich.
Ja ich verstehe immer noch nicht den Unterschied zwischen Funktionen und Methoden. Also einer Funktion kann man Argumente direkt übergeben und eine Methode bekommt ihre Argumente innerhalb einer Klasse von der __init__ .

__blackjack__ hat geschrieben: Montag 30. Juli 2018, 10:38 Genau, `links_unten_x` & Co sind die Attribute die nicht in der `__init__()` definiert werden. Klar musst Du die Ergebnisse irgendwie benennen, aber die gehören halt nicht als Attribute auf die Klasse, genau so wenig wie die Argumente als Attribute auf diese Klasse gehören. Eventuell kann es Sinn machen Argumente für die Funktion und das Ergebnis der Funktion jeweils in einem eigenen Datentyp zusammen zu fassen, aber alle drei Sachen — Argumente, Funktion, und Ergebnisse — gehören einfach so nicht in einer Klasse.

Ich denke Du fängst bei der Programmlogik am falschen Ende an Daten in Klassen zusammenzufassen. Schreib das lieber erst einmal als Funktionen und schau dann was sich an ”Kleinteilen” zusammenfassen lässt, statt von ”oben” anzufangen und gleich erst mal die komplette Berechnung und Ausgabe in eine Klasse zu stecken.
Ja den Absatz verstehe ich überhaupt nicht. Ja so werde ich das jetzt mal machen.
__blackjack__ hat geschrieben: Montag 30. Juli 2018, 10:38 Die Berechnungsklasse mal als Funktionen, mit einem `collections.namedtuple()` um das Ergebnis der `hdpa_berechnen()`-Funktion zu einem Objekt zusammenfassen zu können:

Code: Alles auswählen

from collections import namedtuple

HDPAResult = namedtuple(
    'HDPAResult',
    'links_unten_x links_unten_z'
    ' rechts_unten_x rechts_unten_z'
    ' links_oben_x links_oben_z'
    ' rechts_oben_x rechts_oben_z'
    ' delta_y'
)

        
def hdpa_berechnen(hx, dy, hz, ddy):
    return HDPAResult(
        hx + 126 - 82, hz + 594 - 58,
        hx + 147 + 82, hz + 594 - 52,
        hx + 126 - 82, hz + 693 + 82,
        hx + 147 + 82, hz + 693 + 82,
        dy + ddy,
    )


def hdpa_ausgabe(hx, ddy, hz, sitzreihe, i_start, hdpa_result, out_file):
    out_file.write(
        '$H-Punkt: ({0}, {1}, {2})\n'
        '$HDPA-Feld fuer die {3}. Sitzreihe:\n'.format(hx, ddy, hz, sitzreihe)
    )
    node_values = [
        (hdpa_result.links_unten_x, hdpa_result.links_unten_z),
        (hdpa_result.rechts_unten_x, hdpa_result.rechts_unten_z),
        (hdpa_result.links_oben_x, hdpa_result.links_oben_z),
        (hdpa_result.rechts_oben_x, hdpa_result.rechts_oben_z),
    ]
    for i, (x, z) in enumerate(node_values, i_start):
        out_file.write(
            'NODE  / 1000000{0}{1:16.6f}{2:16.6f}{3:16.6f}\n'.format(
                i, x, hdpa_result.delta_y, z
            )
        )
    out_file.write(
        'SHELL / 1000000{0}       11000000{1}        \n'
        'PART  / 1000000{0}SHELL          1                        \n'
        'NAMEDefault HDPA_Feld_{0}.Sitzreihe\n'
        '                    \n'
        '                              \n'
        '        1.    3               \n\n'
        'END_PART\n'.format(
            sitzreihe,
            '1000000'.join(str(i_start + j) for j in [0, 1, 3, 2]),
        )
    )
Hier sieht man IMHO ganz gut das es Sinn machen könnte eine Klasse für 3D-Punkte einzuführen.
Ok ich kann den Code nicht so richtig nachvollziehen was in dem Code passiert, werde mal ein bisschen rumprobieren.
Marvin75854

Sirius3 hat geschrieben: Montag 30. Juli 2018, 11:04 Sollen wirklich die ganzen Zahlen ohne Leerraum ausgegeben werden? Das erinnert so an kaputten Fortran-Output, den man nicht mehr parsen kann, sobald die Zahlen zu groß werden. Diese NODE-Zahlen, die da aneinandergeklebt werden, sehen auch komisch aus.
__blackjack__ hat geschrieben: Montag 30. Juli 2018, 12:18 @Sirius3: Da es sich ja wohl um irgendwelche numerischen Modellrechnungen/Simulationen handelt, könntest Du mit Fortran vielleicht sogar richtig liegen. Marvin75854's letzter Satz spricht IMHO auch für ein ”fixed size” Ein-/Ausgabeformat.
Ja richtig. Das soll am Ende eine Pamcrash Include Datei werden. Für ein HDPA Feld sieht das Ganze dann ungefähr so aus:

Code: Alles auswählen

$Erstellt durch COG-Tool am: 30.07.2018 14:20:52
$H-Punkt: (50, -365, 50)
$HDPA-Feld fuer die 1. Sitzreihe:
NODE  / 10000001       94.000000     -415.000000      592.000000
NODE  / 10000002      279.000000     -415.000000      592.000000
NODE  / 10000003       94.000000     -415.000000      825.000000
NODE  / 10000004      279.000000     -415.000000      825.000000
SHELL / 10000001       110000001100000021000000410000003        
PART  / 10000001SHELL          1                        
NAMEDefault HDPA_Feld_1.Sitzreihe
                    
                              
        1.    3               

END_PART
ENDDATA
Die Datei wird am Ende dann noch wesentlich lännger, je nachdem was auf der GUI alles eingegeben wird. Man kann das in Python sicherlich noch eleganter schreiben. Da kann ich später nochmal gucken, ob ich das noch anpasse.
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Marvin75854: Wenn man sich das mal mit Punkten überlegt, dann scheint die Berechnungsfunktion beispielsweise vier Punkte zu berechnen, wobei die Y-Koordinaten bei allen gleich ist. Das Ergebnis könnt man also durch eine Liste mit vier Punkten ersetzen.

Code: Alles auswählen

class Vector(object):
    
    def __init__(self, x=0, y=0, z=0):
        self.x = x
        self.y = y
        self.z = z
    
    def __str__(self):
        return '({0.x}, {0.y}, {0.z})'.format(self)
    
    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y, self.z + other.z)

        
def hdpa_berechnen(point, ddy):
    return [
        point + Vector(126 - 82, ddy, 594 - 58),
        point + Vector(147 + 82, ddy, 594 - 52),
        point + Vector(126 - 82, ddy, 693 + 82),
        point + Vector(147 + 82, ddy, 693 + 82),
    ]


def hdpa_ausgabe(h_point, sitzreihe, i_start, node_points, out_file):
    out_file.write(
        '$H-Punkt: {0}\n'
        '$HDPA-Feld fuer die {1}. Sitzreihe:\n'.format(h_point, sitzreihe)
    )
    i = None
    for i, point in enumerate(node_points, i_start):
        out_file.write(
            'NODE  / 1000000{0}{1.x:16.6f}{1.y:16.6f}{1.z:16.6f}\n'.format(
                i, point
            )
        )
    indices = [0, 1, 3, 2]
    if len(indices) != i:
        raise ValueError(
            'wrong number of node points ({} != {})'.format(len(indices), i)
        )
    out_file.write(
        'SHELL / 1000000{0}       11000000{1}        \n'
        'PART  / 1000000{0}SHELL          1                        \n'
        'NAMEDefault HDPA_Feld_{0}.Sitzreihe\n'
        '                    \n'
        '                              \n'
        '        1.    3               \n\n'
        'END_PART\n'.format(
            sitzreihe,
            '1000000'.join(str(i_start + j) for j in indices),
        )
    )
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@__blackjack__: Vector könnte man noch eine __format__-Methode spendieren:

Code: Alles auswählen

class Vector(object):

    def __init__(self, x=0, y=0, z=0):
        self.x = x
        self.y = y
        self.z = z

    def __str__(self):
        return '({0.x}, {0.y}, {0.z})'.format(self)

    def __format__(self, format):
        return '{0.x:{1}}{0.y:{1}}{0.z:{1}}'.format(self, format)

    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y, self.z + other.z)

[...]

    for i, point in enumerate(node_points, i_start):
        out_file.write('NODE  / 1000000{}{:16.6f}\n'.format(i, point))
Marvin75854

__blackjack__ hat geschrieben: Montag 30. Juli 2018, 14:09 @Marvin75854: Wenn man sich das mal mit Punkten überlegt, dann scheint die Berechnungsfunktion beispielsweise vier Punkte zu berechnen, wobei die Y-Koordinaten bei allen gleich ist. Das Ergebnis könnt man also durch eine Liste mit vier Punkten ersetzen.

Code: Alles auswählen

class Vector(object):
    
    def __init__(self, x=0, y=0, z=0):
        self.x = x
        self.y = y
        self.z = z
    
    def __str__(self):
        return '({0.x}, {0.y}, {0.z})'.format(self)
    
    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y, self.z + other.z)

        
def hdpa_berechnen(point, ddy):
    return [
        point + Vector(126 - 82, ddy, 594 - 58),
        point + Vector(147 + 82, ddy, 594 - 52),
        point + Vector(126 - 82, ddy, 693 + 82),
        point + Vector(147 + 82, ddy, 693 + 82),
    ]


def hdpa_ausgabe(h_point, sitzreihe, i_start, node_points, out_file):
    out_file.write(
        '$H-Punkt: {0}\n'
        '$HDPA-Feld fuer die {1}. Sitzreihe:\n'.format(h_point, sitzreihe)
    )
    i = None
    for i, point in enumerate(node_points, i_start):
        out_file.write(
            'NODE  / 1000000{0}{1.x:16.6f}{1.y:16.6f}{1.z:16.6f}\n'.format(
                i, point
            )
        )
    indices = [0, 1, 3, 2]
    if len(indices) != i:
        raise ValueError(
            'wrong number of node points ({} != {})'.format(len(indices), i)
        )
    out_file.write(
        'SHELL / 1000000{0}       11000000{1}        \n'
        'PART  / 1000000{0}SHELL          1                        \n'
        'NAMEDefault HDPA_Feld_{0}.Sitzreihe\n'
        '                    \n'
        '                              \n'
        '        1.    3               \n\n'
        'END_PART\n'.format(
            sitzreihe,
            '1000000'.join(str(i_start + j) for j in indices),
        )
    )
Ich will echt nicht immer alles nachfragen, aber ich habe keine Ahnung wie ich das jetzt verwenden soll. Also wie ich das in der GUI Klasse aufrufen soll. Ich muss ja jetzt die Eingabefelder in eine Liste schreiben und dann übergeben als "point und h_point? Die Funktion hdpa_berechnen verwendet die Klasse Vector bereits automatisch? Das heißt ich muss irgendwie die zwei Funktionen aufrufen. Ich werde mir das morgen nochmal angucken. Verstehe nicht mal was genau die Funktionen wie machen
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Marvin75854: Ups, ich hatte einen Beitrag von Dir übersehen.

Der Unterschied zwischen einer Funktion und einer Methode ist nicht der wie die Argumente übergeben werden, das ist letztlich bei beiden gleich, sondern das eine Methode zu einem Objekt gehört in dem diese Methode, Daten die den Zustand des Objekts beschreiben, und noch andere Methoden, *sinnvoll* zusammengefasst sind. Und Du hast da IMHO zwei Funktionen und Argumente für die Funktionen zu einer Klasse zusammengefasst die semantisch gar keine Klasse ist. Was man dadurch zeigen kann, das es sich ganz einfach als zwei einzelne Funktionen schreiben lässt, und dabei IMHO auch gleich noch lesbarer wird, weil bei `berechnen()` und `ausgabe()` jetzt sichtbarer ist, was die jeweils an Werten brauchen und an den Aufrufer zurückgeben.

Bei der Variante mit `Vector` übergibt man nun statt `hx`, `dy`, und `hz` einzeln, ein `Vector`-Objekt für `point`. Und für `ddy` die -365 die vorher hart kodiert an zwei Stellen im Quelltext stand. Bei `h_point` so ähnlich nur das dort die `y`-Komponente die -365 ist.

Ich konnte mich ja nur an den Namen orientieren. Ist das echt so weit an der Bedeutung vorbei die das tatsächlich hat? Bilden die drei Argumente keinen Punkt? Und erstellt `HDPA_Berechnen()` nicht etwas das sich durch vier Punkte im Raum beschreiben lässt, wobei die alle in einer Ebene parallel zur Z-Achse liegen?

Benutzung könnte so aussehen (ungetestet):

Code: Alles auswählen

            ddy = -365
            hx, dy, hz = (
                float(entry.get()) for entry in [self.hx, self.dy, self.hz]
            )

            filename = tkFileDialog.asksaveasfilename(
                initialdir=os.getcwd(),
                title='Speichern der Include-Datei',
                filetypes=(('all files', '*.*'), ('inc files', '*.inc'))
            )
            with open(filename, 'a') as out_file:
                out_file.write(
                    '$Erstellt durch COG-Tool am: {0}\n'.format(
                        time.strftime('%d.%m.%Y %H:%M:%S')
                    )
                )
                
                hdpa_points = hdpa_berechnen(Vector(hx, dy, hz), ddy)
                hdpa_ausgabe(
                    Vector(hx, ddy, hz),
                    self.sitzreihe,
                    self.i,
                    hdpa_points,
                    out_file
                )
                
                out_file.write('ENDDATA\n') 
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Marvin75854

__blackjack__ hat geschrieben: Montag 30. Juli 2018, 21:05 Der Unterschied zwischen einer Funktion und einer Methode ist nicht der wie die Argumente übergeben werden, das ist letztlich bei beiden gleich, sondern das eine Methode zu einem Objekt gehört in dem diese Methode, Daten die den Zustand des Objekts beschreiben, und noch andere Methoden, *sinnvoll* zusammengefasst sind. Und Du hast da IMHO zwei Funktionen und Argumente für die Funktionen zu einer Klasse zusammengefasst die semantisch gar keine Klasse ist. Was man dadurch zeigen kann, das es sich ganz einfach als zwei einzelne Funktionen schreiben lässt, und dabei IMHO auch gleich noch lesbarer wird, weil bei `berechnen()` und `ausgabe()` jetzt sichtbarer ist, was die jeweils an Werten brauchen und an den Aufrufer zurückgeben.
Ja das meinte ich ja ganz am Anfang schon mal, dass ich den Sinn der Klassen nicht so ganz verstehe, weil es mit normalen Funktionen genauso funktioniert und wahrscheinlich übersichtlicher ist. Vielleicht ist mein Code auch nicht perfekt für die Anwendung von Klassen am Anfang. Werde das jetzt wirklich erstmal mit Funktionen alles machen und vielleicht verstehe ich das später dann doch noch besser.

__blackjack__ hat geschrieben: Montag 30. Juli 2018, 21:05 Bei der Variante mit `Vector` übergibt man nun statt `hx`, `dy`, und `hz` einzeln, ein `Vector`-Objekt für `point`. Und für `ddy` die -365 die vorher hart kodiert an zwei Stellen im Quelltext stand. Bei `h_point` so ähnlich nur das dort die `y`-Komponente die -365 ist.

Ich konnte mich ja nur an den Namen orientieren. Ist das echt so weit an der Bedeutung vorbei die das tatsächlich hat? Bilden die drei Argumente keinen Punkt? Und erstellt `HDPA_Berechnen()` nicht etwas das sich durch vier Punkte im Raum beschreiben lässt, wobei die alle in einer Ebene parallel zur Z-Achse liegen?
Nein ich habe es jetzt verstanden. Genau so ist es. Man gibt einen Punkt ein aus dem dann eine Fläche errechnet wird. Die y-Koordinate von dem Punkt den man eingibt ist -365. Da die immer -365 ist, wäre es einfach unnötig die eingeben zu müssen. Und ddy ist dann einfach die Verschiebung der Fläche auf der y-Achse. ddy soll auch später noch für ganze andere Funktionen verwendet werden. Wie ich das mache, weiß ich noch nicht.

__blackjack__ hat geschrieben: Montag 30. Juli 2018, 21:05 Benutzung könnte so aussehen (ungetestet):

Code: Alles auswählen

            ddy = -365
            hx, dy, hz = (
                float(entry.get()) for entry in [self.hx, self.dy, self.hz]
            )

            filename = tkFileDialog.asksaveasfilename(
                initialdir=os.getcwd(),
                title='Speichern der Include-Datei',
                filetypes=(('all files', '*.*'), ('inc files', '*.inc'))
            )
            with open(filename, 'a') as out_file:
                out_file.write(
                    '$Erstellt durch COG-Tool am: {0}\n'.format(
                        time.strftime('%d.%m.%Y %H:%M:%S')
                    )
                )
                
                hdpa_points = hdpa_berechnen(Vector(hx, dy, hz), ddy)
                hdpa_ausgabe(
                    Vector(hx, ddy, hz),
                    self.sitzreihe,
                    self.i,
                    hdpa_points,
                    out_file
                )
                
                out_file.write('ENDDATA\n') 
Das hilft mir extrem. Konnte ich auch fast genau so übernehmen. Ich werde jetzt wirklich erstmal versuchen alles in Funktionen zu machen. Die GUI Elemente würde ich aber weiterhin gerne auf Klassen aufteilen, weiß aber nicht so richtig wie ich dann am Ende über einen Ausgabe Button alles Funktionen bündeln kann.
Im Moment ist es ja so, dass ich mit einer Klasse hdpa die GUI Elemente auf dem ersten Reiter erzeuge. Dann habe ich die Klasse hdpa in der main Methode nochmal aufgerufen um auch auf dem zweiten Reiter der GUI die Elemente zu erzeugen. In der Klasse habe ich auch den Export Button definiert der die Funktionen in der Programmlogik aufruft. Der Funktioniert auch, allerdings habe ich das Problem, dass ich den Export Button jetzt zwei mal habe. Und es kommen später auch noch weitere Funktionen dazu die auch was exportieren sollen. Ich will also einen Export Button, der alle Berechnungen durchführt und alle Ergebnisse der gesamten GUI ausgibt. Bzw. wichtig ist nur die Ausgabe. Die Berechnungen können schon vorher durchgeführt worden sein.
Also meine Idee war eine eigene Klasse für den Button zu schreiben der dann die GUI Klassen aufruft. Dann hätte ich die GUI Elemente aber ja alle doppelt übereinander liegen. Hier nochmal der Code. Das meiste stammt ja eh von dir. :D :

Code: Alles auswählen

#!/usr/bin/python
from __future__ import print_function
import subprocess
from Tkinter import *
import Tkinter, Tkconstants, tkFileDialog
import os
from tkMessageBox import *
import math
import sys
import time
import Tix as tx

import Programmlogik_2



class HDPA_Sitzreihe_1(Frame):
    def __init__(self, parent, name_Bild):
        Frame.__init__(self, parent)
        
        Label(self, text="X").grid(row=13)
	Label(self, text="Z").grid(row=13, column=1)
        self.name_Bild = name_Bild

	self.hx = Entry(self)
	self.hz = Entry(self)
	self.hx.grid(row=14, column=0)
	self.hz.grid(row=14, column=1)

	Label(self, text="Y-Abstand: COG/H-Punkt zu Airbag").grid(row=15,pady=(20,0))
	self.dy = Entry(self)
	self.dy.grid(row=16, column=0) 
        
        
        self.HDPA = Label(self, text="H-Punkt WS50%")
	self.HDPA.grid(row=12, column=0, padx=20)
        
	self.box = IntVar()
	Checkbutton(self, text="HDPA Feld", variable=self.box).grid(row=11)        
        
        self.logo = PhotoImage(file=self.name_Bild)
	w1 = Label(self, image=self.logo).grid(row=0, column=0, pady=20,padx=20, rowspan=10, columnspan=5)
        
        self.sitzreihe = 1
        self.i = 1
        
        
        
        
        
        self.Export_button = Button(self, text="Export", command=self.Export)
        self.Export_button.grid(row=18, column=0)
        
        
        
    def Export(self):
    
        if self.box.get() == 1:
        
            ddy = -365
        
            hx, dy, hz = (
                float(entry.get()) for entry in [self.hx, self.dy, self.hz]
            )
        
            filename = tkFileDialog.asksaveasfilename(
                initialdir=os.getcwd(),
                title='Speichern der Include-Datei',
                filetypes=(('all files', '*.*'), ('inc files', '*.inc'))
            )
            with open(filename, 'a') as out_file:
                out_file.write(
                    '$Erstellt durch COG-Tool am: {0}\n'.format(
                        time.strftime('%d.%m.%Y %H:%M:%S')
                    )
                )
                
                hdpa_points = Programmlogik_2.hdpa_berechnen(Programmlogik_2.Vector(hx, dy, hz), ddy)
                Programmlogik_2.hdpa_ausgabe(
                    Programmlogik_2.Vector(hx, ddy, hz),
                    self.sitzreihe,
                    self.i,
                    hdpa_points,
                    out_file
                    
                )
                
                out_file.write('ENDDATA\n') 
            
        else:
            print("Kein HDPA-Feld")
            print(self.hx)
                
        
        
        
#class HDPA_Sitzreihe_3(Frame):
#    def __init__(self, parent):
#        Frame.__init__(self, parent)        
#        
#        self.logo = PhotoImage(file="Sitzreferenzfeld_3.Reihe.gif")
#        w = Label(self, image=self.logo).grid(row=0, column=0, pady=20,padx=20, rowspan=10, columnspan=5)
        
        
        

def main():
    GUI = tx.Tk()
    GUI.title ("COG Tool") 
    GUI.geometry("1800x800")
    
    swr=tx.ScrolledWindow(GUI)
    swr.pack(fill=tx.BOTH, expand=1)
    
    nb=tx.NoteBook(swr.window)
    nb.pack(fill=tx.BOTH, expand=1)

    nb.add("sitzreihe"+str(1),label="1. Sitzreihe")
    nb.add("sitzreihe"+str(2),label="2. Sitzreihe")
    nb.add("sitzreihe"+str(3),label="3. Sitzreihe")
     
    
    hdpa_sitzreihe_1 = HDPA_Sitzreihe_1(nb.sitzreihe1,"Sitzreferenzfeld.gif")
    hdpa_sitzreihe_1.grid(row=0, column=0)
    
    
    hdpa_sitzreihe_2 = HDPA_Sitzreihe_1(nb.sitzreihe2,"Sitzreferenzfeld_2.Reihe.gif")
    hdpa_sitzreihe_2.grid(row=0, column=0)
 
#    hdpa_sitzreihe_3 = HDPA_Sitzreihe_3(nb.sitzreihe3)
#    hdpa_sitzreihe_3.grid(row=0, column=0) 
 
    
    GUI.mainloop()


if __name__ == "__main__": 
    main()

Und die Programmlogik:

Code: Alles auswählen

#!/usr/bin/python
from __future__ import print_function
import subprocess
from Tkinter import *
import Tkinter, Tkconstants, tkFileDialog
import os
from tkMessageBox import *
import math
import sys
import time
import Tix as tx


class Vector(object):
    
    def __init__(self, x=0, y=0, z=0):
        self.x = x
        self.y = y
        self.z = z
    
    def __str__(self):
        return '({0.x}, {0.y}, {0.z})'.format(self)
    
    def __add__(self, other):
        return Vector(self.x + other.x, self.y + other.y, self.z + other.z)

        
def hdpa_berechnen(point, ddy):
    return [
        point + Vector(126 - 82, ddy, 594 - 58),
        point + Vector(147 + 82, ddy, 594 - 52),
        point + Vector(126 - 82, ddy, 693 + 82),
        point + Vector(147 + 82, ddy, 693 + 82),
    ]


def hdpa_ausgabe(h_point, sitzreihe, i_start, node_points, out_file):
    out_file.write(
        '$H-Punkt: {0}\n'
        '$HDPA-Feld fuer die {1}. Sitzreihe:\n'.format(h_point, sitzreihe)
    )
    i = None
    for i, point in enumerate(node_points, i_start):
        out_file.write(
            'NODE  / 1000000{0}{1.x:16.6f}{1.y:16.6f}{1.z:16.6f}\n'.format(
                i, point
            )
        )
    indices = [0, 1, 3, 2]
    if len(indices) != i:
        raise ValueError(
            'wrong number of node points ({} != {})'.format(len(indices), i)
        )
    out_file.write(
        'SHELL / 1000000{0}       11000000{1}        \n'
        'PART  / 1000000{0}SHELL          1                        \n'
        'NAMEDefault HDPA_Feld_{0}.Sitzreihe\n'
        '                    \n'
        '                              \n'
        '        1.    3               \n\n'
        'END_PART\n'.format(
            sitzreihe,
            '1000000'.join(str(i_start + j) for j in indices),
        )
    )
Meine Idee wäre eine Klasse für den Button zu schreiben, der dann eine Methode/Funktion aufruft die über die hdpa Klassen und die Klassen die noch später dazu kommen die jeweiligen Berechnungs/Ausgabe Funktionen aufruft. Also dass nicht erneut ein Objekt der Klassen erzeugt wird, sondern nur auf das bereits erzeugte Objekt zugegriffen wird. Also, dass die Funktionen der Klasse durch eine externe Klasse aufgerufen werden. Keine Ahnung ob ich das richtig erklären kann. Vielleicht könnte ich das über Vererbungen machen? Ich will das nicht wieder alles zu extrem miteinander verstricken.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Marvin75854 hat geschrieben: Dienstag 31. Juli 2018, 08:07 Meine Idee wäre eine Klasse für den Button zu schreiben,
Ein Button ist einfach nur ein Button, der braucht keine eigene Klasse. Was Du machen könntest, wäre eine Klasse für das Hauptfenster, denn das hat ja mit den ganzen Sitzreihen und Knöpfen einiges an Zustand.

Module werden komplett kein geschrieben, Programmlogik_2 hat außerdem noch eine Nummer, was kein guter Modulname ist. Komplett groß geschrieben Namen sind für Konstanten reserviert, GUI sollte also gui heißen, denn Variablen und Attribute werden komplett_klein geschrieben. Sternchenimporte vermeiden, die ganzen Tkinter-Namen kannst Du ja auch über tx.xxx referenzieren.
Marvin75854 hat geschrieben: Dienstag 31. Juli 2018, 08:07Also dass nicht erneut ein Objekt der Klassen erzeugt wird, sondern nur auf das bereits erzeugte Objekt zugegriffen wird.
Von welchen Klassen sprichst Du hier? Bisher hast Du nur GUI-Klassen, die einmal am Anfang erzeugt werden. Für die Berechnung sehe ich noch nicht die Notwendigkeit von Klassen. Vererbung ist dann noch eine Stufe später dran. Erst wenn Du merkst, dass es sinnvoll ist aus Funktionen eine Klasse zu machen und wenn Du später mehrere Klassen hast, die gemeinsame Methoden und Zustände haben, dann kannst Du darüber nachdenken, eine Überklasse einzurichten, aber wirklich nur dann, wenn es eine IST-EINE-Beziehung zwischen den Klassen gibt.
Marvin75854

Sirius3 hat geschrieben: Dienstag 31. Juli 2018, 08:46 Von welchen Klassen sprichst Du hier? Bisher hast Du nur GUI-Klassen, die einmal am Anfang erzeugt werden. Für die Berechnung sehe ich noch nicht die Notwendigkeit von Klassen. Vererbung ist dann noch eine Stufe später dran. Erst wenn Du merkst, dass es sinnvoll ist aus Funktionen eine Klasse zu machen und wenn Du später mehrere Klassen hast, die gemeinsame Methoden und Zustände haben, dann kannst Du darüber nachdenken, eine Überklasse einzurichten, aber wirklich nur dann, wenn es eine IST-EINE-Beziehung zwischen den Klassen gibt.
Ja bisher schon. Ich werde aber am Ende noch mehrere Klassen schreiben, da auf meiner GUI ja sehr viel mehr Eingabefelder sein sollen als ich bisher habe. Ich will am Ende alles über einen Button laufen lassen. Also ein Button soll sich Werte aus Eingabefeldern von der GUI holen, mehrere Funktionen aufrufen und alles in eine Text Datei schreiben. Die GUI Elemente in den Klassen werden ja ganz am Anfang in der main Methode erzeugt. Jetzt habe ich bisher immer nur aus der Klasse selbst auf Funktionen zugegriffen. Aber wie kann ich es hinkriegen, dass der Button aus bspw. 3 verschiedenen Klassen mit GUI Elementen die Werte holt und damit 5 verschiedene Funktionen ausführt. Ich wüsste wie ich es mache, wenn ich die gesamte GUI in eine Klasse schreibe, aber dann kann ich es ja auch gleich wieder global definieren...
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Marvin75854: Die drei verschiedenen Klassen mit GUI-Elementen müssen da halt eine Methode für vorsehen. So wie `Entry`-Objekte eine `get()`-Methode haben.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Antworten