Refresh Components in Gh-Canvas

Hello everyone,

I’ve been iterating quickly and building new components for a larger plugin. During this process, I often change parameter names or even the names of the components themselves.

Now that the plugin has become more mature, I’d like to refresh the instances in my Grasshopper canvas to reflect the updated plugin names and parameters. Currently, I delete the old components, add the updated ones back to the canvas, and reconnect all the wires manually.

This process feels unnecessarily repetitive. Is there an automated way to achieve this?

(I know about “OLD” but for the first stable version I would like to brute force it)

Thanks and Best Felix

I will write the example in Python, but should be fairly straightforward to translate to C#.

For updating Component names you just need to find the components proxy (think of it as “template”) in the Component server and get the current name from there.

import Grasshopper

for object in Grasshopper.Instances.ActiveCanvas.Document.Objects:
    proxy = Grasshopper.Instances.ComponentServer.EmitObjectProxy(object.ComponentGuid)
    object.Name = proxy.Desc.Name

Updating parameter requires one more step. You need to intialize new component instance from the Proxy object for the RegisterInputParams() and RegisterOutputParams() method to be called. Then you can access the .Params as they should be in the latest release.

for object in Grasshopper.Instances.ActiveCanvas.Document.Objects:
    proxy = Grasshopper.Instances.ComponentServer.EmitObjectProxy(object.ComponentGuid)
    # Initialize new instance of the component
    new_object = proxy.Type() 
    # Only GH_Component objects have Params
    if isinstance(new_object, Grasshopper.Kernel.GH_Component):
        # iterate trough Params - their number and order has to match obviously
        for old, new in zip(object.Params, new_object.Params):
            old.Name = new.Name
2 Likes

Hey Ondrej

Thanks for sharing the code! I used your idea to create a little component out of this

Here would be the code if anyone ever needs it:

protected override void RegisterInputParams(GH_InputParamManager pManager)
        {
            pManager.AddBooleanParameter("Run", "R", "Execute the reset on currently selected components", GH_ParamAccess.item, false);
            pManager.AddBooleanParameter("Clear Connections", "C", "If true, remove all sources and recipients as well", GH_ParamAccess.item, false);
        }

        protected override void RegisterOutputParams(GH_OutputParamManager pManager)
        {
            pManager.AddTextParameter("Reset Items", "RI", "GUIDs of the reset components", GH_ParamAccess.list);
        }

        protected override void SolveInstance(IGH_DataAccess DA)
        {
            bool run = false;
            bool clearConnections = false;
            if (!DA.GetData(0, ref run)) return;
            if (!DA.GetData(1, ref clearConnections)) return;
            if (!run) return;

            var doc = OnPingDocument();
            if (doc == null)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "Could not access Grasshopper document.");
                return;
            }

            var selectedObjects = doc.SelectedObjects();
            if (selectedObjects == null || selectedObjects.Count == 0)
            {
                AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "No components are currently selected.");
                return;
            }

            var resetGuids = new List<string>();

            foreach (var selectedObject in selectedObjects)
            {
                if (selectedObject is GH_Component targetComponent)
                {
                    if (targetComponent is GH_Cluster ||
                        targetComponent.GetType().Name == "CSharpComponent" ||
                        targetComponent.GetType().Name == "ZuiPythonComponent" ||
                        targetComponent.GetType().Name == "Python3Component" ||
                        targetComponent.ComponentGuid == this.ComponentGuid) 
                        continue;

                    var freshComponent = Activator.CreateInstance(targetComponent.GetType()) as GH_Component;
                    if (freshComponent == null)
                    {
                        AddRuntimeMessage(GH_RuntimeMessageLevel.Error, $"Failed to create a fresh instance of {targetComponent.Name}.");
                        continue;
                    }

                    targetComponent.Name = freshComponent.Name;
                    targetComponent.NickName = freshComponent.NickName;
                    targetComponent.Description = freshComponent.Description;
                    targetComponent.IconDisplayMode = freshComponent.IconDisplayMode;

                    for (int i = 0; i < Math.Min(targetComponent.Params.Input.Count, freshComponent.Params.Input.Count); i++)
                    {
                        ResetParameter(targetComponent.Params.Input[i], freshComponent.Params.Input[i], clearConnections);
                    }

                    for (int i = 0; i < Math.Min(targetComponent.Params.Output.Count, freshComponent.Params.Output.Count); i++)
                    {
                        ResetParameter(targetComponent.Params.Output[i], freshComponent.Params.Output[i], clearConnections);
                    }

                    resetGuids.Add(targetComponent.InstanceGuid.ToString());
                }
            }

            DA.SetDataList(0, resetGuids);
        }

        private void ResetParameter(IGH_Param existingParam, IGH_Param templateParam, bool clearConnections)
        {
            existingParam.Name = templateParam.Name;
            existingParam.NickName = templateParam.NickName;
            existingParam.Description = templateParam.Description;
            existingParam.Access = templateParam.Access;
            
            if (clearConnections)
            {
                var sources = new List<IGH_Param>(existingParam.Sources);
                foreach (var s in sources)
                {
                    existingParam.RemoveSource(s);
                }

                var recipients = new List<IGH_Param>(existingParam.Recipients);
                foreach (var r in recipients)
                {
                    r.RemoveSource(existingParam);
                }
            }
        }
3 Likes