Support Forum

Preview unterscheidet sich von platzierten PythonParts [Gelöst]

Schlagworte:
  • Allplan
  • 2023
  • Pythonpart
  • PythonPartUtil
  • Cylinder3D
  • Preview

Hallo zusammen,

ich habe die Vermutung dass ich die "PythonPartUtil"-Klasse etwas falsch verwende. Derzeit platziere ich über eine Schleife mehrere Zylinder als PythonParts auf meinem Teilbild. Hierbei wird bei jedem Part ggf. die Lage(x,y,z) und Zylinderhöhe angepasst. Die Anpassung der Lage wird in jedem Durchgang korrekt durchgeführt, lediglich die Höhe der Zylinder ist falsch. Alle Zylinder haben nämlich die Höhe des ersten platzierten Zylinders. Das komische hierbei ist, dass in der Vorschau in Allplan alle Zylinder mit korrekter Höhe angezeigt werden. Im Grunde genauso wie ich sie auch in ihrer Lage und Höhe haben möchte. Da die Vorschau richtig funktioniert, denke ich dass bis zu diesem Punkt alles richtig läuft und bei der eigentlichen Erstellung als PythonPart ein Fehler sein muss. Anbei habe ich euch meinen derzeitigen Code angehängt, vielleicht findet den Denkfehler jemand.

#----------------- Geometrie des Zylinders über Attribute festlege
zylinder = AllplanGeo.Cylinder3D()
zylinder.MajorRadius = 250
zylinder.MinorRadius = 250
zylinder.Apex = AllplanGeo.Point3D(0.0, 0.0, self.oberkante - self.unterkante)
zylinder.LocalPlacement = AllplanGeo.AxisPlacement3D(AllplanGeo.Point3D(0, 0, 0),AllplanGeo.Vector3D(1 , 0 , 0),AllplanGeo.Vector3D(0, 0, 1))

#----------------- Zylinderkörper erzeugen
körper = AllplanGeo.CreatePolyhedron(zylinder, 36)
körper = AllplanGeo.Cylinder3D(zylinder

#----------------- Platzieren des PP
startpunkt = AllplanGeo.Point3D(0.0, 0.0, 0.0)
endpunkt = AllplanGeo.Point3D(self.x_koord, self.y_koord, self.unterkante)
verschiebevektor = AllplanGeo.Vector3D(startpunkt, endpunkt)
placement_matrix = AllplanGeo.Matrix3D()
placement_matrix.SetTranslation(verschiebevektor)

local_placement_matrix = AllplanGeo.Matrix3D()

pythonpart = PythonPartUtil()
pythonpart.add_pythonpart_view_2d3d(körper)

zu_erstellende_elemente = pythonpart.create_pythonpart(build_ele, local_placement_matrix = local_placement_matrix, placement_matrix = placement_matrix)

Die Variablen self.oberkante, self.unterkante, self.x_koord, self.y_koord und self.unterkante nehmen nach jedem Durchgang neue Werte an.

Grüße
Niklas

Anhänge (2)

Typ: image/png
30-mal heruntergeladen
Größe: 14,69 KiB
Typ: image/png
23-mal heruntergeladen
Größe: 4,79 KiB

Lösung anzeigen Lösung verbergen

Oh, also Du willst PythonParts mit unterschiedlicher Geometrie absetzen? Das macht die Sache etwas komplizierter, denn eine PythonPart ist wie ein Makro: eine Definition, mehrere Vorkommnisse. Eine PythonPart-Definition ist mit einem SHA-224 Hash gekennzeichnet. Identischer Hash kann in mehreren PythonParts vorkommen und sie werden damit als Identisch erkannt.

PythonPartUtil generiert den Hash basierend auf dem BuildingElement-Objekt, den man als Argument für die create_pythonpart Funktion übergibt. Du kannst es auch selber generieren, denn die get_hash() ist eine Methode des build_ele. D.h.: Identische Parameter in der Palette = identische PythonParts. Die werden im Preview noch unterschiedlich dargestellt, aber beim Erstellen, wenn man die Daten im Datenbank (Teilbild) speichern muss, wird die Definition nur einmal geschrieben und somit werden alle PythonParts identisch gemacht.

Du müsstest also den Hash für jede PythonPart selber generieren und an die Makrodefinition zuweisen. create_pythonpart generiert eine liste mit zwei objekten: Makrodefinition (MacroElement) und Makroverlegung (MacroPlacementElement). Ändere den Hash in dem ersten Objekt mit SetHash:

    ...
        macro, macro_placement = pythonpart.create_pythonpart(build_ele,
                                                              local_placement_matrix = AllplanGeo.Matrix3D(),
                                                              placement_matrix = placement_matrix)

        macro.SetHash(...)
        model_elements.append(macro)
        model_elements.append(macro_placement)

    return CreateElementResult(elements=model_elements,
                               placement_point=AllplanGeo.Point3D())

Das löst aber nicht ein anderes Problem: Modifikationsmodus von den so platzierten PythonParts. Das verhalten einer Standard PythonPart ist nicht dafür ausgelegt, mehrmals verlegt zu werden. Es funktioniert beim Erstellen, aber sobald du auf eine PythonPart doppel-clickst, wird sie entfernt und der Skript wird gestartet. Im Normalfall generiert der Skript die PythonPart erneut, aber hier generiert der Skript erneut mehrere PythonParts. Am Anfang würde aber nur eine entfernt. Am Ende hast du also mehrere PythonParts übereinander. Um das umzugehen, würde ich entweder:

  • PythonParts in eine PythonParGroup zu fassen, oder
  • Zwei Skripe schreiben: einen für den PythonPart selbst und zweiten für die Verlegung (ein Interactor)

Hallo Niklas,

das o.g. Skript ist denke ich nicht vollständig, weil so Du es gescriptet hast kann es nicht funkitonieren. Der grund dafür ist, dass add_python_part_view_2d3d muss einen Objekt bekommen, der von der Klasse AllplanElement erbt. Du muss echte Allplan elemente haben (wand, stütze, 3d körper) um sie in eine PythnPart zu packen. Diese objekte bestehen unter anderem aus Geometrie objekten, haben aber auch weitere informationen, wie z.B. Stift, Strich, Farbe, Attribute, etc... Dein körper ist ein Cylinder3D, was ein reines geomerie ist. DU musst davon einen ModelElement3D machen (3d-Körper in Allplan) und den kannst du erst in einen PythonPart-Slide (view2d3d) einfügen. Hast du wahrscheinlich auch gemacht, weil dein Skript funktioniert ja.

Mit diesem Schritt:

körper = AllplanGeo.CreatePolyhedron(zylinder, 36)
Wandelst Du den Cylinder3D Objekt in einen Polyhedron3D, also tessellierst du seine Geometrie mithilfe von CreatePolyhedron. Im zweiten Schritt überschreibst du sie wieder mit einer Kopie von Zylinder:
körper = AllplanGeo.Cylinder3D(zylinder)
Somit befindet sich unter körper eine Kopie von zylinder. Also ein untesseliertes Zylinder (BRep). Damit ist der erste Schritt umsonst.

Probiere es mal damit:

    # ----------------- Zylinderkörper erzeugen
    zylinder = AllplanGeo.Cylinder3D(AllplanGeo.AxisPlacement3D(),
                                     radiusMajor=250,
                                     radiusMinor=250,
                                     apex=AllplanGeo.Point3D(0.0, 0.0, 1000))
    _, polyhedron    = AllplanGeo.CreatePolyhedron(zylinder, 36)
    common_props     = AllplanBaseElements.CommonProperties()
    model_element_3d = AllplanBasisElements.ModelElement3D(common_props,polyhedron)

    # ----------------- Platzieren des PP

    placement_points = [AllplanGeo.Point3D(),
                        AllplanGeo.Point3D(1000, 0, 0),
                        AllplanGeo.Point3D(500, 1000, 0),
                        AllplanGeo.Point3D(900, 500, 0),
                        ]

    pythonpart = PythonPartUtil()
    pythonpart.add_pythonpart_view_2d3d(model_element_3d)
    placement_matrix = AllplanGeo.Matrix3D()

    model_elements = []

    for placement_point in placement_points:

        placement_matrix.SetTranslation(AllplanGeo.Vector3D(placement_point))

        model_elements.extend(pythonpart.create_pythonpart(build_ele,
                                                           local_placement_matrix = AllplanGeo.Matrix3D(),
                                                           placement_matrix = placement_matrix))

    return CreateElementResult(elements        = model_elements,
                               placement_point = AllplanGeo.Point3D())

Adaptiere den Code zu deinen bedürfnissen und mach dir generell Gedanken, ob du mit Polyhedrons arbeiten willst, oder mit Breps (sh. hier)

Hallo bmarciniec,
danke dir für die mal wieder schnelle Antwort!

Zitiert von: bmarciniec
Hallo Niklas,
das o.g. Skript ist denke ich nicht vollständig, weil so Du es gescriptet hast kann es nicht funkitonieren.

Stimmt, die Zeile hatte ich in meinem Beitrag vergessen! Im Script war sie vorhanden und deshalb hat die Preview funktioniert.

Mit deinem Codeschnipsel komme ich leider zum gleichen Ergebnis. Die Preview passt wieder, jedoch sind die erstellten Zylinder wieder alle mit einer Höhe (Höhenlage und Koordinaten passen!). Anbei mal mein restlicher Code mit dem ich die Schleife starte und die Werte für die Variablen anpasse. Zur Info:
schicht_oks, schicht_uks, bohr_x_koords und bohr_y_koords sind gefüllte Listen. bereinigte_spalten_listen_namen ist ein dictionary, welches mit Listen (und Elementen) gefüllt ist.

def create_element(build_ele, doc):
del doc
#----------------- Ermittlung des Projektoffsets
offset = AllplanSettings.AllplanGlobalSettings.GetOffsetPoint()
offset_xyz = AllplanGeo.Point3D.GetCoords(offset)
offset_x = offset_xyz[0]offset_y = offset_xyz[1]elements =[]

if build_ele.Erstellen.value == True:
for key, value in bereinigte_spalten_listen_namen.items():
for element in value:
zyl = Zylinder()
zyl.oberkante = schicht_oks.pop(0) * 1000
zyl.unterkante = schicht_uks.pop(0) * 1000
zyl.x_koord = bohr_x_koords.pop(0) * 1000 - offset_x
zyl.y_koord = bohr_y_koords.pop(0) * 1000 - offset_y

elements.extend(zyl.erstelle_PP(build_ele))

return CreateElementResult(elements, [], [], AllplanGeo.Point3D(0, 0, 0))

class Zylinder():
def __init__(self):

self.oberkante = None
self.unterkante = None
self.x_koord = None
self.y_koord = None

def erstelle_zylinder(self, build_ele):

#----------------- Zylindereigenschaften
self.eigenschaften = AllplanBaseElements.CommonProperties()
self.eigenschaften.Layer = build_ele.Layer.value
self.eigenschaften.Color = build_ele.Farbe.value
self.eigenschaften.Pen = build_ele.Stift.value
self.eigenschaften.Stroke = build_ele.Strich.value

#----------------- Zylinder erzeugen
zylinder = AllplanGeo.Cylinder3D(AllplanGeo.AxisPlacement3D(),
radiusMajor=250,
radiusMinor=250,
apex=AllplanGeo.Point3D(0.0, 0.0, self.oberkante - self.unterkante))

_, polyhedron = AllplanGeo.CreatePolyhedron(zylinder, 36)
model_element_3d = AllplanBasisElements.ModelElement3D(self.eigenschaften, polyhedron)

#----------------- Platzieren und Erzeugung des PP
placement_point = AllplanGeo.Point3D(self.x_koord, self.y_koord, self.unterkante)
placement_matrix = AllplanGeo.Matrix3D()
placement_matrix.SetTranslation(AllplanGeo.Vector3D(placement_point))

model_elements = []
pythonpart = PythonPartUtil()
pythonpart.add_pythonpart_view_2d3d(model_element_3d)

model_elements.extend(pythonpart.create_pythonpart(build_ele, local_placement_matrix = AllplanGeo.Matrix3D(), placement_matrix = placement_matrix))

return model_elements

Oh, also Du willst PythonParts mit unterschiedlicher Geometrie absetzen? Das macht die Sache etwas komplizierter, denn eine PythonPart ist wie ein Makro: eine Definition, mehrere Vorkommnisse. Eine PythonPart-Definition ist mit einem SHA-224 Hash gekennzeichnet. Identischer Hash kann in mehreren PythonParts vorkommen und sie werden damit als Identisch erkannt.

PythonPartUtil generiert den Hash basierend auf dem BuildingElement-Objekt, den man als Argument für die create_pythonpart Funktion übergibt. Du kannst es auch selber generieren, denn die get_hash() ist eine Methode des build_ele. D.h.: Identische Parameter in der Palette = identische PythonParts. Die werden im Preview noch unterschiedlich dargestellt, aber beim Erstellen, wenn man die Daten im Datenbank (Teilbild) speichern muss, wird die Definition nur einmal geschrieben und somit werden alle PythonParts identisch gemacht.

Du müsstest also den Hash für jede PythonPart selber generieren und an die Makrodefinition zuweisen. create_pythonpart generiert eine liste mit zwei objekten: Makrodefinition (MacroElement) und Makroverlegung (MacroPlacementElement). Ändere den Hash in dem ersten Objekt mit SetHash:

    ...
        macro, macro_placement = pythonpart.create_pythonpart(build_ele,
                                                              local_placement_matrix = AllplanGeo.Matrix3D(),
                                                              placement_matrix = placement_matrix)

        macro.SetHash(...)
        model_elements.append(macro)
        model_elements.append(macro_placement)

    return CreateElementResult(elements=model_elements,
                               placement_point=AllplanGeo.Point3D())

Das löst aber nicht ein anderes Problem: Modifikationsmodus von den so platzierten PythonParts. Das verhalten einer Standard PythonPart ist nicht dafür ausgelegt, mehrmals verlegt zu werden. Es funktioniert beim Erstellen, aber sobald du auf eine PythonPart doppel-clickst, wird sie entfernt und der Skript wird gestartet. Im Normalfall generiert der Skript die PythonPart erneut, aber hier generiert der Skript erneut mehrere PythonParts. Am Anfang würde aber nur eine entfernt. Am Ende hast du also mehrere PythonParts übereinander. Um das umzugehen, würde ich entweder:

  • PythonParts in eine PythonParGroup zu fassen, oder
  • Zwei Skripe schreiben: einen für den PythonPart selbst und zweiten für die Verlegung (ein Interactor)

Hallo bmarciniec,
danke für die tolle und ausführliche Erklärung! Mit der Antwort ist mir die funktionsweise der Parts nochmal ein Stück klarer geworden und mein Ziel konnte ich auch erreichen.

Zitiert von: bmarciniec
PythonPartUtil generiert den Hash basierend auf dem BuildingElement-Objekt, den man als Argument für die create_pythonpart Funktion übergibt. Du kannst es auch selber generieren, denn die get_hash() ist eine Methode des build_ele. D.h.: Identische Parameter in der Palette = identische PythonParts.

In meinem Script wird nun nach jedem Durchgang ein Parameter in der Palette aktualisiert, nimmt also immer einen neuen Wert an. Somit werden nun alle Parts unterschiedlich nach meinen Vorgaben erstellt. Vielleicht nicht die beste oder endgültige Lösung, aber für meine bisherigen Anforderungen ausreichend!