Create input components from within ghpython component

Hello!

I would like my custom ghpython component to create its own input components (e.g. a toggle switch) but am struggling with hooking into the correct events in the component’s life-cycle. Ideally, the component would create the input component when it is being added to the document (and delete it when it’s being removed).

When creating a C# component which inherits from GH_Component overriding AddedToDocument seems to be the way to go, but I was unable to find the equivalent method of ExecutingComponent available in python.

Below is an example component, and I’m looking for the right place in which to call add_toggle_input.

from ghpythonlib.componentbase import executingcomponent

class MyComponent(executingcomponent):

    def add_toggle_input(self):
        toggle = Grasshopper.Kernel.Special.GH_BooleanToggle()
        toggle.CreateAttributes()
        toggle.Attributes.Pivot = System.Drawing.PointF(
            self.Attributes.InputGrip.X - toggle.Attributes.Bounds.Width - 20,
            self.Attributes.InputGrip.Y - toggle.Attributes.Bounds.Height / 2
            )

        Grasshopper.Instances.ActiveCanvas.Document.AddObject(toggle, False)
        self.Params.Input[0].AddSource(toggle)

Thanks!
Chen

Hi Chen. That looks like a great function. By the way, the API docs have a lot of warnings to only mess around with self.Params with the methods provided when adding Params. Presumably .AddSource is fine?

Anyway, inside a GhPython component in SDK mode (class MyComponent…), you have code in
a) the body of the module (the same as where the code goes in normal script mode)
b) the body of the MyComponent class
c) optionally in MyComponent’s __init__ method (probably best to call super(MyComponent, self).__init__() from there but I’ve not noticed it do anything)
d) in RunScript.

a), b) and c) are only run once, either when the grasshopper document is first opened, when the component is placed, or when you change the code inside the component and click Test, resetting the component. d) is also then called once afterwards for the first time.

However whenever the component gets updated, when something in its input changes, or when manually recalculated, d) runs again.

So if you don’t want a new Boolean component on the canvas for each time the inputs change, you should try calling add_toggle_input in c), i.e. add in def __init__(self):.

Maybe I forgot to call super.__init__, and I can’t remember if Params was one of them (was ghenv one), but some attributes aren’t finalised for use in __init__, and maybe need to be called as a one off, only the first time RunScript is called.

In a GhPython component only one instance of the MyComponent is ever created, so in Grasshopper, it’s much of a muchness whether you pick b) or c). b) runs first but c) gives you access to self. The distinction is important if doing anything else with class instances in Python.

Hi James!

Thanks a lot for the detailed reply.
So the problem with a) and b) is, as you mentioned, that in that scope I’m missing a reference to the instance of my component’s class which I need in order to access Params where I can add the input.

Putting it in __init__() seems right, but it looks like Params is being initialized somewhere after the call to __init__() because when trying to access it there I’m getting a null-reference error.

RunScript would work, but then I’d have to maintain some global state tracking the input creation, otherwise it’ll create an input component every time the component runs. This I’d like to avoid if possible.

I tried __enter__ and AfterSolveInstance as well but these seem to also get executed more often than you’d expect.

So I’m back to square one, wondering if there’s some way of hooking into the AddedToDocument.

Thanks!
Chen

I checked the code I wrote - Params might be accessible pre __init__ as ghenv.Component.Params

I wanted self-updating components anyway (they weren’t creating other components), so just included startup in the update condition, and just did the check every call to RunScript.

I don’t think there’s anything wrong with using the state of the component by the way - you’re already working on a class definition, and it’s a simple way of making sure something is idempotent.