Property Provider Example

Goal

This example has two parts: the Python entry and the Property Provider entry.

  • Python: This part of the script retrieves all Python Code object properties and writes them to the ds.dat file as comments.

  • Property Provider: The code contained in the Property Provider tab defines property definitions. The example below creates properties that are similar to what you would see in a Force object.

Python Code

"""
To insert commands just prior to the Ansys SOLVE command, use solver_input_file.WriteLine("!Your command")
Global Helpers:
    this -- the datamodel object instance of the python code object you are currently editing in the tree
    solver_input_file  -- file stream that allows you to inject commands into the solver input file
    solver_data -- data stucture that allows you to access information from the model such as current step, contact pair ids, etc.
"""

# To access properties created using the Property Provider, please use the following command.
# this.GetCustomPropertyByPath("your_property_group_name/your_property_name")

# To access scoping properties use the following to access geometry scoping and named selection respectively:
# this.GetCustomPropertyByPath("your_property_group_name/your_property_name/Geometry Selection")
# this.GetCustomPropertyByPath("your_property_group_name/your_property_name/Named Selection")

properties = this.PropertyProvider.GetProperties()
for prop in properties:
    line = "!" + prop.Name + " " + prop.ValueString
    solver_input_file.WriteLine(line)

Property Provider Code

def reload_props(): 

    this.PropertyProvider = None
    # comment the return below to allow the rest of the function definition to be executed and add properties
    return

    #Create a new PropertyProvider Instance called Force
    Force = Provider()
    
    #Add a new group to called Definition2 to Force
    Definition = Force.AddGroup("Definition2")
    
    #Add an Expression property "Type" to the Definition2 group and set its value = "Force"
    type = Definition.AddProperty("Type", Control.Expression   )
    type.Value = "Force"
    
    #Create an instance of the custom ForceProperty that was created above and add it to the Definition2 group
    force_prop = Definition.AddProperty("Force Property", "Force")   
    
    #Add an Options property "Applied By" Options property
    applied_by = Definition.AddProperty("Applied By", Control.Options)
    applied_by.Options = {0:"Surface Effect", 1:"Direct"}
    applied_by.Value = 0
    
    #Add Options property "Suppressed" Options property
    suppressed = Definition.AddProperty("Suppressed", Control.Options)
    suppressed.Options = {0:"Yes", 1:"No"}
    suppressed.Value = 1
    
    #Add a new group called "Scope" to Force 
    Scope = Force.AddGroup("Scope")
    
    #Add a Scoping Property to the Scope group
    scoping_prop = Scope.AddProperty("Scoping Property", "Scoping", "property_templates")   
 
    #Connects the provider instance back to the object by setting the PropertyProvider member on this, 'this' being the
    #current instance of the Python Code object. 
    this.PropertyProvider = Force

 

#region Property Provider Template 
from Ansys.ACT.Mechanical.AdditionalProperties import PropertyProviderAdapter 
from Ansys.ACT.Mechanical.AdditionalProperties import * 

 

""" 
The property_templates module is located in {YOUR_INSTALL_DIR}/DesignSpace/DSPages/Python/mech_templates 
""" 

from mech_templates import property_templates 
property_templates.set_ext_api(ExtAPI) 

 

class Provider(Ansys.ACT.Interfaces.Mechanical.IPropertyProvider): 
    """
    Provider template that implements IPropertyProvider to demonstrate the usage of IPropertyProvider. 
    It provides helper methods and classes that manage properties that can be dynamicall added to an object. 
    """ 
    # region These are callbacks that as a user you may want to modify to get specific behavior
    def IsValid(self, prop):
        """
        Called when checking the validity of a property, with the property instance.
        """

        # for double property use the ValidRange property to check validity
        if(isinstance(prop, DoubleProperty)):
            return prop.ValidRange[0] <= prop.Value and prop.ValidRange[1] >= prop.Value
        
        return True
    
    def IsReadOnly(self, prop):
        """
        Called when checking if a property should be readonly, with the property instance.
        """

        return False
    
    def IsVisible(self, prop):
        """
        Called when checking if a property should be visible, with the property instance.
        """

        return True
    
    def SetValue(self, prop, val):
        """
        Allows you to override the setter of the Value property on the property instance. 
        Keyword Arguments:
            prop -- property of which the value is being set
            val -- the value that was set
        Returns:
            The value that the Value property should be set to
        """
        return val

    def GetValue(self, prop, val):
        """
        Allows you to override the getter of the Value property on the property instance. 
        Keyword Arguments:
            prop -- property of which the value is being set
            val -- current value of the Value property
        Returns:
            The value that the getter on the internal value should return
        """
        return val
    # endregion

    #structures that hold property instances 
    prop_list = [] 
    prop_map = {} 
    prop_groups = set() 

    class AnsGroup(): 
        """Helper group class to group properties, and provides methods to add properties to groups.""" 
        provider = None 
        
        def __init__(self,name=None, provider=None):
            self.name = name 
            self.provider = provider 

        def __AddScopingProperty(self, name): 
            """
            Adds a scoping property with a given name to this group. 

            Keyword Arguments :  
                name -- unique name for the scoping property
            """ 

            scoping_prop = property_templates.ScopingProperty(name, self.name) 

            for prop in scoping_prop.GetGroupedProps(): 
                self.provider.AddProperty(prop) 
            return scoping_prop.GetGroupedProps() 

         
        def __AddForceProperty(self, name):
            """
            Adds a Force property with a given name to this group.
            
            Keyword Arguments : 
                name -- unique name for the force property
            """
            force_prop = ForceProperty(name, self.name)
            
            for prop in force_prop.GetGroupedProps():
                self.provider.AddProperty(prop)
            return force_prop.GetGroupedProps()


        def AddProperty(self, name=None, prop_control=None, module_name=None): 
            """
            Creates an instance of the property and connects delgates in  
            the associated Property Propvider. 

            Keyword Arguments :  
                name -- unique name for the scoping property 
                prop_control -- one of the built in controls, or extended controls 
                module_name -- module where the control is defined 
            """ 

            #special case for scoping property 
            if(prop_control == "Scoping" and module_name == "property_templates"): 
                return self.__AddScopingProperty(name) 

            #special case for Force property
            if(prop_control == "Force"):
                return self.__AddForceProperty(name)
                
            #if no module_name is passed, use the globals in current module
            #that has the built in controls imported 

            prop_mod_globals = None 

            if(module_name != None): 
                if(module_name not in globals()): 
                    raise Exception("Unknown module : " + module_name) 

                prop_mod_globals = globals()[module_name].get_globals() 

            else: 
                prop_mod_globals = globals() 

             

            #class name is built based on control + "Property"
            #    Double - > DoubleProperty 
            prop_class_name = str(prop_control) + "Property" 

            if(prop_class_name not in prop_mod_globals): 
                raise Exception("Unknown property class : " + prop_class_name) 

            #instantiate the property based on module and class name 
            prop = prop_mod_globals[prop_class_name](self.name + "/" + name, self.name) 

            if(prop == None): 
                raise Exception("Issue while creating the property instance.") 

            #set the delegates to property provider functions 
            prop.IsValidCallback = self.provider.IsValid 
            prop.IsReadOnlyCallback = self.provider.IsReadOnly 
            prop.IsVisibleCallback = self.provider.IsVisible 
            prop.GetValueCallback = self.provider.GetValue 
            prop.SetValueCallback = self.provider.SetValue 

            #as a default make the property name the property display name 
            prop.DisplayName = name 

            #add property to the provider 
            self.provider.AddProperty(prop) 

            return prop 

    def __init__(self): 
        pass 
     
    def GetProperties(self): 
        """
        Returns a list of properties in the order that they were added to the property provider.  
        """ 
        return [self.prop_map[propName] for propName in self.prop_list] 


    def AddGroup(self, name=None): 
        """
        Creates an instance of helper group class and returns it. 
        """ 

        if name in self.prop_groups: 
            raise Exception("Group with name " + name + " already exists, please use a unique group name.") 

        #keep groups names so we can make sure no duplicate groups are added 
        self.prop_groups.add(name) 

        return self.AnsGroup(name, self) 

     

    def AddProperty(self, prop): 
        """
        Method used by the helper group class to add the property to the data-structure holding 
        the property instances. 
        """ 

        if(prop.Name in self.prop_map): 
            raise Exception("Property name must be unique, property with name '" + prop.Name + "' already exisits.") 

        self.prop_list.append(prop.Name) 
        self.prop_map[prop.Name] = prop 

#end region 

"""
Create a Force like object using PropertyProvider
"""
class ForceProperty:
    """
     Class to create a custom Force property using a combination of different control types
    """
    grouped_props = []
    def __init__(self, name, groupName="Forcing"):
        
        #Create a DefineBy property using the OptionsProperty 
        self.def_by_prop = OptionsProperty(name + "/Define By", groupName)
        self.def_by_prop.Options = {0: "Vector", 1: "Components"}
        self.def_by_prop.Value = 0
        self.def_by_prop.DisplayName = ExtAPI.Application.GetLocalString("ID_ScopingDefineBy")
        
        #Create a Magnitude property using DoubleProperty with a valid range. 
        #Override the default IsVisible and IsValid callbacks with the ones defined int this class.
        self.magnitude = DoubleProperty(name + "/Magnitude", groupName)
        self.magnitude.Value = 0
        self.magnitude.ValidRange = (1.0, 4.0)
        self.magnitude.DisplayName = ExtAPI.Application.GetLocalString("ID_MagnitudeProperty")
        self.magnitude.IsVisibleCallback = self.IsVisible
        self.magnitude.IsValidCallback = self.IsValid
        
        #Create a Direction property using GeometetrySelectionProperty from property_templates. 
        #Override the default IsVisible callback with the one defined int this class."
        self.direction = property_templates.GeometrySelectionProperty(name + "/Direction", groupName)
        self.direction.DisplayName = ExtAPI.Application.GetLocalString("ID_Direction")
        self.direction.IsVisibleCallback = self.IsVisible
        
        
        #Create a Coordinate System property using OptionsProperty.
        #Add an Option and set the value to the first option.
        #Override the default IsVisible callback with the one defined int this class."
        self.coordinate_system = OptionsProperty(name + "/Coordinate System", groupName)
        self.coordinate_system.Options = {0: "Global Coordinate System"}
        self.coordinate_system.Value = 0
        self.coordinate_system.DisplayName = ExtAPI.Application.GetLocalString("ID_Coordinate_System")
        self.coordinate_system.IsVisibleCallback = self.IsVisible
        
        #Create an X Component property using DoubleProperty with a valid range. 
        #Override the default IsVisible and IsValid callbacks with the ones defined int this class.
        #Allow this property to be parameterized by setting CanParameterize to True.
        self.x_comp = DoubleProperty(name + "/X Component", groupName)
        self.x_comp.Value = 0
        self.x_comp.ValidRange = (1.0, 4.0)
        self.x_comp.DisplayName = ExtAPI.Application.GetLocalString("ID_DirectionalXComponent")
        self.x_comp.IsVisibleCallback = self.IsVisible
        self.x_comp.CanParameterize = True
        self.x_comp.IsValidCallback = self.IsValid
        
        
        #Create a Y Component property using DoubleProperty with a valid range. 
        #Override the default IsVisible and IsValid callbacks with the ones defined int this class.
        #Allow this property to be parameterized by setting CanParameterize to True.
        self.y_comp = DoubleProperty(name + "/Y Component", groupName)
        self.y_comp.Value = 0
        self.y_comp.ValidRange = (1.0, 4.0)
        self.y_comp.DisplayName = ExtAPI.Application.GetLocalString("ID_DirectionalYComponent")
        self.y_comp.IsVisibleCallback = self.IsVisible
        self.y_comp.CanParameterize = True
        self.y_comp.IsValidCallback = self.IsValid
        
        #Create a Z Component property using DoubleProperty with a valid range. 
        #Override the default IsVisible and IsValid callbacks with the ones defined int this class.
        #Allow this property to be parameterized by setting CanParameterize to True.
        self.z_comp = DoubleProperty(name + "/Z Component", groupName)
        self.z_comp.ValidRange = (1.0, 4.0)
        self.z_comp.Value = 0
        self.z_comp.DisplayName = ExtAPI.Application.GetLocalString("ID_DirectionalZComponent")
        self.z_comp.IsVisibleCallback = self.IsVisible
        self.z_comp.CanParameterize = True
        self.z_comp.IsValidCallback = self.IsValid
        
        #Add all properties to the grouped_props list
        self.grouped_props = [self.def_by_prop, self.magnitude,self.direction, self.coordinate_system, self.x_comp, self.y_comp, self.z_comp]
        
    def GetGroupedProps(self):
        """
        Returns the properties list
        """
        return self.grouped_props
    
    def IsVisible(self, prop):
        """
        Overrides the IsVisible callback for properties. 
        Decides which properties will be visible in the details pane based on the option selected in the DefineBy property
        """
        if(prop.DisplayName == "Magnitude"):
            return int(self.def_by_prop.Value) == 0
        if(prop.DisplayName == "Direction"):
            return int(self.def_by_prop.Value) == 0
        if(prop.DisplayName == "Coordinate System"):
            return int(self.def_by_prop.Value) == 1
        if(prop.DisplayName == "X Component"):
            return int(self.def_by_prop.Value) == 1
        if(prop.DisplayName == "Y Component"):
            return int(self.def_by_prop.Value) == 1
        if(prop.DisplayName == "Z Component"):
            return int(self.def_by_prop.Value) == 1
            
    def IsValid(self, prop):
        """Overrides the isValid callback for properties.
        """
        #for double property use the ValidRange property to check validity
        if(isinstance(prop, DoubleProperty)):
            return prop.ValidRange[0] <= prop.Value and prop.ValidRange[1] >= prop.Value
        
        return True
        
            

""" 
Reload the properties at the end to make sure the class definition is executed before instantiation 
""" 
reload_props()