Script input params in Python Component

Hi everyone,

@eirannejad I see you’re the one focused on Python functionality. Would it be possible to implement an easy way to code the component’s metadata and input and output parameters? (Added bonus: potentially script a simple way to pass Value Listener values)

With the proliferation of LLMs as code assistants, I believe this can be really valuable for users to copy and paste code, they would just need to prompt the LLM to follow a specific format (whichever you feel is appropriate or required to set up the components data).

Let me show you what I do at the moment. It works, but it’s clunky, and scripting Value Listeners means getting into more complex stuff like object expiration and persistence.

class TextJustification:
    """Manages text justification options and validation.
    
    This class encapsulates text justification functionality, providing a clean
    interface for working with Rhino's text justification system. It converts
    between human-readable position descriptions (e.g., "BottomLeft") and their
    corresponding numeric values in the Rhino system.
    
    The justification system uses a coordinate-based approach where:
    - Vertical alignment uses the 1st and 2nd bytes (values 65536, 131072, 262144)
    - Horizontal alignment uses the 1st and 2nd bits (values 1, 2, 4)
    
    For example, BottomLeft (65537) combines:
    - Bottom alignment (65536) with
    - Left alignment (1)
    """
    
    OPTIONS = {
        "BottomLeft": 65537,
        "BottomCenter": 65538,
        "BottomRight": 65540,
        "MiddleLeft": 131073,
        "MiddleCenter": 131074,
        "MiddleRight": 131076,
        "TopLeft": 262145,
        "TopCenter": 262146,
        "TopRight": 262148
    }
    
    @classmethod
    def get_default(cls):
        """Returns the default justification value (MiddleCenter)."""
        return cls.OPTIONS["MiddleCenter"]
    
    @classmethod
    def is_valid(cls, value):
        """Checks if a value is a valid justification option."""
        return value in cls.OPTIONS.values()

def setup_component():
    """Initialize and configure the Grasshopper component.
    
    This function handles the complete setup of the component, including:
    1. Setting basic component properties (name, category, etc.)
    2. Configuring input parameters with appropriate data access patterns
    3. Setting up dynamic behavior for parameter updates
    4. Establishing default values and optional parameters
    
    The setup ensures the component will behave consistently within the
    Grasshopper environment and provides clear feedback through parameter
    naming and descriptions.
    """
    
    # Component setup
    ghenv.Component.Name = "Text to Geometry"
    ghenv.Component.NickName = "T2G"
    ghenv.Component.Message = "Text2Geometry"
    ghenv.Component.Category = "Text"
    ghenv.Component.SubCategory = "Format"
    
    # Make the component volatile so it updates when inputs change
    ghenv.Component.Params.Input[4].DataMapping = Grasshopper.Kernel.GH_DataMapping.NONE
    ghenv.Component.Params.Input[4].Optional = True
    
    # Set component to recompute when anything changes
    ghenv.Component.Attributes.Selected = True
    
    # Input parameter setup
    inputs = ghenv.Component.Params.Input
    
    params_config = [
        ("Text", "T", "Text string to convert to curves", Grasshopper.Kernel.GH_ParamAccess.item),
        ("Font", "F", "Font name (defaults to Arial if not specified)", Grasshopper.Kernel.GH_ParamAccess.item),
        ("Size", "S", "Text height (defaults to 10 units if not specified)", Grasshopper.Kernel.GH_ParamAccess.item),
        ("Location", "L", "Location as either a Plane or Point3d", Grasshopper.Kernel.GH_ParamAccess.item),
        ("Text Justification", "J", "Text justification value (optional)", Grasshopper.Kernel.GH_ParamAccess.item)
    ]
    
    for i, (name, nick, desc, access) in enumerate(params_config):
        inputs[i].Name = name
        inputs[i].NickName = nick
        inputs[i].Description = desc
        inputs[i].Access = access

def configure_connected_value_list():
    """Set up a connected Value List with text justification options.
    
    This function provides a user-friendly interface for text justification by:
    1. Detecting when a Value List component is connected to the justification input
    2. Automatically populating it with available justification options
    3. Maintaining the component's default behavior for the current execution
    
    The configuration happens "just in time" - when a Value List is connected,
    it's immediately configured but doesn't force a solution update, allowing
    for smooth integration into existing Grasshopper definitions.
    """
    
    # Get our justification input parameter
    j_param = ghenv.Component.Params.Input[4]
    
    # Look for connected components
    sources = j_param.Sources
    
    if sources and sources.Count > 0:
        source = sources[0]
        if source:
            # Get the actual component
            upstream = source.Attributes.GetTopLevel.DocObject
            
            # Check if it's a Value List
            if upstream.GetType().Name == "GH_ValueList":
                try:
                    # Configure the Value List with justification options
                    upstream.ListItems.Clear()
                    
                    # Add all justification options
                    for display_name, value in TextJustification.OPTIONS.items():
                        item = Grasshopper.Kernel.Special.GH_ValueListItem(display_name, str(value))
                        upstream.ListItems.Add(item)
                    
                    # Set a descriptive name for the Value List
                    upstream.NickName = "TextJustification"
                    
                    # Set component's description to indicate it's been configured
                    upstream.Description = "Configured with text justification options"
                    
                    # Don't force a solution update - let Grasshopper handle it naturally
                    ghenv.Component.OnPingDocument().ScheduleSolution(10)
                    
                except Exception as e:
                    ghenv.Component.AddRuntimeMessage(
                        Grasshopper.Kernel.GH_RuntimeMessageLevel.Warning,
                        f"Error configuring Value List: {str(e)}"
                    )
class TextJustification:
    """Manages text justification options and validation.
    
    This class encapsulates text justification functionality, providing a clean
    interface for working with Rhino's text justification system. It converts
    between human-readable position descriptions (e.g., "BottomLeft") and their
    corresponding numeric values in the Rhino system.
    
    The justification system uses a coordinate-based approach where:
    - Vertical alignment uses the 1st and 2nd bytes (values 65536, 131072, 262144)
    - Horizontal alignment uses the 1st and 2nd bits (values 1, 2, 4)
    
    For example, BottomLeft (65537) combines:
    - Bottom alignment (65536) with
    - Left alignment (1)
    """
    
    OPTIONS = {
        "BottomLeft": 65537,
        "BottomCenter": 65538,
        "BottomRight": 65540,
        "MiddleLeft": 131073,
        "MiddleCenter": 131074,
        "MiddleRight": 131076,
        "TopLeft": 262145,
        "TopCenter": 262146,
        "TopRight": 262148
    }
    
    @classmethod
    def get_default(cls):
        """Returns the default justification value (MiddleCenter)."""
        return cls.OPTIONS["MiddleCenter"]
    
    @classmethod
    def is_valid(cls, value):
        """Checks if a value is a valid justification option."""
        return value in cls.OPTIONS.values()

def setup_component():
    """Initialize and configure the Grasshopper component.
    
    This function handles the complete setup of the component, including:
    1. Setting basic component properties (name, category, etc.)
    2. Configuring input parameters with appropriate data access patterns
    3. Setting up dynamic behavior for parameter updates
    4. Establishing default values and optional parameters
    
    The setup ensures the component will behave consistently within the
    Grasshopper environment and provides clear feedback through parameter
    naming and descriptions.
    """
    
    # Component setup
    ghenv.Component.Name = "Text to Geometry"
    ghenv.Component.NickName = "T2G"
    ghenv.Component.Message = "Text2Geometry"
    ghenv.Component.Category = "Text"
    ghenv.Component.SubCategory = "Format"
    
    # Make the component volatile so it updates when inputs change
    ghenv.Component.Params.Input[4].DataMapping = Grasshopper.Kernel.GH_DataMapping.NONE
    ghenv.Component.Params.Input[4].Optional = True
    
    # Set component to recompute when anything changes
    ghenv.Component.Attributes.Selected = True
    
    # Input parameter setup
    inputs = ghenv.Component.Params.Input
    
    params_config = [
        ("Text", "T", "Text string to convert to curves", Grasshopper.Kernel.GH_ParamAccess.item),
        ("Font", "F", "Font name (defaults to Arial if not specified)", Grasshopper.Kernel.GH_ParamAccess.item),
        ("Size", "S", "Text height (defaults to 10 units if not specified)", Grasshopper.Kernel.GH_ParamAccess.item),
        ("Location", "L", "Location as either a Plane or Point3d", Grasshopper.Kernel.GH_ParamAccess.item),
        ("Text Justification", "J", "Text justification value (optional)", Grasshopper.Kernel.GH_ParamAccess.item)
    ]
    
    for i, (name, nick, desc, access) in enumerate(params_config):
        inputs[i].Name = name
        inputs[i].NickName = nick
        inputs[i].Description = desc
        inputs[i].Access = access

def configure_connected_value_list():
    """Set up a connected Value List with text justification options.
    
    This function provides a user-friendly interface for text justification by:
    1. Detecting when a Value List component is connected to the justification input
    2. Automatically populating it with available justification options
    3. Maintaining the component's default behavior for the current execution
    
    The configuration happens "just in time" - when a Value List is connected,
    it's immediately configured but doesn't force a solution update, allowing
    for smooth integration into existing Grasshopper definitions.
    """
    
    # Get our justification input parameter
    j_param = ghenv.Component.Params.Input[4]
    
    # Look for connected components
    sources = j_param.Sources
    
    if sources and sources.Count > 0:
        source = sources[0]
        if source:
            # Get the actual component
            upstream = source.Attributes.GetTopLevel.DocObject
            
            # Check if it's a Value List
            if upstream.GetType().Name == "GH_ValueList":
                try:
                    # Configure the Value List with justification options
                    upstream.ListItems.Clear()
                    
                    # Add all justification options
                    for display_name, value in TextJustification.OPTIONS.items():
                        item = Grasshopper.Kernel.Special.GH_ValueListItem(display_name, str(value))
                        upstream.ListItems.Add(item)
                    
                    # Set a descriptive name for the Value List
                    upstream.NickName = "TextJustification"
                    
                    # Set component's description to indicate it's been configured
                    upstream.Description = "Configured with text justification options"
                    
                    # Don't force a solution update - let Grasshopper handle it naturally
                    ghenv.Component.OnPingDocument().ScheduleSolution(10)
                    
                except Exception as e:
                    ghenv.Component.AddRuntimeMessage(
                        Grasshopper.Kernel.GH_RuntimeMessageLevel.Warning,
                        f"Error configuring Value List: {str(e)}"
                    )

# ... rest of methods

Cheers

What you are using is what has been also available in the legacy GHPython component. There has been some discussions around this topic.

I would appreciate if you can list your expectations from this here. How do you want to code the component parameters? I also don’t know what a Value Listener is.

Thanks. Yeah, I meant something like what is being discussed in Programmatically creating new C# & Python script components. The only additional ask would be, to have a way to script key and values that can be automatically passed to a Value List component (I’m sorry I originally wrote Value Listener by mistake) when connecting to an input. This is very useful from a UX perspective, I’ve done it before, but it’s really complex to deal with persistence and/or object expiration. I’ve managed to make it work, but very glitchy. When I connect a Value List, it usually renders an empty field instead of displaying the current option’s name. Then I need to manually move the Value List component for it to render the correct value. Does this make sense? Let me know if there’s any specific context I should provide.

Yeah it would be better to provide a simple specific example. AFAIK the Value List component does not take any inputs :thinking:

Sure, I’m adding this GIF, maybe this is more helpful.

UnitConvValueList

The idea is that, you connect a Value List to a scripted component and the expected ENUM values are passed back into the Value List component. That functionality is scripted in the Python component. In this GIF, it works perfectly, but it usually does not haha

Are you Expiring the display on the Value List component after making changes to its data?

I’m not sure, probably not. Is there a resource you could point me at?

This topic might help:

2 Likes

Thank you for your help @AndersDeleuran @eirannejad

2 Likes