«fischertechnik Designer» und Python: Beschreibung der Schnittstelle

Alles rund um TX(T) und RoboPro, mit ft-Hard- und Software
Computing using original ft hard- and software
Forumsregeln
Bitte beachte die Forumsregeln!
Antworten
DieterStriebel
Beiträge: 24
Registriert: 13 Mär 2023, 17:49
Wohnort: Schweiz, Breitenbach

«fischertechnik Designer» und Python: Beschreibung der Schnittstelle

Beitrag von DieterStriebel » 16 Mär 2023, 15:07

So, liebe Python-Interessierte jetzt geht es ins Eingemachte.
Dateifreigabe
In loser Folge (bei schönem Wetter bin ich im Garten) werde ich diesen Beitrag immer wieder ergänzen, so dass ein Handbuch entsteht (Eure Frage geben die Richtung vor). Ich weiss es gibt bessere System für so eine Doku, aber wir sind nun einmal hier und können einfach interagieren
Für diejenigen, die über diesen Beitrag zu Thema gekommen sind, bitte auch «fischertechnik Designer» und Python: Konfiguration - Probleme und Lösungen... beachten
  1. Einführung
    Los geht es: (ftD gestartet, Modell geladen, Python-Manager gestartet)
    ich kommentiere gewisse Zeilen, viele Befehle sprechen aber für sich

    Code: Alles auswählen

    #Module enthalten Python-Erweiterungen für verschiedene Bereiche
    import ftd		#Modul "ftd" in den Python-Interpreter laden
    			#    dieses Modul implementiert alle Funktionen für den ftD
    #import math		#"math" implementiert Funktionen wie "sin()", "cos()", etc.
    			#    aber noch brauchen wir diese Funktionen nicht, darum ist die Zeile mit "#" auskommentiert
    print ftd.Version()	#"print" erkläre ich nicht ;-)
     			#"Version()" ist eine Methode der Python-Erweiterung für den ftD
    
    lngPartCount = ftd.GetPartCount()	#eine andere Methode, die das Ergebnis einer Variable zuweist
    print "Anzahl Bausteine:", lngPartCount 
    
    Ausgabe:

    Code: Alles auswählen

    Version 1.9.0
    Anzahl Bausteine: 6907
    bis hier mal zum Anfang... weiter geht es dann mit diesem Code als Ausganglage:

    Code: Alles auswählen

    import ftd
    lngPartCount = ftd.GetPartCount()
    prt = ftd.GetPartAtIndex(lngPartCount-1)            #Ja, ich weiss es ging dann anders weiter ;-)
    
    schauen wir uns mal die 3. Zeile an:
    • prt1: eine Variable; diese bekommt einen Wert zugewiesen vom Ausdruck nach dem "="
    • ftd.: Verwende eine Funktion aus dem Modul "ftd"
    • GetPartAtIndex (jeder Grossbuchstabe leitet ein neues Wort ein)
      • Get: Bitte gebe mir...
      • Part: ...den "Baustein" (oder Light, Definition, etc.)...
      • At: ...von der...
      • Index: ...Position (die Zählung startet bei 0)
    • (lngPartCount-1)
      • "(" ... ")": umschliesst Parameter, die der vorgestellten Funktion übergeben werden
      • lngPartCount-1: ist die Nummer (Index) des letzten hinzugefügten Bauteils
        • lngPartCount: Anzahl der Bausteine im Modell (bei 5 Bausteine: gibt "GetPartCount()" "5" zurück
        • -1: der Index beginnt aber bei 0, also hat das letzte Bauteil den Index "GetPartCount() - 1" = "4"
    also das Ganze recht gut lesbar.... diese Beschreibung war für absolute Neulinge gedacht, in Zukunft geht es dann mit weniger Erklärungen weiter.
    Bei Bedarf werde ich dieses und die folgenden Abschnitte je nach Euren Reaktionen ergänzen,
  2. Systematische Dokumentation
    die Python Erweiterung für ftD enthält eine Modul und verschiedene Klassen

    Code: Alles auswählen

    Modul	ftd	
    Klasse	Part		Bauteil
    Klasse	Object3D	Position von … im Raum
    Klasse	Point3D		<später mehr>
    Klasse	Color		Farbe von …
    Klasse	Camera		<später mehr>
    Klasse	Definition	<später mehr>
    Klasse	Grid		<später mehr>
    Klasse	Light		<später mehr>
    
    1. Einleitung
      1. Bedeutung der, in den folgenden Abschnitten verwendeten Variablennamen:
        • _PartNumber: The number of the part in the library e.g. "31010"
        • _PartName: The represented name of the part in the current scene e.g. „F31010_1“
        • _Index: die Nummer eines Objektes, wobei das erste Element die Nummer 0 (Null) hat
      • Bedeutung der in den folgenden Tabellen verwendeten Farben
        • grün: aus der Dokumentation von Michael
        • blau: von mir selber "erforscht" ;-)
    2. Modul: ftd
      module ftd.jpg
      module ftd.jpg (212.5 KiB) 795 mal betrachtet
    3. Klasse: Part (ein Bauteil)
      Klasse Part.jpg
      Klasse Part.jpg (54.82 KiB) 795 mal betrachtet
    4. Klasse: Object3D (die Position von … im Raum
    5. Klasse: Point3D
    6. Klasse: Color (die Farbe von …)
    7. Klasse: Camera
    8. Klasse: Definition
    9. Klasse: Grid
    10. Klasse: Light
  3. Entwicklung des Seriengenerators und andere Grundlagen
    1. im folgenden die Python Grundlagen zu "if ... elif ... else (wenn ... sonst-wenn ... sonst)" (Theorie: Python If ... Else)

      Code: Alles auswählen

      import ftd                           #wenn Du diese Zeile nicht verstehst, muss du weiter oben anfangen ;-)
      prt = ftd.GetSelectedPart()          #alles klar, oder? ;-)
      if (prt):                            #wenn das Objekt "prt" existiert, dann... 
        print "das von Dir gewählte Bauelement wird verwendet"
      elif (ftd.GetPartCount() == 0):      #bei Bedingungen: beachte die "(", ")" und den ":"
        print "kein Bauteil vorhanden, also 'Add.en' wir eines"
        prtName = ftd.AddPart("31004")   #Add.. ==> Name des neuen Elements 
        ftd.SelectPart(prtName)          #das neue Bauteil (über seinen Namen) auswählen 
        prt = ftd.GetSelectedPart()
      else:
        print "kein Bauteil ausgewählt, aber es sind Bausteine vorhanden, also nehmen wir das letzte das hinzugefügt wurde"
        prtCount = ftd.GetPartCount()
        prt = ftd.GetPartAtIndex(prtCount-1)
      print "Name:", prt.Name, "Bauphase:", prt.Phase
      
    2. Benutzereingabe (Auswahl des neuen Bausteins) [folgendes ist nur ein Ausschnitt aus obigen Skript, gekennzeichnet mit "::"]

      Code: Alles auswählen

          ::
      elif (ftd.GetPartCount() == 0):
        print "kein Bauteil vorhanden, also 'Add.en' wir eines, ...             ...aber welches?"
        eingabe = ftd.InputDlg("Seriengenerator", "welches Bauteil möchtest Du verwenden", "31004")
                                         #ok, alles klar, Danke für die Eingabe
        prtName = ftd.AddPart(eingabe)   #jetzt kommt das Bauteil aus einer Variable
         
        ftd.SelectPart(prtName)
        prt = ftd.GetSelectedPart()
      else:
          ::
      
    3. Einschub: Verschieben und Drehen (der "Koordinaten-Manager (Alt+Q) in Python)
      {der Koordinaten-Manager passt hier nicht wirklich rein, zeigt aber sehr schön wie die Schnittstelle funktioniert}

      Code: Alles auswählen

      import ftd
      prt=ftd.GetSelectedPart()
      if (prt):
        prt.Object3D.RotateRed(45)    #Winkel in Grad  !!! wird später noch wichtig sein!!! 
        prt.Object3D.RotateGreen(20)
        prt.Object3D.RotateBlue(30)
        prt.Object3D.MoveRed(15)      #Verschiebung in Millimeter
                                      #ja klar, geht auch mit 'Green' und 'Blue'
      
      ... und lernen gleich eine ganz wichtige Klasse kennen "Object3D", Uhhhh... die sphärische Trigonometrie, habe ich in der Schule vor bald 50 Jahren auch verpasst, aber da müssen wir durch ;-)
    4. Was braucht es für eine Serie?
      Eine Wiederholung die uns 99 Bauteile erzeugt (Theorie: Python For Loops)
      Ausgangslage: ein Baustein 31003 (Baustein 30 grau)
      31003 (Baustein 30 grau).jpg
      31003 (Baustein 30 grau).jpg (6.59 KiB) 743 mal betrachtet

      Code: Alles auswählen

      import ftd
      prt = ftd.GetSelectedPart()                    #prt ist das von Dir ausgewählte Bauteil
      if (prt):                                      #hast du wirklich eines ausgewählt?
        print "Name:   ", prt.Name                   #  JA
        orgNumber = prt.Name[1:6]                    #  aus dem Namen (FC0050_1), lesen wir uns "FC0050"
        print "Nummer: ", orgNumber
        for i in range(9):                           #  von 0 bis 9 (oder 99)
          prtName = ftd.AddPart(orgNumber)           #    füge das vorher selectierte Bauteil erneut hinzu
          print i, "Name: ", prtName
          ftd.SelectPart(prtName)                    #    Bauteil über den Namen auswählen (Anklicken)
          prt = ftd.GetSelectedPart()                #    prt ist unser neues Objekt(Baustein)
          prt.Object3D.MoveRed(15*(i+1))             #    verschiebe prt in roter Richtung (kleines Ein-Mal-Eins)
      else:                                          #hast du wirklich eines ausgewählt?
        ftd.MessageDlg("Kein Bauteil ausgewählt")     #  NEIN
      ftd.MessageDlg("ENDE, nächste Staffel folgt...") #Guet Nacht, Zämme
      
      Da siehst Du ein Problem (Zapfen) für das ich bis jetzt keine Lösung habe. Es hat auch gar nichts mit Python zu tun, das Problem liegt am ftD selber. Es betriff Bauteile, die aus zwei einzelnen Teilen bestehen.
      Ein Ketteglied (z.B. 36248) (besteht aus nur einem Teil, ist darum vom obigen Problem nicht betroffen.
      .... Ohhhh, ganz übel, das ist noch nicht eine Kette!!!, hmmm was ist die Lösung
    5. 2 Bausteine die uns als Referenz dienen, der 2. wird dann 99 mal wiederholt. Diese erstellen wir uns vorerst mit der Maus
      2 * 36248 (Rasterkettenglied rot)
      ab und zu ein Bild ist doch gut als Auflockerung ;-)
      ab und zu ein Bild ist doch gut als Auflockerung ;-)
      2 x 36248 (Rasterkettenglied rot).jpg (7.62 KiB) 759 mal betrachtet
    6. ... die Distanz der beiden (Referenz-)Bausteine im 3D-Raum

      Code: Alles auswählen

      folgt
      
[/list]
[/list]
Liebe Leserinnen und liebe Leser: Bitte schreibt, ob das so lesbar und verständlich ist, Danke
Für die oben gezeigten Beschreibung von "ftd" und "Part" folgt bald ein Link damit Ihr die komplette Beschreibung als pdf oder Excel herunterladen könnt
und ganz WICHTIG: ich hasse Typfehler (sehe diese immer nur bei andern), also bitte Korrekturvorschläge per PN an mich
Des weitern bin ich bemüht, alle neuen Erkenntnisse, die wie ZUSAMMEN erarbeiten, laufend in der Beschreibung ein zu pflegen
Zuletzt geändert von DieterStriebel am 31 Mär 2023, 13:29, insgesamt 5-mal geändert.

DieterStriebel
Beiträge: 24
Registriert: 13 Mär 2023, 17:49
Wohnort: Schweiz, Breitenbach

Re: «fischertechnik Designer» und Python: Beschreibung der Schnittstelle

Beitrag von DieterStriebel » 19 Mär 2023, 12:16

so, jetzt bin ich auf Reaktionen gespannt
bitte gebt mir Bescheid per PN wenn Ihr noch dabei seid
Danke Dieter

DieterStriebel
Beiträge: 24
Registriert: 13 Mär 2023, 17:49
Wohnort: Schweiz, Breitenbach

Re: «fischertechnik Designer» und Python: Beschreibung der Schnittstelle

Beitrag von DieterStriebel » 31 Mär 2023, 16:33

So nun geht es also weiter.
Wir wollen versuchen, zu berechnen wo die neuen Bauteile platziert werden müssen.
Im folgenden den ganzen GANZE Python-Code in 4 Teile aufgeteilt
  1. letzte Bauteile feststellen, selektieren und in neue Phase verschieben

    Code: Alles auswählen

    import ftd
    import math
        
    PartCount = ftd.GetPartCount()
    #get the two last added parts.                  >>> based on this parts, the serie will be generated
    prt1 = ftd.GetPartAtIndex(PartCount-2)          #    vorletztes Bauteil, wir in Variable 'prt1' gespeichert     
    prt2 = ftd.GetPartAtIndex(PartCount-1)          #    letztes Bauteil, ...
    #select the two last added parts
    ftd.SelectPart(prt1.Name)                       #Bauteil auswählen, anhand seines Namens
    ftd.CoSelectPart(prt2.Name)                     #weiteres Bauteil auswählen
    #move selected parts zu new phase
    
    PhaseNew = prt1.Phase + 1                       #Phase von 'prt1' um 1 erhöht, in Variable speichern
    prt1.Phase = PhaseNew                           #    beide Bausteine bekommen die neue Phase zugewiesen
    prt2.Phase = PhaseNew                           #        damit wird die neue Bauphase auch angelegt (ABER NICHT AKTIVIERT)
    ftd.RepaintPhaseView()                          #    damit das im Designer sichtbar ist, muss der Bildschirm neu gezeichnet werden
    
  2. Position im Raum und deren Differenz

    Code: Alles auswählen

    #get Object3D
    o3d1 = prt1.Object3D                            #vom 1. Bauteil holen wir uns die Informationen zur Anordnung im Raum
    o3d2 = prt2.Object3D                            #...
    pos1 = o3d1.Position                            #die Classe 'Object3D' enthält auch eine Klasse 'Position'
    pos2 = o3d2.Position                            #    von welcher wir unten gleich die Koordinaten x, y und z verwenden
    
    #calculate difference                           Differenz der Position im Raum berechnen
    PosDiffX = pos2.x - pos1.x                      
    PosDiffY = pos2.y - pos1.y
    PosDiffZ = pos2.z - pos1.z
    
  3. Startpunkt und neues Bauteil

    Code: Alles auswählen

    #store new starting point                       abspeichern, der Position des 2. Bauteils,  
    PosStartX = pos2.x                              #    diese Information benötigen wir später
    PosStartY = pos2.y                              #    um Anhand der Differenz, zum 1. Bauteil
    PosStartZ = pos2.z                              #    die Position des neuen Bauteils zu berechnen
    
    #OrgNumberNew = prt2.OrgNumber                  #ja, wäre schön wenn es diese Eigenschaft geben würde, ist leider (noch) nicht der Fall
    OrgNumberNew = prt2.Name[1:6]                   #aber, wenn wir vom Namen die Zeichen 1 bis und ohne 6 (die Zählung beginnt bei 0) entnehmen, haben wir auch die OrgNummer
    NumberOfNewParts = int(ftd.InputDlg("Serie erstellen", "Anzahl Bauteile", "0"))
    
  4. Serie erzeugen

    Code: Alles auswählen

    for i in range(NumberOfNewParts):               #jetzt die Serie erzeugen, von 0 bis ...
        #create new part
        NameNew = ftd.AddPart(OrgNumberNew)
        ftd.CoSelectPart(NameNew)                   #neues Bauteil auswählen, anhand seines Namens
        prtNew = ftd.GetPartAtIndex(PartCount + i)  #in Variable speichern
        prtNew.Phase = PhaseNew                     #Phase zuweisen
        ftd.RepaintPhaseView()
        o3dNew = prtNew.Object3D                    #Anordung im Raum (siehe oben)
        posNew = o3dNew.Position                    #Position
        #   set new coordinates based on the starting-point and the different of the origianly two last added parts
        posNew.x = PosStartX + PosDiffX             #die neue Position setzen, 
        posNew.y = PosStartY + PosDiffY             #    basierend auf, 
        posNew.z = PosStartZ + PosDiffZ             #    Startposition plus Differenz
        prtNew.CommitChanges(0)     
        #   and store it as new starting point 
        PosStartX = posNew.x                        #und die neue Startposition festlegen
        PosStartY = posNew.y
        PosStartZ = posNew.z
    
Mit "print" Werte ausgeben im Ausgabefenster, Beispiele:

Code: Alles auswählen

print prt1.Name, prt2.Name
print pos1.x
print pos1, pos2
so weit so gut, aber es gibt noch viele Probleme
  • Baustein 30 (31003): besteht aus Körper und Zapfen ==> 2 Teile
  • Drehung der Bausteine, die gesamte sphärischen Berechnungen fehlen noch
  • und sicher noch viel anderes
So weit bis jetzt, viel Vergnügen
Dieter

Antworten