Forum de la communauté Allplan

[Question] StringComboBox im .py Skript füllen | Attribute in der Palette darstellen [Résolu]

Balises:
  • Pythonpart
  • Palette
  • StringComboBox

Hallo zusammen,

ich arbeite gerade an einem PythonPart, bei dem ich einen Parameter vom Typ StringComboBox mit Werten füllen möchte. Es gibt ja die Möglichkeit in der.pyp-Datei das ganz einfach zu regeln indem man die einzelnen Werte mit einem | voneinander trennt (https://pythonparts.allplan.com/2023/manual/key_components/palette/parameters/parameter_with_combo_box/).
Da in meinem Falls die Werte in der Liste dynamisch sind und je nach Anwendungsfall variieren, möchte ich die Werte für den Parameter in der .py-Datei festlegen. Bei anderen Parametern geht das ja auch recht einfach mit z.B. build_ele.Parameter.value = gewünschter Wert. Bei der StringComboBox bekomme ich es jedoch nur hin den ersten Wert in der Auswahl festzulegen, der Rest bleibt leer.

Eine weitere Frage wäre es, wie ich in der py.-Datei erstellte Attribute in der Palette angezeigt bekomme. Aktuell greif ich auf eine Excel-Tabelle in meinem Skript zu, aus dieser werden dann Informationen extrahiert und dann in Form von Attributen an Bauteile angehängt. Ich attribuiere also mit einem Interactor Part und diesem Workflow bestehende Körper in Allplan. Im Skript steht zum Beispiel:
self.attr_list = []
self.attr_list.append((1905, "C20/25"))
AllplanBaseElements.ElementsAttributeService.ChangeAttributes(self.attr_list, sel_elements)

Das Attribut mit der ID 1905 und dem Wert "C20/25" soll nun in der Eingabepalette dargestellt werden um dem Nutzer in einer Art Übersicht zu zeigen welche Attribute von der Excel Datei alle übertragen und an das Bauteil geheftet werden . Ich habe schon versucht die Informationen an einen Parameter vom Typ AttributeIdValue zu übergeben, aber funktioniert hat das nicht. Gibt es hier vielleicht auch einen Ansatz wie das zu schaffen wäre?

Grüße
Niklas Leipert

Show solution Hide solution

Hallo Niklas,

ich nehme alsso für beide deine Fragen an, dass du einen Interactor PythonPart baust. Ich weiß nicht, wann die "Erstellung" der ValueList erfolgen soll, von daher gehe ich davon aus, dass sie ganz am Anfang erfolgt.

bzgl. deiner erste Frage:
In der .pyp definierts du eine parameter mit string Combobox als ValueType, z.B. so einen:

        <Parameter>
            <Name>DynamicStringComboBox</Name>
            <Text>Dynamic String Combo Box</Text>
            <Value></Value>
            <ValueList>Es ist total egal, was hier definiert ist, weil es eher am skript start überschrieben wird</ValueList>
            <ValueType>StringComboBox</ValueType>
        </Parameter>

Jetzt beim start des skripts willst du die ValueList definiren, deshalb in der __init__ Funktion von deiner Interactor-Klasse muss du den ControlPropertiesUtil starten, so wie in diesem Kapitel beschrieben:

        self.ctrl_props_util = ControlPropertiesUtil(control_props_list,
                                                     build_ele_list)

control_props_list ist der 7. argument deiner __init__ Funktion, und der build_ele_list der 5. Das is vom PythonParts Framework so (sh hier). Jetzt kannst du die ValueList von deinem Parameter überdefinieren:

        self.ctrl_props_util.set_value_list("DynamicStringComboBox", "Erster eintrag|zweiter eintrag|dritter eintrag")

Und jetzt kannst du die Palette starten:

        self.palette_service = BuildingElementPaletteService(build_ele_list,
                                                             build_ele_composite,
                                                             build_ele_list[0].script_name,
                                                             control_props_list,
                                                             build_ele_list[0].pyp_file_name)

        self.palette_service.show_palette(build_ele_list[0].script_name)

        self.palette_service.show_palette(self.build_ele.script_name)

Die selbe Logik kannst du überall in dein Skript implementieren. Bitte immer nach dem verwenden der ControlPropertiesUtil die palette aktualisieren (update_palette() vom BuildingElementPaletteService ausführen). Schau mal in die API reference, was der Util noch kann.

Für deine zweite Frage:
Definiere einen inaktiven AttributeIdValue Parameter als eine leere liste in deiner Palette, so:

        <Parameter>
            <Name>DynamicAttributeList</Name>
            <Text>Attributes</Text>
            <Value>[_]</Value>
            <ValueType>AttributeIdValue</ValueType>
            <Enable>False</Enable>
        </Parameter>

Und in deinem .py Datei die attr_list als eine Python-Liste, die sich aber aus AttributeIdValue-Objekten zusammensetzt:
        self.attr_list: list[AttributeIdValue] = []
        self.attr_list.append(AttributeIdValue(1905,"C20/25"))

weil diese kannst du deinem DynamicAttributeList-Parameter als value zuweisen:
        self.build_ele.DynamicAttributeList.value = self.attr_list

Wenn du danach die Palette initialisierst bzw. aktualisierst, solltest du dort deine Attribute sehen.
Wichtig: die Liste kannst du in der Form nicht direkt an die ChangeAttributes-Methode übergeben. Du musst sie in eine Liste von Tuples umbauen, so:

        AllplanBaseElements.ElementsAttributeService.ChangeAttributes([(attr.attribute_id, attr.value) for attr in self.attr_list], sel_elements)

Und noch Vollständigkeitshalber: die Klasse AttributeIdValue musst du natürlich ganz am Anfang importieren:

from AttributeIdValue import AttributeIdValue

Ich hoffe, damit kommst du weiter.

Grüße, Bart

Hallo Niklas,

ich nehme alsso für beide deine Fragen an, dass du einen Interactor PythonPart baust. Ich weiß nicht, wann die "Erstellung" der ValueList erfolgen soll, von daher gehe ich davon aus, dass sie ganz am Anfang erfolgt.

bzgl. deiner erste Frage:
In der .pyp definierts du eine parameter mit string Combobox als ValueType, z.B. so einen:

        <Parameter>
            <Name>DynamicStringComboBox</Name>
            <Text>Dynamic String Combo Box</Text>
            <Value></Value>
            <ValueList>Es ist total egal, was hier definiert ist, weil es eher am skript start überschrieben wird</ValueList>
            <ValueType>StringComboBox</ValueType>
        </Parameter>

Jetzt beim start des skripts willst du die ValueList definiren, deshalb in der __init__ Funktion von deiner Interactor-Klasse muss du den ControlPropertiesUtil starten, so wie in diesem Kapitel beschrieben:

        self.ctrl_props_util = ControlPropertiesUtil(control_props_list,
                                                     build_ele_list)

control_props_list ist der 7. argument deiner __init__ Funktion, und der build_ele_list der 5. Das is vom PythonParts Framework so (sh hier). Jetzt kannst du die ValueList von deinem Parameter überdefinieren:

        self.ctrl_props_util.set_value_list("DynamicStringComboBox", "Erster eintrag|zweiter eintrag|dritter eintrag")

Und jetzt kannst du die Palette starten:

        self.palette_service = BuildingElementPaletteService(build_ele_list,
                                                             build_ele_composite,
                                                             build_ele_list[0].script_name,
                                                             control_props_list,
                                                             build_ele_list[0].pyp_file_name)

        self.palette_service.show_palette(build_ele_list[0].script_name)

        self.palette_service.show_palette(self.build_ele.script_name)

Die selbe Logik kannst du überall in dein Skript implementieren. Bitte immer nach dem verwenden der ControlPropertiesUtil die palette aktualisieren (update_palette() vom BuildingElementPaletteService ausführen). Schau mal in die API reference, was der Util noch kann.

Für deine zweite Frage:
Definiere einen inaktiven AttributeIdValue Parameter als eine leere liste in deiner Palette, so:

        <Parameter>
            <Name>DynamicAttributeList</Name>
            <Text>Attributes</Text>
            <Value>[_]</Value>
            <ValueType>AttributeIdValue</ValueType>
            <Enable>False</Enable>
        </Parameter>

Und in deinem .py Datei die attr_list als eine Python-Liste, die sich aber aus AttributeIdValue-Objekten zusammensetzt:
        self.attr_list: list[AttributeIdValue] = []
        self.attr_list.append(AttributeIdValue(1905,"C20/25"))

weil diese kannst du deinem DynamicAttributeList-Parameter als value zuweisen:
        self.build_ele.DynamicAttributeList.value = self.attr_list

Wenn du danach die Palette initialisierst bzw. aktualisierst, solltest du dort deine Attribute sehen.
Wichtig: die Liste kannst du in der Form nicht direkt an die ChangeAttributes-Methode übergeben. Du musst sie in eine Liste von Tuples umbauen, so:

        AllplanBaseElements.ElementsAttributeService.ChangeAttributes([(attr.attribute_id, attr.value) for attr in self.attr_list], sel_elements)

Und noch Vollständigkeitshalber: die Klasse AttributeIdValue musst du natürlich ganz am Anfang importieren:

from AttributeIdValue import AttributeIdValue

Ich hoffe, damit kommst du weiter.

Grüße, Bart

Hallo Bart,

ich konnte mit deinen Tipps mal wieder alles Gewünschte und sogar noch ein paar Kleinigkeiten mehr erreichen, vielen Dank dafür!

Hallo Nyglas, hallo Bart,

ist das Vorgehen so auch in script-objects übertragbar? Ich habe hier leider einige Probleme dynamisch die Palette zur Laufzeit anzupassen. Bei mir geht es ebenfalls darum eine Combobox dynamisch zu füllen basierend auf Werten welche zur Laufzeit abgerufen werden.
Oder muss ich hier etwas anderes beachten?

Danke und Grüße,
Bastian

Hi Bastian,

Im Script Object war es dies bisher nicht möglich. Wir haben es nachgebessert, nur die Doku ist noch auf dem alten Stand.

In deinem .py Datei definierst du die Funktion create_script_object. Diese muss 2 Argumente haben, wobei der zweite ist der BaseScriptObjectData, eine Dataclass mit mehreren nutzvollen Objekten.

def create_script_object(build_ele:   BuildingElement,
                         script_object_data: BaseScriptObjectData) -> BaseScriptObject:

    return MyScriptObject(script_object_data, build_ele)

U.a. ist dort der ControlPropertiesUtil. Am besten hole den in dem Constructor uns speichere als Class-Attribut:

class MyScriptObject(BaseScriptObject):
   
    def __init__(self,
                 script_object_data: BaseScriptObjectData,
                 build_ele: BuildingElement):

        super().__init__(script_object_data)

        self.build_ele = build_ele
        self.control_props_util = script_object_data.control_props_util

Dann kannst du den innerhalb deiner Klasse genauso nutzen, wie oben beschrieben. Ich habe dir einen Beispiel angehängt.

Grüße,
Bart

Pièces-jointes (1)

Type: application/zip
Téléchargé 93 fois
Size: 2,25 KiB

perfekt! Vielen Dank.

Grüße,
Bastian